From c131c0a258c183bfeac5bd6849f22314576410e5 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:49 +0000 Subject: [PATCH 01/14] feat: added bulk insert and bulk delete fun --- packages/nc-gui/composables/useData.ts | 230 +++++++++++++++++++------ 1 file changed, 173 insertions(+), 57 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index d51c33d726..130630664b 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -154,6 +154,53 @@ export function useData(args: { } } + async function bulkInsertRows( + rows: Row[], + ltarState: Record = {}, + { metaValue = meta.value, viewMetaValue = viewMeta.value }: { metaValue?: TableType; viewMetaValue?: ViewType } = {}, + undo = false, + ) { + const rowsToInsert = [] + try { + for (const currentRow of rows) { + const { missingRequiredColumns, insertObj } = await populateInsertObject({ + meta: metaValue!, + ltarState, + getMeta, + row: currentRow.row, + undo, + }) + if (missingRequiredColumns.size) continue + else rowsToInsert.push({ ...insertObj, ...(ltarState || {}) }) + } + + const bulkInsertedData = await $api.dbDataTableRow.create(metaValue?.id as string, rowsToInsert, { + viewId: viewMetaValue?.id as string, + }) + + // if (!undo) { + // addUndo({ + // redo: { + // fn: async function redo() {}, + // args: [], + // }, + // undo: { + // fn: async function undo(this: UndoRedoAction) {}, + // args: [], + // }, + // scope: defineViewScope({ view: viewMeta.value }), + // }) + // } + + await callbacks?.syncCount?.() + return bulkInsertedData + } catch (error: any) { + message.error(await extractSdkResponseErrorMsg(error)) + } finally { + await callbacks?.globalCallback?.() + } + } + // inside this method use metaValue and viewMetaValue to refer meta // since sometimes we need to pass old metas async function updateRowProperty( @@ -566,71 +613,108 @@ export function useData(args: { async function deleteSelectedRows() { let row = formattedData.value.length - const removedRowsData: { id?: string; row: Row; rowIndex: number }[] = [] + const removedRowsData: { Id: string; row: Row; rowIndex: number }[] = [] + console.log('formated rows', formattedData.value) + while (row--) { - try { - const { row: rowObj, rowMeta } = formattedData.value[row] as Record - 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 { row: rowObj, rowMeta } = formattedData.value[row] as Record + 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 - } - removedRowsData.push({ id, row: clone(formattedData.value[row]), rowIndex: row }) + if (id) { + removedRowsData.push({ Id: id, row: clone(formattedData.value[row]), rowIndex: row }) } - formattedData.value.splice(row, 1) - } catch (e: any) { - return message.error(`${t('msg.error.deleteRowFailed')}: ${await extractSdkResponseErrorMsg(e)}`) } } - addUndo({ - redo: { - fn: async function redo(this: UndoRedoAction, removedRowsData: { id?: string; row: Row; rowIndex: number }[]) { - for (const { id, row } of removedRowsData) { - await deleteRowById(id as string) - const pk: Record = rowPkData(row.row, meta?.value?.columns as ColumnType[]) - const rowIndex = findIndexByPk(pk, formattedData.value) - if (rowIndex !== -1) formattedData.value.splice(rowIndex, 1) - paginationData.value.totalRows = paginationData.value.totalRows! - 1 + try { + const removedRowIds: { Id: string }[] = await bulkDeleteRows( + removedRowsData.map((row) => { + return { + Id: row.Id, } - await callbacks?.syncPagination?.() - }, - args: [removedRowsData], - }, - undo: { - fn: async function undo( - this: UndoRedoAction, - removedRowsData: { id?: string; row: Row; rowIndex: number }[], - pg: { page: number; pageSize: number }, - ) { - for (const { row, rowIndex } of removedRowsData.slice().reverse()) { - const pkData = rowPkData(row.row, meta.value?.columns as ColumnType[]) - row.row = { ...pkData, ...row.row } - await insertRow(row, {}, {}, true) - recoverLTARRefs(row.row) - if (rowIndex !== -1 && pg.pageSize === paginationData.value.pageSize) { - if (pg.page === paginationData.value.page) { - formattedData.value.splice(rowIndex, 0, row) - } else { - await callbacks?.changePage?.(pg.page) - } - } else { - await callbacks?.loadData?.() - } + }), + ) + if (Array.isArray(removedRowsData)) { + const removedRowsMap: Map = new Map(removedRowIds.map((row) => [row.Id as string, ''])) + removedRowsData.filter((row) => { + if (removedRowsMap.has(row.Id)) { + removedRowsMap.set(row.Id, row.rowIndex) + return true } - }, - args: [removedRowsData, { page: paginationData.value.page, pageSize: paginationData.value.pageSize }], - }, - scope: defineViewScope({ view: viewMeta.value }), - }) + }) + const rowIndex = removedRowsData.map((row) => row.rowIndex) + formattedData.value = formattedData.value.filter((_, index) => rowIndex.includes(index)) + } + } catch (e: any) { + return message.error(`${t('msg.error.bulkDeleteRowsFailed')}: ${await extractSdkResponseErrorMsg(e)}`) + } + + // addUndo({ + // redo: { + // fn: async function redo(this: UndoRedoAction, removedRowsData: { Id: string; row: Row; rowIndex: number }[]) { + // const removedRowIds = await bulkDeleteRows( + // removedRowsData.map((row) => { + // return { + // Id: row.Id, + // } + // }), + // ) + // if (Array.isArray(removedRowIds)) { + // for (const { row } of removedRowsData) { + // const pk: Record = rowPkData(row.row, meta?.value?.columns as ColumnType[]) + // const rowIndex = findIndexByPk(pk, formattedData.value) + // if (rowIndex !== -1) formattedData.value.splice(rowIndex, 1) + // paginationData.value.totalRows = paginationData.value.totalRows! - 1 + // } + // } + // await callbacks?.syncPagination?.() + // }, + // args: [removedRowsData], + // }, + // undo: { + // fn: async function undo( + // this: UndoRedoAction, + // removedRowsData: { Id: string; row: Row; rowIndex: number }[], + // pg: { page: number; pageSize: number }, + // ) { + // const rowsToInsert = removedRowsData.reverse().map((row) => { + // const pkData = rowPkData(row.row, meta.value?.columns as ColumnType[]) + // row.row = { ...pkData, ...row.row } + // return row + // }) + + // const insertedRowIds = await bulkInsertRows( + // rowsToInsert.map((row) => row.row), + // {}, + // undefined, + // true, + // ) + // if (Array.isArray(insertedRowIds)) { + // for (const { row, rowIndex } of rowsToInsert) { + // recoverLTARRefs(row.row) + // if (rowIndex !== -1 && pg.pageSize === paginationData.value.pageSize) { + // if (pg.page === paginationData.value.page) { + // formattedData.value.splice(rowIndex, 0, row) + // } else { + // await callbacks?.changePage?.(pg.page) + // } + // } else { + // await callbacks?.loadData?.() + // } + // } + // } + // }, + // args: [removedRowsData, { page: paginationData.value.page, pageSize: paginationData.value.pageSize }], + // }, + // scope: defineViewScope({ view: viewMeta.value }), + // }) await callbacks?.syncCount?.() await callbacks?.syncPagination?.() @@ -714,6 +798,38 @@ export function useData(args: { await callbacks?.globalCallback?.() } + async function bulkDeleteRows( + rows: { Id: string }[], + { metaValue = meta.value, viewMetaValue = viewMeta.value }: { metaValue?: TableType; viewMetaValue?: ViewType } = {}, + undo = false, + ) { + try { + const bulkDeletedRowsData = await $api.dbDataTableRow.delete(metaValue?.id as string, rows, { + viewId: viewMetaValue?.id as string, + }) + // if (!undo) { + // addUndo({ + // redo: { + // fn: async function redo() {}, + // args: [], + // }, + // undo: { + // fn: async function undo(this: UndoRedoAction) {}, + // args: [], + // }, + // scope: defineViewScope({ view: viewMeta.value }), + // }) + // } + + await callbacks?.syncCount?.() + return bulkDeletedRowsData + } catch (error: any) { + message.error(await extractSdkResponseErrorMsg(error)) + } finally { + await callbacks?.globalCallback?.() + } + } + const removeRowIfNew = (row: Row) => { const index = formattedData.value.indexOf(row) From e905cd12a218eedfa5f892745a6cde8ee7cb6c58 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:49 +0000 Subject: [PATCH 02/14] feat: added undo redo for bulk delete --- packages/nc-gui/composables/useData.ts | 149 ++++++++++++++----------- 1 file changed, 83 insertions(+), 66 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index 130630664b..0ef5f28c74 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -1,5 +1,5 @@ import type { ColumnType, LinkToAnotherRecordType, PaginatedType, RelationTypes, TableType, ViewType } from 'nocodb-sdk' -import { UITypes } from 'nocodb-sdk' +import { UITypes, getSystemColumnsTitles, isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' import type { ComputedRef, Ref } from 'vue' import { NOCO, @@ -163,13 +163,26 @@ export function useData(args: { const rowsToInsert = [] try { for (const currentRow of rows) { - const { missingRequiredColumns, insertObj } = await populateInsertObject({ + let { missingRequiredColumns, insertObj } = await populateInsertObject({ meta: metaValue!, ltarState, getMeta, row: currentRow.row, undo, }) + + const autoGeneratedKeys = clone(metaValue?.columns || []) + .filter((c) => isCreatedOrLastModifiedByCol(c) || isCreatedOrLastModifiedTimeCol(c)) + .map((c) => c.title) + + console.log('auto', autoGeneratedKeys, currentRow) + // delete auto generated keys + for (const key of autoGeneratedKeys) { + if (key !== 'Id') { + delete insertObj[key] + } + } + if (missingRequiredColumns.size) continue else rowsToInsert.push({ ...insertObj, ...(ltarState || {}) }) } @@ -614,7 +627,6 @@ export function useData(args: { async function deleteSelectedRows() { let row = formattedData.value.length const removedRowsData: { Id: string; row: Row; rowIndex: number }[] = [] - console.log('formated rows', formattedData.value) while (row--) { const { row: rowObj, rowMeta } = formattedData.value[row] as Record @@ -641,80 +653,85 @@ export function useData(args: { } }), ) - if (Array.isArray(removedRowsData)) { + if (Array.isArray(removedRowIds)) { const removedRowsMap: Map = new Map(removedRowIds.map((row) => [row.Id as string, ''])) removedRowsData.filter((row) => { if (removedRowsMap.has(row.Id)) { - removedRowsMap.set(row.Id, row.rowIndex) return true } }) - const rowIndex = removedRowsData.map((row) => row.rowIndex) - formattedData.value = formattedData.value.filter((_, index) => rowIndex.includes(index)) + const rowIndexes = removedRowsData.map((row) => row.rowIndex) + formattedData.value = formattedData.value.filter((_, index) => rowIndexes.includes(index)) } } catch (e: any) { return message.error(`${t('msg.error.bulkDeleteRowsFailed')}: ${await extractSdkResponseErrorMsg(e)}`) } - // addUndo({ - // redo: { - // fn: async function redo(this: UndoRedoAction, removedRowsData: { Id: string; row: Row; rowIndex: number }[]) { - // const removedRowIds = await bulkDeleteRows( - // removedRowsData.map((row) => { - // return { - // Id: row.Id, - // } - // }), - // ) - // if (Array.isArray(removedRowIds)) { - // for (const { row } of removedRowsData) { - // const pk: Record = rowPkData(row.row, meta?.value?.columns as ColumnType[]) - // const rowIndex = findIndexByPk(pk, formattedData.value) - // if (rowIndex !== -1) formattedData.value.splice(rowIndex, 1) - // paginationData.value.totalRows = paginationData.value.totalRows! - 1 - // } - // } - // await callbacks?.syncPagination?.() - // }, - // args: [removedRowsData], - // }, - // undo: { - // fn: async function undo( - // this: UndoRedoAction, - // removedRowsData: { Id: string; row: Row; rowIndex: number }[], - // pg: { page: number; pageSize: number }, - // ) { - // const rowsToInsert = removedRowsData.reverse().map((row) => { - // const pkData = rowPkData(row.row, meta.value?.columns as ColumnType[]) - // row.row = { ...pkData, ...row.row } - // return row - // }) - - // const insertedRowIds = await bulkInsertRows( - // rowsToInsert.map((row) => row.row), - // {}, - // undefined, - // true, - // ) - // if (Array.isArray(insertedRowIds)) { - // for (const { row, rowIndex } of rowsToInsert) { - // recoverLTARRefs(row.row) - // if (rowIndex !== -1 && pg.pageSize === paginationData.value.pageSize) { - // if (pg.page === paginationData.value.page) { - // formattedData.value.splice(rowIndex, 0, row) - // } else { - // await callbacks?.changePage?.(pg.page) - // } - // } else { - // await callbacks?.loadData?.() - // } - // } - // } - // }, - // args: [removedRowsData, { page: paginationData.value.page, pageSize: paginationData.value.pageSize }], - // }, - // scope: defineViewScope({ view: viewMeta.value }), - // }) + addUndo({ + redo: { + fn: async function redo(this: UndoRedoAction, removedRowsData: { Id: string; row: Row; rowIndex: number }[]) { + const removedRowIds = await bulkDeleteRows( + removedRowsData.map((row) => { + return { + Id: row.Id, + } + }), + ) + + if (Array.isArray(removedRowIds)) { + for (const { row } of removedRowsData) { + const pk: Record = rowPkData(row.row, meta?.value?.columns as ColumnType[]) + const rowIndex = findIndexByPk(pk, formattedData.value) + if (rowIndex !== -1) formattedData.value.splice(rowIndex, 1) + paginationData.value.totalRows = paginationData.value.totalRows! - 1 + } + } + + await callbacks?.syncPagination?.() + }, + args: [removedRowsData], + }, + undo: { + fn: async function undo( + this: UndoRedoAction, + removedRowsData: { Id: string; row: Row; rowIndex: number }[], + pg: { page: number; pageSize: number }, + ) { + const rowsToInsert = removedRowsData + .map((row) => { + const pkData = rowPkData(row.row, meta.value?.columns as ColumnType[]) + row.row = { ...pkData, ...row.row } + return row + }) + .reverse() + + const insertedRowIds = await bulkInsertRows( + rowsToInsert.map((row) => row.row), + {}, + undefined, + true, + ) + + if (Array.isArray(insertedRowIds)) { + for (const { row, rowIndex } of rowsToInsert) { + recoverLTARRefs(row.row) + + if (rowIndex !== -1 && pg.pageSize === paginationData.value.pageSize) { + if (pg.page === paginationData.value.page) { + formattedData.value.splice(rowIndex, 0, row) + } else { + await callbacks?.changePage?.(pg.page) + } + } else { + await callbacks?.loadData?.() + } + } + } + }, + args: [removedRowsData, { page: paginationData.value.page, pageSize: paginationData.value.pageSize }], + }, + scope: defineViewScope({ view: viewMeta.value }), + }) await callbacks?.syncCount?.() await callbacks?.syncPagination?.() From d6359632b411bb8d3cfc96fb6eee4ae0ac408d02 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:49 +0000 Subject: [PATCH 03/14] feat: added bulk delete for range delete rows --- packages/nc-gui/composables/useData.ts | 154 ++++++++++++++----------- 1 file changed, 86 insertions(+), 68 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index 0ef5f28c74..f63b9d39cc 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -45,6 +45,8 @@ export function useData(args: { const { $api } = useNuxtApp() + const { isPaginationLoading } = storeToRefs(useViewsStore()) + const selectedAllRecords = computed({ get() { return !!formattedData.value.length && formattedData.value.every((row: Row) => row.rowMeta.selected) @@ -112,6 +114,7 @@ export function useData(args: { ) { row.row = { ...pkData, ...row.row } const insertedData = await insertRow(row, ltarState, undefined, true) + if (rowIndex !== -1 && pg.pageSize === paginationData.value.pageSize) { if (pg.page === paginationData.value.page) { formattedData.value.splice(rowIndex, 0, { @@ -156,61 +159,47 @@ export function useData(args: { async function bulkInsertRows( rows: Row[], - ltarState: Record = {}, { metaValue = meta.value, viewMetaValue = viewMeta.value }: { metaValue?: TableType; viewMetaValue?: ViewType } = {}, undo = false, ) { const rowsToInsert = [] + + isPaginationLoading.value = true try { for (const currentRow of rows) { let { missingRequiredColumns, insertObj } = await populateInsertObject({ meta: metaValue!, - ltarState, + ltarState: {}, getMeta, row: currentRow.row, undo, }) const autoGeneratedKeys = clone(metaValue?.columns || []) - .filter((c) => isCreatedOrLastModifiedByCol(c) || isCreatedOrLastModifiedTimeCol(c)) + .filter((c) => c.uidt !== UITypes.ID && (isCreatedOrLastModifiedByCol(c) || isCreatedOrLastModifiedTimeCol(c))) .map((c) => c.title) - console.log('auto', autoGeneratedKeys, currentRow) // delete auto generated keys for (const key of autoGeneratedKeys) { - if (key !== 'Id') { - delete insertObj[key] - } + delete insertObj[key!] } if (missingRequiredColumns.size) continue - else rowsToInsert.push({ ...insertObj, ...(ltarState || {}) }) + else rowsToInsert.push({ ...insertObj }) } - const bulkInsertedData = await $api.dbDataTableRow.create(metaValue?.id as string, rowsToInsert, { + const bulkInsertedIds = await $api.dbDataTableRow.create(metaValue?.id as string, rowsToInsert, { viewId: viewMetaValue?.id as string, }) - // if (!undo) { - // addUndo({ - // redo: { - // fn: async function redo() {}, - // args: [], - // }, - // undo: { - // fn: async function undo(this: UndoRedoAction) {}, - // args: [], - // }, - // scope: defineViewScope({ view: viewMeta.value }), - // }) - // } - await callbacks?.syncCount?.() - return bulkInsertedData + + return bulkInsertedIds } catch (error: any) { message.error(await extractSdkResponseErrorMsg(error)) } finally { await callbacks?.globalCallback?.() + isPaginationLoading.value = false } } @@ -653,13 +642,12 @@ export function useData(args: { } }), ) + if (Array.isArray(removedRowIds)) { const removedRowsMap: Map = new Map(removedRowIds.map((row) => [row.Id as string, ''])) - removedRowsData.filter((row) => { - if (removedRowsMap.has(row.Id)) { - return true - } - }) + + removedRowsData.filter((row) => removedRowsMap.has(row.Id)) + const rowIndexes = removedRowsData.map((row) => row.rowIndex) formattedData.value = formattedData.value.filter((_, index) => rowIndexes.includes(index)) } @@ -707,7 +695,6 @@ export function useData(args: { const insertedRowIds = await bulkInsertRows( rowsToInsert.map((row) => row.row), - {}, undefined, true, ) @@ -746,7 +733,7 @@ export function useData(args: { // plus one because we want to include the end row let row = start + 1 - const removedRowsData: { id?: string; row: Row; rowIndex: number }[] = [] + const removedRowsData: { Id: string; row: Row; rowIndex: number }[] = [] while (row--) { try { const { row: rowObj, rowMeta } = formattedData.value[row] as Record @@ -756,13 +743,10 @@ export function useData(args: { .map((c) => rowObj[c.title as string]) .join('___') - const successfulDeletion = await deleteRowById(id as string) - if (!successfulDeletion) { - continue + if (id) { + removedRowsData.push({ Id: id, row: clone(formattedData.value[row]), rowIndex: row }) } - removedRowsData.push({ id, row: clone(formattedData.value[row]), rowIndex: row }) } - formattedData.value.splice(row, 1) } catch (e: any) { return message.error(`${t('msg.error.deleteRowFailed')}: ${await extractSdkResponseErrorMsg(e)}`) } @@ -770,16 +754,46 @@ export function useData(args: { if (row === end) break } + try { + const removedRowIds: { Id: string }[] = await bulkDeleteRows( + removedRowsData.map((row) => { + return { + Id: row.Id, + } + }), + ) + if (Array.isArray(removedRowIds)) { + const removedRowsMap: Map = new Map(removedRowIds.map((row) => [row.Id as string, ''])) + + removedRowsData.filter((row) => removedRowsMap.has(row.Id)) + + const rowIndexes = removedRowsData.map((row) => row.rowIndex) + formattedData.value = formattedData.value.filter((_, index) => rowIndexes.includes(index)) + } + } catch (e: any) { + return message.error(`${t('msg.error.bulkDeleteRowsFailed')}: ${await extractSdkResponseErrorMsg(e)}`) + } + addUndo({ redo: { - fn: async function redo(this: UndoRedoAction, removedRowsData: { id?: string; row: Row; rowIndex: number }[]) { - for (const { id, row } of removedRowsData) { - await deleteRowById(id as string) - const pk: Record = rowPkData(row.row, meta?.value?.columns as ColumnType[]) - const rowIndex = findIndexByPk(pk, formattedData.value) - if (rowIndex !== -1) formattedData.value.splice(rowIndex, 1) - paginationData.value.totalRows = paginationData.value.totalRows! - 1 + fn: async function redo(this: UndoRedoAction, removedRowsData: { Id: string; row: Row; rowIndex: number }[]) { + const removedRowIds = await bulkDeleteRows( + removedRowsData.map((row) => { + return { + Id: row.Id, + } + }), + ) + + if (Array.isArray(removedRowIds)) { + for (const { row } of removedRowsData) { + const pk: Record = rowPkData(row.row, meta?.value?.columns as ColumnType[]) + const rowIndex = findIndexByPk(pk, formattedData.value) + if (rowIndex !== -1) formattedData.value.splice(rowIndex, 1) + paginationData.value.totalRows = paginationData.value.totalRows! - 1 + } } + await callbacks?.syncPagination?.() }, args: [removedRowsData], @@ -787,21 +801,36 @@ export function useData(args: { undo: { fn: async function undo( this: UndoRedoAction, - removedRowsData: { id?: string; row: Row; rowIndex: number }[], + removedRowsData: { Id: string; row: Row; rowIndex: number }[], pg: { page: number; pageSize: number }, ) { - for (const { row, rowIndex } of removedRowsData.slice().reverse()) { - const pkData = rowPkData(row.row, meta.value?.columns as ColumnType[]) - row.row = { ...pkData, ...row.row } - await insertRow(row, {}, {}, true) - if (rowIndex !== -1 && pg.pageSize === paginationData.value.pageSize) { - if (pg.page === paginationData.value.page) { - formattedData.value.splice(rowIndex, 0, row) + const rowsToInsert = removedRowsData + .map((row) => { + const pkData = rowPkData(row.row, meta.value?.columns as ColumnType[]) + row.row = { ...pkData, ...row.row } + return row + }) + .reverse() + + const insertedRowIds = await bulkInsertRows( + rowsToInsert.map((row) => row.row), + undefined, + true, + ) + + if (Array.isArray(insertedRowIds)) { + for (const { row, rowIndex } of rowsToInsert) { + recoverLTARRefs(row.row) + + if (rowIndex !== -1 && pg.pageSize === paginationData.value.pageSize) { + if (pg.page === paginationData.value.page) { + formattedData.value.splice(rowIndex, 0, row) + } else { + await callbacks?.changePage?.(pg.page) + } } else { - await callbacks?.changePage?.(pg.page) + await callbacks?.loadData?.() } - } else { - await callbacks?.loadData?.() } } }, @@ -818,32 +847,21 @@ export function useData(args: { async function bulkDeleteRows( rows: { Id: string }[], { metaValue = meta.value, viewMetaValue = viewMeta.value }: { metaValue?: TableType; viewMetaValue?: ViewType } = {}, - undo = false, ) { + isPaginationLoading.value = true try { const bulkDeletedRowsData = await $api.dbDataTableRow.delete(metaValue?.id as string, rows, { viewId: viewMetaValue?.id as string, }) - // if (!undo) { - // addUndo({ - // redo: { - // fn: async function redo() {}, - // args: [], - // }, - // undo: { - // fn: async function undo(this: UndoRedoAction) {}, - // args: [], - // }, - // scope: defineViewScope({ view: viewMeta.value }), - // }) - // } await callbacks?.syncCount?.() + return bulkDeletedRowsData } catch (error: any) { message.error(await extractSdkResponseErrorMsg(error)) } finally { await callbacks?.globalCallback?.() + isPaginationLoading.value = false } } From 2669747d311889084a7831a5273490cb5bd0279d Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:49 +0000 Subject: [PATCH 04/14] fix: typo error --- packages/nc-gui/composables/useData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index f63b9d39cc..9c555dc483 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -652,7 +652,7 @@ export function useData(args: { formattedData.value = formattedData.value.filter((_, index) => rowIndexes.includes(index)) } } catch (e: any) { - return message.error(`${t('msg.error.bulkDeleteRowsFailed')}: ${await extractSdkResponseErrorMsg(e)}`) + return message.error(`${t('msg.error.deleteRowFailed')}: ${await extractSdkResponseErrorMsg(e)}`) } addUndo({ @@ -771,7 +771,7 @@ export function useData(args: { formattedData.value = formattedData.value.filter((_, index) => rowIndexes.includes(index)) } } catch (e: any) { - return message.error(`${t('msg.error.bulkDeleteRowsFailed')}: ${await extractSdkResponseErrorMsg(e)}`) + return message.error(`${t('msg.error.deleteRowFailed')}: ${await extractSdkResponseErrorMsg(e)}`) } addUndo({ From 393537b98520453d4bb6e552d5092aa0ac46aaf8 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:49 +0000 Subject: [PATCH 05/14] fix: lint error --- packages/nc-gui/composables/useData.ts | 4 ++-- packages/nocodb-sdk/src/lib/UITypes.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index 9c555dc483..a30dcdf6d3 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -1,5 +1,5 @@ import type { ColumnType, LinkToAnotherRecordType, PaginatedType, RelationTypes, TableType, ViewType } from 'nocodb-sdk' -import { UITypes, getSystemColumnsTitles, isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' +import { UITypes, isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' import type { ComputedRef, Ref } from 'vue' import { NOCO, @@ -167,7 +167,7 @@ export function useData(args: { isPaginationLoading.value = true try { for (const currentRow of rows) { - let { missingRequiredColumns, insertObj } = await populateInsertObject({ + const { missingRequiredColumns, insertObj } = await populateInsertObject({ meta: metaValue!, ltarState: {}, getMeta, diff --git a/packages/nocodb-sdk/src/lib/UITypes.ts b/packages/nocodb-sdk/src/lib/UITypes.ts index 8e954b76c1..659a8e744d 100644 --- a/packages/nocodb-sdk/src/lib/UITypes.ts +++ b/packages/nocodb-sdk/src/lib/UITypes.ts @@ -137,6 +137,7 @@ export function isVirtualCol( // UITypes.Count, ].includes((typeof col === 'object' ? col?.uidt : col)); } + export function isCreatedOrLastModifiedTimeCol( col: | UITypes From b58da304347bed7ac2d8f3590ba81e9897e17e33 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:49 +0000 Subject: [PATCH 06/14] fix: oss import module issue --- packages/nc-gui/composables/useData.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index a30dcdf6d3..5d6cf4e7bf 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -1,5 +1,5 @@ import type { ColumnType, LinkToAnotherRecordType, PaginatedType, RelationTypes, TableType, ViewType } from 'nocodb-sdk' -import { UITypes, isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' +import { UITypes, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' import type { ComputedRef, Ref } from 'vue' import { NOCO, @@ -175,8 +175,13 @@ export function useData(args: { undo, }) + // Todo: use isCreatedOrLastModifiedByCol insted of hardcoded values once upgrader is ready for oss const autoGeneratedKeys = clone(metaValue?.columns || []) - .filter((c) => c.uidt !== UITypes.ID && (isCreatedOrLastModifiedByCol(c) || isCreatedOrLastModifiedTimeCol(c))) + .filter( + (c) => + c.uidt !== UITypes.ID && + (isCreatedOrLastModifiedTimeCol(c) || c.uidt === 'CreatedBy' || c.uidt === 'LastModifiedBy'), + ) .map((c) => c.title) // delete auto generated keys From 8c19de9f14695c3bb3fa6b7a4e1974de181fbd43 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:50 +0000 Subject: [PATCH 07/14] refactor: bulk delete useData fun --- packages/nc-gui/composables/useData.ts | 120 ++++++++++--------------- 1 file changed, 48 insertions(+), 72 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index 5d6cf4e7bf..e5452734e5 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -165,40 +165,41 @@ export function useData(args: { const rowsToInsert = [] isPaginationLoading.value = true - try { - for (const currentRow of rows) { - const { missingRequiredColumns, insertObj } = await populateInsertObject({ - meta: metaValue!, - ltarState: {}, - getMeta, - row: currentRow.row, - undo, - }) - - // Todo: use isCreatedOrLastModifiedByCol insted of hardcoded values once upgrader is ready for oss - const autoGeneratedKeys = clone(metaValue?.columns || []) - .filter( - (c) => - c.uidt !== UITypes.ID && - (isCreatedOrLastModifiedTimeCol(c) || c.uidt === 'CreatedBy' || c.uidt === 'LastModifiedBy'), - ) - .map((c) => c.title) - // delete auto generated keys - for (const key of autoGeneratedKeys) { - delete insertObj[key!] - } + // Todo: use isCreatedOrLastModifiedByCol insted of hardcoded values once upgrader is ready for oss + const autoGeneratedKeys = clone(metaValue?.columns || []) + .filter( + (c) => + c.uidt !== UITypes.ID && (isCreatedOrLastModifiedTimeCol(c) || c.uidt === 'CreatedBy' || c.uidt === 'LastModifiedBy'), + ) + .map((c) => c.title) - if (missingRequiredColumns.size) continue - else rowsToInsert.push({ ...insertObj }) - } + try { + rowsToInsert = + ( + await Promise.all( + rows.map(async (currentRow) => { + const { missingRequiredColumns, insertObj } = await populateInsertObject({ + meta: metaValue!, + ltarState: {}, + getMeta, + row: currentRow.row, + undo, + }) + + if (missingRequiredColumns.size === 0) { + autoGeneratedKeys.forEach((key) => delete insertObj[key]) + return insertObj + } + }), + ) + )?.filter(Boolean) ?? [] // Filter out undefined values (if any) const bulkInsertedIds = await $api.dbDataTableRow.create(metaValue?.id as string, rowsToInsert, { viewId: viewMetaValue?.id as string, }) await callbacks?.syncCount?.() - return bulkInsertedIds } catch (error: any) { message.error(await extractSdkResponseErrorMsg(error)) @@ -620,7 +621,7 @@ export function useData(args: { async function deleteSelectedRows() { let row = formattedData.value.length - const removedRowsData: { Id: string; row: Row; rowIndex: number }[] = [] + let removedRowsData: { Id: string; row: Row; rowIndex: number }[] = [] while (row--) { const { row: rowObj, rowMeta } = formattedData.value[row] as Record @@ -628,10 +629,7 @@ export function useData(args: { continue } if (!rowMeta.new) { - const id = meta?.value?.columns - ?.filter((c) => c.pk) - .map((c) => rowObj[c.title as string]) - .join('___') + const id = extractPkFromRow(rowObj, meta?.value?.columns as ColumnType[]) if (id) { removedRowsData.push({ Id: id, row: clone(formattedData.value[row]), rowIndex: row }) @@ -640,21 +638,15 @@ export function useData(args: { } try { - const removedRowIds: { Id: string }[] = await bulkDeleteRows( - removedRowsData.map((row) => { - return { - Id: row.Id, - } - }), - ) + const removedRowIds: { Id: string }[] = await bulkDeleteRows(removedRowsData.map((row) => ({ Id: row.Id }))) if (Array.isArray(removedRowIds)) { - const removedRowsMap: Map = new Map(removedRowIds.map((row) => [row.Id as string, ''])) + const removedRowIdsSet = new Set(removedRowIds.map((row) => row.Id)) - removedRowsData.filter((row) => removedRowsMap.has(row.Id)) + removedRowsData = removedRowsData.filter((row) => removedRowIdsSet.has(row.Id)) - const rowIndexes = removedRowsData.map((row) => row.rowIndex) - formattedData.value = formattedData.value.filter((_, index) => rowIndexes.includes(index)) + const rowIndexesSet = new Set(removedRowsData.map((row) => row.rowIndex)) + formattedData.value = formattedData.value.filter((_, index) => rowIndexesSet.has(index)) } } catch (e: any) { return message.error(`${t('msg.error.deleteRowFailed')}: ${await extractSdkResponseErrorMsg(e)}`) @@ -663,13 +655,7 @@ export function useData(args: { addUndo({ redo: { fn: async function redo(this: UndoRedoAction, removedRowsData: { Id: string; row: Row; rowIndex: number }[]) { - const removedRowIds = await bulkDeleteRows( - removedRowsData.map((row) => { - return { - Id: row.Id, - } - }), - ) + const removedRowIds = await bulkDeleteRows(removedRowsData.map((row) => ({ Id: row.Id }))) if (Array.isArray(removedRowIds)) { for (const { row } of removedRowsData) { @@ -738,15 +724,14 @@ export function useData(args: { // plus one because we want to include the end row let row = start + 1 - const removedRowsData: { Id: string; row: Row; rowIndex: number }[] = [] + let removedRowsData: { Id: string; row: Row; rowIndex: number }[] = [] + while (row--) { try { const { row: rowObj, rowMeta } = formattedData.value[row] as Record + if (!rowMeta.new) { - const id = meta?.value?.columns - ?.filter((c) => c.pk) - .map((c) => rowObj[c.title as string]) - .join('___') + const id = extractPkFromRow(rowObj, meta?.value?.columns as ColumnType[]) if (id) { removedRowsData.push({ Id: id, row: clone(formattedData.value[row]), rowIndex: row }) @@ -760,20 +745,15 @@ export function useData(args: { } try { - const removedRowIds: { Id: string }[] = await bulkDeleteRows( - removedRowsData.map((row) => { - return { - Id: row.Id, - } - }), - ) + const removedRowIds: { Id: string }[] = await bulkDeleteRows(removedRowsData.map((row) => ({ Id: row.Id }))) + if (Array.isArray(removedRowIds)) { - const removedRowsMap: Map = new Map(removedRowIds.map((row) => [row.Id as string, ''])) + const removedRowIdsSet = new Set(removedRowIds.map((row) => row.Id)) - removedRowsData.filter((row) => removedRowsMap.has(row.Id)) + removedRowsData = removedRowsData.filter((row) => removedRowIdsSet.has(row.Id)) - const rowIndexes = removedRowsData.map((row) => row.rowIndex) - formattedData.value = formattedData.value.filter((_, index) => rowIndexes.includes(index)) + const rowIndexesSet = new Set(removedRowsData.map((row) => row.rowIndex)) + formattedData.value = formattedData.value.filter((_, index) => rowIndexesSet.has(index)) } } catch (e: any) { return message.error(`${t('msg.error.deleteRowFailed')}: ${await extractSdkResponseErrorMsg(e)}`) @@ -782,13 +762,7 @@ export function useData(args: { addUndo({ redo: { fn: async function redo(this: UndoRedoAction, removedRowsData: { Id: string; row: Row; rowIndex: number }[]) { - const removedRowIds = await bulkDeleteRows( - removedRowsData.map((row) => { - return { - Id: row.Id, - } - }), - ) + const removedRowIds = await bulkDeleteRows(removedRowsData.map((row) => ({ Id: row.Id }))) if (Array.isArray(removedRowIds)) { for (const { row } of removedRowsData) { @@ -893,5 +867,7 @@ export function useData(args: { bulkUpdateView, selectedAllRecords, removeRowIfNew, + bulkDeleteRows, + bulkInsertRows, } } From 6f14da61214ca95a9109b03d550d1d4423f6d7b9 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:50 +0000 Subject: [PATCH 08/14] fix: lint errors --- packages/nc-gui/composables/useData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index e5452734e5..d1d3a56ce0 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -162,7 +162,7 @@ export function useData(args: { { metaValue = meta.value, viewMetaValue = viewMeta.value }: { metaValue?: TableType; viewMetaValue?: ViewType } = {}, undo = false, ) { - const rowsToInsert = [] + let rowsToInsert = [] isPaginationLoading.value = true @@ -188,7 +188,7 @@ export function useData(args: { }) if (missingRequiredColumns.size === 0) { - autoGeneratedKeys.forEach((key) => delete insertObj[key]) + autoGeneratedKeys.forEach((key) => delete insertObj[key!]) return insertObj } }), From 46fd690a57908291ac0e24e27b341eacd236b588 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:50 +0000 Subject: [PATCH 09/14] fix: review changes --- packages/nc-gui/composables/useData.ts | 74 ++++++++++++++++++-------- packages/nc-gui/utils/dataUtils.ts | 6 +++ 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index d1d3a56ce0..74ec9d851c 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -621,7 +621,8 @@ export function useData(args: { async function deleteSelectedRows() { let row = formattedData.value.length - let removedRowsData: { Id: string; row: Row; rowIndex: number }[] = [] + let removedRowsData: Record[] = [] + let pk = '' while (row--) { const { row: rowObj, rowMeta } = formattedData.value[row] as Record @@ -629,33 +630,44 @@ export function useData(args: { continue } if (!rowMeta.new) { - const id = extractPkFromRow(rowObj, meta?.value?.columns as ColumnType[]) + const primaryKey = extractPk(meta?.value?.columns as ColumnType[]) + const pkValue = extractPkFromRow(rowObj, meta?.value?.columns as ColumnType[]) - if (id) { - removedRowsData.push({ Id: id, row: clone(formattedData.value[row]), rowIndex: row }) + if (primaryKey && pkValue) { + if (!pk) pk = primaryKey + + removedRowsData.push({ [pk]: pkValue as string, row: clone(formattedData.value[row]) as Row, rowIndex: row as number }) } } } + if (!removedRowsData.length) return + try { - const removedRowIds: { Id: string }[] = await bulkDeleteRows(removedRowsData.map((row) => ({ Id: row.Id }))) + const removedRowIds: Record[] = await bulkDeleteRows( + removedRowsData.map((row) => ({ [pk]: row[pk] as string })), + ) if (Array.isArray(removedRowIds)) { - const removedRowIdsSet = new Set(removedRowIds.map((row) => row.Id)) + const removedRowsDataSet = new Set(removedRowIds.map((row) => row[pk])) - removedRowsData = removedRowsData.filter((row) => removedRowIdsSet.has(row.Id)) + removedRowsData = removedRowsData.filter((row) => removedRowsDataSet.has(row[pk] as string)) const rowIndexesSet = new Set(removedRowsData.map((row) => row.rowIndex)) formattedData.value = formattedData.value.filter((_, index) => rowIndexesSet.has(index)) + } else { + removedRowsData = [] } } catch (e: any) { return message.error(`${t('msg.error.deleteRowFailed')}: ${await extractSdkResponseErrorMsg(e)}`) } + if (!removedRowsData.length) return + addUndo({ redo: { - fn: async function redo(this: UndoRedoAction, removedRowsData: { Id: string; row: Row; rowIndex: number }[]) { - const removedRowIds = await bulkDeleteRows(removedRowsData.map((row) => ({ Id: row.Id }))) + fn: async function redo(this: UndoRedoAction, removedRowsData: Record[], pk: string) { + const removedRowIds = await bulkDeleteRows(removedRowsData.map((row) => ({ [pk]: row[pk] as string }))) if (Array.isArray(removedRowIds)) { for (const { row } of removedRowsData) { @@ -668,12 +680,12 @@ export function useData(args: { await callbacks?.syncPagination?.() }, - args: [removedRowsData], + args: [removedRowsData, pk], }, undo: { fn: async function undo( this: UndoRedoAction, - removedRowsData: { Id: string; row: Row; rowIndex: number }[], + removedRowsData: Record[], pg: { page: number; pageSize: number }, ) { const rowsToInsert = removedRowsData @@ -724,17 +736,25 @@ export function useData(args: { // plus one because we want to include the end row let row = start + 1 - let removedRowsData: { Id: string; row: Row; rowIndex: number }[] = [] + let removedRowsData: Record[] = [] + let pk = '' while (row--) { try { const { row: rowObj, rowMeta } = formattedData.value[row] as Record if (!rowMeta.new) { - const id = extractPkFromRow(rowObj, meta?.value?.columns as ColumnType[]) + const primaryKey = extractPk(meta?.value?.columns as ColumnType[]) + const pkValue = extractPkFromRow(rowObj, meta?.value?.columns as ColumnType[]) + + if (primaryKey && pkValue) { + if (!pk) pk = primaryKey - if (id) { - removedRowsData.push({ Id: id, row: clone(formattedData.value[row]), rowIndex: row }) + removedRowsData.push({ + [pk]: pkValue as string, + row: clone(formattedData.value[row]) as Row, + rowIndex: row as number, + }) } } } catch (e: any) { @@ -744,25 +764,33 @@ export function useData(args: { if (row === end) break } + if (!removedRowsData.length) return + try { - const removedRowIds: { Id: string }[] = await bulkDeleteRows(removedRowsData.map((row) => ({ Id: row.Id }))) + const removedRowIds: Record[] = await bulkDeleteRows( + removedRowsData.map((row) => ({ [pk]: row[pk] as string })), + ) if (Array.isArray(removedRowIds)) { - const removedRowIdsSet = new Set(removedRowIds.map((row) => row.Id)) + const removedRowsDataSet = new Set(removedRowIds.map((row) => row[pk])) - removedRowsData = removedRowsData.filter((row) => removedRowIdsSet.has(row.Id)) + removedRowsData = removedRowsData.filter((row) => removedRowsDataSet.has(row[pk] as string)) const rowIndexesSet = new Set(removedRowsData.map((row) => row.rowIndex)) formattedData.value = formattedData.value.filter((_, index) => rowIndexesSet.has(index)) + } else { + removedRowsData = [] } } catch (e: any) { return message.error(`${t('msg.error.deleteRowFailed')}: ${await extractSdkResponseErrorMsg(e)}`) } + if (!removedRowsData.length) return + addUndo({ redo: { - fn: async function redo(this: UndoRedoAction, removedRowsData: { Id: string; row: Row; rowIndex: number }[]) { - const removedRowIds = await bulkDeleteRows(removedRowsData.map((row) => ({ Id: row.Id }))) + fn: async function redo(this: UndoRedoAction, removedRowsData: Record[], pk: string) { + const removedRowIds = await bulkDeleteRows(removedRowsData.map((row) => ({ [pk]: row[pk] as string }))) if (Array.isArray(removedRowIds)) { for (const { row } of removedRowsData) { @@ -775,12 +803,12 @@ export function useData(args: { await callbacks?.syncPagination?.() }, - args: [removedRowsData], + args: [removedRowsData, pk], }, undo: { fn: async function undo( this: UndoRedoAction, - removedRowsData: { Id: string; row: Row; rowIndex: number }[], + removedRowsData: Record[], pg: { page: number; pageSize: number }, ) { const rowsToInsert = removedRowsData @@ -824,7 +852,7 @@ export function useData(args: { } async function bulkDeleteRows( - rows: { Id: string }[], + rows: Record[], { metaValue = meta.value, viewMetaValue = viewMeta.value }: { metaValue?: TableType; viewMetaValue?: ViewType } = {}, ) { isPaginationLoading.value = true diff --git a/packages/nc-gui/utils/dataUtils.ts b/packages/nc-gui/utils/dataUtils.ts index b7fda18cb9..0465c388a5 100644 --- a/packages/nc-gui/utils/dataUtils.ts +++ b/packages/nc-gui/utils/dataUtils.ts @@ -22,6 +22,12 @@ export const rowPkData = (row: Record, columns: ColumnType[]) => { return pkData } +export const extractPk = (columns: ColumnType[]) => { + if (!columns && !Array.isArray(columns)) return null + const pkData = columns?.find((c) => c.pk) + return pkData ? pkData?.title || pkData?.column_name : null +} + export const findIndexByPk = (pk: Record, data: Row[]) => { for (const [i, row] of Object.entries(data)) { if (Object.keys(pk).every((k) => pk[k] === row.row[k])) { From b348b4d7b49ac8ee24def62426ea7534750768ec Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:50 +0000 Subject: [PATCH 10/14] fix: small changes --- packages/nc-gui/composables/useData.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index 74ec9d851c..299b833bf0 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -1,5 +1,5 @@ import type { ColumnType, LinkToAnotherRecordType, PaginatedType, RelationTypes, TableType, ViewType } from 'nocodb-sdk' -import { UITypes, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' +import { UITypes, isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' import type { ComputedRef, Ref } from 'vue' import { NOCO, @@ -166,12 +166,8 @@ export function useData(args: { isPaginationLoading.value = true - // Todo: use isCreatedOrLastModifiedByCol insted of hardcoded values once upgrader is ready for oss const autoGeneratedKeys = clone(metaValue?.columns || []) - .filter( - (c) => - c.uidt !== UITypes.ID && (isCreatedOrLastModifiedTimeCol(c) || c.uidt === 'CreatedBy' || c.uidt === 'LastModifiedBy'), - ) + .filter((c) => !c.pk && (isCreatedOrLastModifiedTimeCol(c) || isCreatedOrLastModifiedByCol(c))) .map((c) => c.title) try { From 5eb3dc2c10b5856b3b01191d2c284e1cb55dc69d Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:50 +0000 Subject: [PATCH 11/14] fix: oss build fail issue --- packages/nc-gui/composables/useData.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index 299b833bf0..55c577aec6 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -1,5 +1,5 @@ import type { ColumnType, LinkToAnotherRecordType, PaginatedType, RelationTypes, TableType, ViewType } from 'nocodb-sdk' -import { UITypes, isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' +import { UITypes, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' import type { ComputedRef, Ref } from 'vue' import { NOCO, @@ -166,8 +166,9 @@ export function useData(args: { isPaginationLoading.value = true + // Todo: use isCreatedOrLastModifiedByCol insted of hardcoded values once upgrader is ready for oss const autoGeneratedKeys = clone(metaValue?.columns || []) - .filter((c) => !c.pk && (isCreatedOrLastModifiedTimeCol(c) || isCreatedOrLastModifiedByCol(c))) + .filter((c) => !c.pk && (isCreatedOrLastModifiedTimeCol(c) || c.uidt === 'CreatedBy' || c.uidt === 'LastModifiedBy')) .map((c) => c.title) try { From 02eb9139bf7c603bd5f76946028cac1891859e92 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:50 +0000 Subject: [PATCH 12/14] fix: review change - extractPk --- packages/nc-gui/composables/useData.ts | 1 + packages/nc-gui/utils/dataUtils.ts | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index 55c577aec6..7abdb63833 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -4,6 +4,7 @@ import type { ComputedRef, Ref } from 'vue' import { NOCO, computed, + extractPk, extractPkFromRow, extractSdkResponseErrorMsg, findIndexByPk, diff --git a/packages/nc-gui/utils/dataUtils.ts b/packages/nc-gui/utils/dataUtils.ts index 0465c388a5..229123547e 100644 --- a/packages/nc-gui/utils/dataUtils.ts +++ b/packages/nc-gui/utils/dataUtils.ts @@ -24,8 +24,10 @@ export const rowPkData = (row: Record, columns: ColumnType[]) => { export const extractPk = (columns: ColumnType[]) => { if (!columns && !Array.isArray(columns)) return null - const pkData = columns?.find((c) => c.pk) - return pkData ? pkData?.title || pkData?.column_name : null + return columns + .filter((c) => c.pk) + .map((c) => c.title) + .join('___') } export const findIndexByPk = (pk: Record, data: Row[]) => { From bc7f38e114eb2f833afdebd1799846c0e20fd677 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:50 +0000 Subject: [PATCH 13/14] fix(nc-gui): review changes --- packages/nc-gui/composables/useData.ts | 71 ++++++++++++++------------ 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index 7abdb63833..c7afe11002 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -1,5 +1,5 @@ import type { ColumnType, LinkToAnotherRecordType, PaginatedType, RelationTypes, TableType, ViewType } from 'nocodb-sdk' -import { UITypes, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' +import { UITypes, isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedTimeCol } from 'nocodb-sdk' import type { ComputedRef, Ref } from 'vue' import { NOCO, @@ -163,17 +163,14 @@ export function useData(args: { { metaValue = meta.value, viewMetaValue = viewMeta.value }: { metaValue?: TableType; viewMetaValue?: ViewType } = {}, undo = false, ) { - let rowsToInsert = [] - isPaginationLoading.value = true - // Todo: use isCreatedOrLastModifiedByCol insted of hardcoded values once upgrader is ready for oss const autoGeneratedKeys = clone(metaValue?.columns || []) - .filter((c) => !c.pk && (isCreatedOrLastModifiedTimeCol(c) || c.uidt === 'CreatedBy' || c.uidt === 'LastModifiedBy')) + .filter((c) => !c.pk && (isCreatedOrLastModifiedTimeCol(c) || isCreatedOrLastModifiedByCol(c))) .map((c) => c.title) try { - rowsToInsert = + const rowsToInsert = ( await Promise.all( rows.map(async (currentRow) => { @@ -620,7 +617,7 @@ export function useData(args: { async function deleteSelectedRows() { let row = formattedData.value.length let removedRowsData: Record[] = [] - let pk = '' + let compositePrimaryKey = '' while (row--) { const { row: rowObj, rowMeta } = formattedData.value[row] as Record @@ -628,13 +625,17 @@ export function useData(args: { continue } if (!rowMeta.new) { - const primaryKey = extractPk(meta?.value?.columns as ColumnType[]) - const pkValue = extractPkFromRow(rowObj, meta?.value?.columns as ColumnType[]) + const extractedPk = extractPk(meta?.value?.columns as ColumnType[]) + const compositePkValue = extractPkFromRow(rowObj, meta?.value?.columns as ColumnType[]) - if (primaryKey && pkValue) { - if (!pk) pk = primaryKey + if (extractedPk && compositePkValue) { + if (!compositePrimaryKey) compositePrimaryKey = extractedPk - removedRowsData.push({ [pk]: pkValue as string, row: clone(formattedData.value[row]) as Row, rowIndex: row as number }) + removedRowsData.push({ + [compositePrimaryKey]: compositePkValue as string, + row: clone(formattedData.value[row]) as Row, + rowIndex: row as number, + }) } } } @@ -643,13 +644,13 @@ export function useData(args: { try { const removedRowIds: Record[] = await bulkDeleteRows( - removedRowsData.map((row) => ({ [pk]: row[pk] as string })), + removedRowsData.map((row) => ({ [compositePrimaryKey]: row[compositePrimaryKey] as string })), ) if (Array.isArray(removedRowIds)) { - const removedRowsDataSet = new Set(removedRowIds.map((row) => row[pk])) + const removedRowsDataSet = new Set(removedRowIds.map((row) => row[compositePrimaryKey])) - removedRowsData = removedRowsData.filter((row) => removedRowsDataSet.has(row[pk] as string)) + removedRowsData = removedRowsData.filter((row) => removedRowsDataSet.has(row[compositePrimaryKey] as string)) const rowIndexesSet = new Set(removedRowsData.map((row) => row.rowIndex)) formattedData.value = formattedData.value.filter((_, index) => rowIndexesSet.has(index)) @@ -664,13 +665,15 @@ export function useData(args: { addUndo({ redo: { - fn: async function redo(this: UndoRedoAction, removedRowsData: Record[], pk: string) { - const removedRowIds = await bulkDeleteRows(removedRowsData.map((row) => ({ [pk]: row[pk] as string }))) + fn: async function redo(this: UndoRedoAction, removedRowsData: Record[], compositePrimaryKey: string) { + const removedRowIds = await bulkDeleteRows( + removedRowsData.map((row) => ({ [compositePrimaryKey]: row[compositePrimaryKey] as string })), + ) if (Array.isArray(removedRowIds)) { for (const { row } of removedRowsData) { - const pk: Record = rowPkData(row.row, meta?.value?.columns as ColumnType[]) - const rowIndex = findIndexByPk(pk, formattedData.value) + const primaryKey: Record = rowPkData(row.row, meta?.value?.columns as ColumnType[]) + const rowIndex = findIndexByPk(primaryKey, formattedData.value) if (rowIndex !== -1) formattedData.value.splice(rowIndex, 1) paginationData.value.totalRows = paginationData.value.totalRows! - 1 } @@ -678,7 +681,7 @@ export function useData(args: { await callbacks?.syncPagination?.() }, - args: [removedRowsData, pk], + args: [removedRowsData, compositePrimaryKey], }, undo: { fn: async function undo( @@ -735,21 +738,21 @@ export function useData(args: { let row = start + 1 let removedRowsData: Record[] = [] - let pk = '' + let compositePrimaryKey = '' while (row--) { try { const { row: rowObj, rowMeta } = formattedData.value[row] as Record if (!rowMeta.new) { - const primaryKey = extractPk(meta?.value?.columns as ColumnType[]) - const pkValue = extractPkFromRow(rowObj, meta?.value?.columns as ColumnType[]) + const extractedPk = extractPk(meta?.value?.columns as ColumnType[]) + const compositePkValue = extractPkFromRow(rowObj, meta?.value?.columns as ColumnType[]) - if (primaryKey && pkValue) { - if (!pk) pk = primaryKey + if (extractedPk && compositePkValue) { + if (!compositePrimaryKey) compositePrimaryKey = extractedPk removedRowsData.push({ - [pk]: pkValue as string, + [compositePrimaryKey]: compositePkValue as string, row: clone(formattedData.value[row]) as Row, rowIndex: row as number, }) @@ -766,13 +769,13 @@ export function useData(args: { try { const removedRowIds: Record[] = await bulkDeleteRows( - removedRowsData.map((row) => ({ [pk]: row[pk] as string })), + removedRowsData.map((row) => ({ [compositePrimaryKey]: row[compositePrimaryKey] as string })), ) if (Array.isArray(removedRowIds)) { - const removedRowsDataSet = new Set(removedRowIds.map((row) => row[pk])) + const removedRowsDataSet = new Set(removedRowIds.map((row) => row[compositePrimaryKey])) - removedRowsData = removedRowsData.filter((row) => removedRowsDataSet.has(row[pk] as string)) + removedRowsData = removedRowsData.filter((row) => removedRowsDataSet.has(row[compositePrimaryKey] as string)) const rowIndexesSet = new Set(removedRowsData.map((row) => row.rowIndex)) formattedData.value = formattedData.value.filter((_, index) => rowIndexesSet.has(index)) @@ -787,13 +790,13 @@ export function useData(args: { addUndo({ redo: { - fn: async function redo(this: UndoRedoAction, removedRowsData: Record[], pk: string) { - const removedRowIds = await bulkDeleteRows(removedRowsData.map((row) => ({ [pk]: row[pk] as string }))) + fn: async function redo(this: UndoRedoAction, removedRowsData: Record[], compositePrimaryKey: string) { + const removedRowIds = await bulkDeleteRows(removedRowsData.map((row) => ({ [compositePrimaryKey]: row[compositePrimaryKey] as string }))) if (Array.isArray(removedRowIds)) { for (const { row } of removedRowsData) { - const pk: Record = rowPkData(row.row, meta?.value?.columns as ColumnType[]) - const rowIndex = findIndexByPk(pk, formattedData.value) + const primaryKey: Record = rowPkData(row.row, meta?.value?.columns as ColumnType[]) + const rowIndex = findIndexByPk(primaryKey, formattedData.value) if (rowIndex !== -1) formattedData.value.splice(rowIndex, 1) paginationData.value.totalRows = paginationData.value.totalRows! - 1 } @@ -801,7 +804,7 @@ export function useData(args: { await callbacks?.syncPagination?.() }, - args: [removedRowsData, pk], + args: [removedRowsData, compositePrimaryKey], }, undo: { fn: async function undo( From 262af67a66c09a106bc48673b5836f948da3c3c7 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:33:50 +0000 Subject: [PATCH 14/14] fix(nc-gui): linting --- packages/nc-gui/composables/useData.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nc-gui/composables/useData.ts b/packages/nc-gui/composables/useData.ts index c7afe11002..df497941fc 100644 --- a/packages/nc-gui/composables/useData.ts +++ b/packages/nc-gui/composables/useData.ts @@ -791,7 +791,9 @@ export function useData(args: { addUndo({ redo: { fn: async function redo(this: UndoRedoAction, removedRowsData: Record[], compositePrimaryKey: string) { - const removedRowIds = await bulkDeleteRows(removedRowsData.map((row) => ({ [compositePrimaryKey]: row[compositePrimaryKey] as string }))) + const removedRowIds = await bulkDeleteRows( + removedRowsData.map((row) => ({ [compositePrimaryKey]: row[compositePrimaryKey] as string })), + ) if (Array.isArray(removedRowIds)) { for (const { row } of removedRowsData) {