From 97c894dcab96a90e7e2c14835a3622de6b1f1ae8 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Aug 2022 11:53:06 +0530 Subject: [PATCH] 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, } }