From e585c9d3827481d3bc417091eeb50ee8f2162e9b Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 1 Aug 2022 12:39:38 +0530 Subject: [PATCH 01/23] feat(gui-v2): delete column and set primary column Signed-off-by: Pranav C --- .../components/smartsheet-header/Cell.vue | 66 +------------------ .../components/smartsheet-header/Menu.vue | 47 ++++++++++++- 2 files changed, 48 insertions(+), 65 deletions(-) diff --git a/packages/nc-gui-v2/components/smartsheet-header/Cell.vue b/packages/nc-gui-v2/components/smartsheet-header/Cell.vue index 4171e6b974..8d7e69cfe0 100644 --- a/packages/nc-gui-v2/components/smartsheet-header/Cell.vue +++ b/packages/nc-gui-v2/components/smartsheet-header/Cell.vue @@ -1,4 +1,5 @@ From 8b41cbe09d5fde8c009b112901098f24d2919651 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Aug 2022 02:21:04 +0530 Subject: [PATCH 05/23] feat(gui-v2): show error message on row insert/update Signed-off-by: Pranav C --- packages/nc-gui-v2/composables/useViewData.ts | 130 +++++++++++------- 1 file changed, 78 insertions(+), 52 deletions(-) diff --git a/packages/nc-gui-v2/composables/useViewData.ts b/packages/nc-gui-v2/composables/useViewData.ts index c1a843619e..a86fad19a0 100644 --- a/packages/nc-gui-v2/composables/useViewData.ts +++ b/packages/nc-gui-v2/composables/useViewData.ts @@ -3,6 +3,8 @@ import type { ComputedRef, Ref } from 'vue' import { useNuxtApp } from '#app' import { useProject } from '#imports' import { NOCO } from '~/lib' +import { notification } from 'ant-design-vue' +import { extractSdkResponseErrorMsg } from '~/utils' const formatData = (list: Record[]) => list.map((row) => ({ @@ -36,65 +38,79 @@ export function useViewData( } const updateRowProperty = async (row: Record, property: string) => { - const id = meta?.value?.columns - ?.filter((c) => c.pk) - .map((c) => row[c.title as string]) - .join('___') as string + try { + const id = meta?.value?.columns + ?.filter((c) => c.pk) + .map((c) => row[c.title as string]) + .join('___') as string - return $api.dbViewRow.update( - NOCO, - project?.value.id as string, - meta?.value.id as string, - viewMeta?.value?.id as string, - id, - { - [property]: row[property], - }, - // todo: - // { - // query: { ignoreWebhook: !saved } - // } - ) + return $api.dbViewRow.update( + NOCO, + project?.value.id as string, + meta?.value.id as string, + viewMeta?.value?.id as string, + id, + { + [property]: row[property], + }, + // todo: + // { + // query: { ignoreWebhook: !saved } + // } + ) - /* + /* - todo: audit + todo: audit - // audit - this.$api.utils - .auditRowUpdate(id, { - fk_model_id: this.meta.id, - column_name: column.title, - row_id: id, - value: getPlainText(rowObj[column.title]), - prev_value: getPlainText(oldRow[column.title]) - }) - .then(() => {}) - */ + // audit + this.$api.utils + .auditRowUpdate(id, { + fk_model_id: this.meta.id, + column_name: column.title, + row_id: id, + value: getPlainText(rowObj[column.title]), + prev_value: getPlainText(oldRow[column.title]) + }) + .then(() => {}) + */ + } catch (error) { + notification.error({ + message: 'Row update failed', + description: + await extractSdkResponseErrorMsg(error), + }) + } } const insertRow = async (row: Record, rowIndex = formattedData.value?.length) => { - // todo: implement insert row + try { + const insertObj = meta?.value?.columns?.reduce((o: any, col) => { + if (!col.ai && row?.[col.title as string] !== null) { + o[col.title as string] = row?.[col.title as string] + } + return o + }, {}) - const insertObj = meta?.value?.columns?.reduce((o: any, col) => { - if (!col.ai && row?.[col.title as string] !== null) { - o[col.title as string] = row?.[col.title as string] - } - return o - }, {}) + const insertedData = await $api.dbViewRow.create( + NOCO, + project?.value.id as string, + meta?.value.id as string, + viewMeta?.value?.id as string, + insertObj, + ) - const insertedData = await $api.dbViewRow.create( - NOCO, - project?.value.id as string, - meta?.value.id as string, - viewMeta?.value?.id as string, - insertObj, - ) - - formattedData.value?.splice(rowIndex ?? 0, 1, { - row: insertedData, - rowMeta: {}, - oldRow: { ...insertedData }, - }) + formattedData.value?.splice(rowIndex ?? 0, 1, { + row: insertedData, + rowMeta: {}, + oldRow: { ...insertedData }, + }) + } catch (error) { + notification.error({ + message: 'Row insert failed', + description: + await extractSdkResponseErrorMsg(error), + }) + } } const changePage = async (page: number) => { @@ -110,5 +126,15 @@ export function useViewData( }) } - return { data, loadData, paginationData, formattedData, insertRow, updateRowProperty, changePage, addRow, selectedRows } + return { + data, + loadData, + paginationData, + formattedData, + insertRow, + updateRowProperty, + changePage, + addRow, + selectedRows, + } } From 97c894dcab96a90e7e2c14835a3622de6b1f1ae8 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Aug 2022 11:53:06 +0530 Subject: [PATCH 06/23] feat(gui-v2): context menu Signed-off-by: Pranav C --- .../nc-gui-v2/components/smartsheet/Grid.vue | 198 ++++++++++-------- packages/nc-gui-v2/composables/useViewData.ts | 113 ++++++++-- 2 files changed, 206 insertions(+), 105 deletions(-) diff --git a/packages/nc-gui-v2/components/smartsheet/Grid.vue b/packages/nc-gui-v2/components/smartsheet/Grid.vue index 642307562b..b9c3994441 100644 --- a/packages/nc-gui-v2/components/smartsheet/Grid.vue +++ b/packages/nc-gui-v2/components/smartsheet/Grid.vue @@ -39,6 +39,8 @@ const editEnabled = ref(false) const { sqlUi } = useProject() const { xWhere } = useSmartsheetStoreOrThrow() const addColumnDropdown = ref(false) +const contextMenu = ref(false) +const contextMenuTarget = ref(false) const visibleColLength = computed(() => { const cols = fields.value @@ -51,8 +53,10 @@ const { formattedData: data, updateRowProperty, changePage, - addRow, + addEmptyRow, selectedRows, + deleteRow, + deleteSelectedRows, } = useViewData(meta, view as any, xWhere) const { loadGridViewColumns, updateWidth, resizingColWidth, resizingCol } = useGridViewColumnWidth(view) onMounted(loadGridViewColumns) @@ -105,103 +109,116 @@ defineExpose({ // watchEffect(() => { if (meta) useProvideColumnCreateStore(meta) // }) + +// reset context menu target on hide +watch(contextMenu, () => (contextMenuTarget.value = null)) @@ -222,6 +239,7 @@ if (meta) useProvideColumnCreateStore(meta) & > * { @apply flex align-center h-auto; } + overflow: hidden; } diff --git a/packages/nc-gui-v2/composables/useViewData.ts b/packages/nc-gui-v2/composables/useViewData.ts index a86fad19a0..5e8c0f66c9 100644 --- a/packages/nc-gui-v2/composables/useViewData.ts +++ b/packages/nc-gui-v2/composables/useViewData.ts @@ -1,9 +1,9 @@ import type { Api, PaginatedType, TableType, ViewType } from 'nocodb-sdk' import type { ComputedRef, Ref } from 'vue' +import { notification } from 'ant-design-vue' import { useNuxtApp } from '#app' import { useProject } from '#imports' import { NOCO } from '~/lib' -import { notification } from 'ant-design-vue' import { extractSdkResponseErrorMsg } from '~/utils' const formatData = (list: Record[]) => @@ -13,13 +13,18 @@ const formatData = (list: Record[]) => rowMeta: {}, })) +interface Row { + row: Record + oldRow: Record + rowMeta?: any +} + export function useViewData( meta: Ref | ComputedRef | undefined, viewMeta: Ref | ComputedRef | undefined, where?: ComputedRef, ) { - const data = ref[]>() - const formattedData = ref<{ row: Record; oldRow: Record; rowMeta?: any }[]>([]) + const formattedData = ref([]) const paginationData = ref({ page: 1, pageSize: 25 }) const selectedRows = reactive([]) @@ -32,7 +37,6 @@ export function useViewData( ...params, where: where?.value, }) - data.value = response.list formattedData.value = formatData(response.list) paginationData.value = response.pageInfo } @@ -74,11 +78,10 @@ export function useViewData( }) .then(() => {}) */ - } catch (error) { + } catch (error: any) { notification.error({ message: 'Row update failed', - description: - await extractSdkResponseErrorMsg(error), + description: await extractSdkResponseErrorMsg(error), }) } } @@ -104,11 +107,10 @@ export function useViewData( rowMeta: {}, oldRow: { ...insertedData }, }) - } catch (error) { + } catch (error: any) { notification.error({ message: 'Row insert failed', - description: - await extractSdkResponseErrorMsg(error), + description: await extractSdkResponseErrorMsg(error), }) } } @@ -118,23 +120,104 @@ export function useViewData( await loadData({ offset: (page - 1) * (paginationData.value.pageSize || 25), where: where?.value } as any) } - const addRow = () => { - formattedData.value.push({ + const addEmptyRow = (addAfter = formattedData.value.length) => { + formattedData.value[addAfter] = { row: {}, oldRow: {}, rowMeta: { new: true }, - }) + } + } + + const deleteRowById = async (id: string) => { + if (!id) { + throw new Error("Delete not allowed for table which doesn't have primary Key") + } + + const res: any = await $api.dbViewRow.delete( + 'noco', + project.value.id as string, + meta?.value.id as string, + viewMeta?.value.id as string, + id, + ) + + if (res.message) { + notification.info({ + message: 'Row delete failed', + description: h('div', { + innerHTML: `
Unable to delete row with ID ${id} because of the following: +

${res.message.join('
')}

+ Clear the data first & try again
`, + }), + }) + return false + } + + return true + } + + const deleteRow = async (rowIndex: number) => { + try { + const row = formattedData.value[rowIndex] + if (row.rowMeta.new) { + const id = meta?.value?.columns + ?.filter((c) => c.pk) + .map((c) => row.row[c.title as any]) + .join('___') + + const deleted = await deleteRowById(id as string) + if (!deleted) { + return + } + } + formattedData.value.splice(rowIndex, 1) + } catch (e: any) { + notification.error({ + message: 'Failed to delete row', + description: await extractSdkResponseErrorMsg(e), + }) + } + } + + const deleteSelectedRows = async () => { + let row = formattedData.value.length + while (row--) { + try { + const { row: rowObj, rowMeta } = formattedData.value[row] + if (!rowMeta.selected) { + continue + } + if (!rowMeta.new) { + const id = meta?.value?.columns + ?.filter((c) => c.pk) + .map((c) => rowObj[c.title as string]) + .join('___') + + const successfulDeletion = await deleteRowById(id as string) + if (!successfulDeletion) { + continue + } + } + formattedData.value.splice(row, 1) + } catch (e: any) { + return notification.error({ + message: 'Failed to delete row', + description: await extractSdkResponseErrorMsg(e), + }) + } + } } return { - data, loadData, paginationData, formattedData, insertRow, updateRowProperty, changePage, - addRow, + addEmptyRow, selectedRows, + deleteRow, + deleteSelectedRows, } } From 2bf39dbd884458a61a06d49a22e32c0d00a83db1 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Aug 2022 12:13:57 +0530 Subject: [PATCH 07/23] feat(gui-v2): row insert Signed-off-by: Pranav C --- .../nc-gui-v2/components/smartsheet/Grid.vue | 23 +++++++++++-------- packages/nc-gui-v2/composables/useViewData.ts | 7 ++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/nc-gui-v2/components/smartsheet/Grid.vue b/packages/nc-gui-v2/components/smartsheet/Grid.vue index b9c3994441..e4abfed91d 100644 --- a/packages/nc-gui-v2/components/smartsheet/Grid.vue +++ b/packages/nc-gui-v2/components/smartsheet/Grid.vue @@ -51,7 +51,7 @@ const { loadData, paginationData, formattedData: data, - updateRowProperty, + updateOrSaveRow, changePage, addEmptyRow, selectedRows, @@ -111,7 +111,12 @@ if (meta) useProvideColumnCreateStore(meta) // }) // reset context menu target on hide -watch(contextMenu, () => (contextMenuTarget.value = null)) +watch(contextMenu, () => { + if(!contextMenu.value) { + contextMenuTarget.value = false + } +}) +