From be7f60e388125db1162674e26380e0bea970936a Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 14 Jun 2024 08:35:58 +0000 Subject: [PATCH 1/2] refactor: type corrections and update state to deleting and delete filter in background to avoid duplicate click --- packages/nc-gui/composables/useViewFilters.ts | 119 ++++++++++-------- 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/packages/nc-gui/composables/useViewFilters.ts b/packages/nc-gui/composables/useViewFilters.ts index 51e905bd01..f4d3c302bb 100644 --- a/packages/nc-gui/composables/useViewFilters.ts +++ b/packages/nc-gui/composables/useViewFilters.ts @@ -10,12 +10,14 @@ import type { ComputedRef, Ref } from 'vue' import type { SelectProps } from 'ant-design-vue' import { UITypes, isSystemColumn } from 'nocodb-sdk' +type ColumnFilterType = FilterType & { status?: string; id?: string; children?: ColumnFilterType[]; is_group?: boolean } + export function useViewFilters( view: Ref, _parentId: Ref | null | string, autoApply?: ComputedRef, reloadData?: () => void, - _currentFilters?: Filter[], + _currentFilters?: ColumnFilterType[], isNestedRoot?: boolean, isWebhook?: boolean, isLink?: boolean, @@ -27,7 +29,7 @@ export function useViewFilters( const currentFilters = ref(_currentFilters) - const btLookupTypesMap = ref({}) + const btLookupTypesMap = ref>({}) const reloadHook = inject(ReloadViewDataHookInj) @@ -45,15 +47,15 @@ export function useViewFilters( const { addUndo, clone, defineViewScope } = useUndoRedo() - const _filters = ref([...(currentFilters.value || [])]) + const _filters = ref([...(currentFilters.value || [])]) const nestedMode = computed(() => isPublic.value || !isUIAllowed('filterSync') || !isUIAllowed('filterChildrenRead')) - const filters = computed({ + const filters = computed({ get: () => { return nestedMode.value && !isLink && !isWebhook ? currentFilters.value! : _filters.value }, - set: (value: Filter[]) => { + set: (value: ColumnFilterType[]) => { if (nestedMode.value) { currentFilters.value = value if (!isLink && !isWebhook) { @@ -117,9 +119,9 @@ export function useViewFilters( }, {}) }) - const lastFilters = ref([]) + const lastFilters = ref([]) - watchOnce(filters, (filters: Filter[]) => { + watchOnce(filters, (filters: ColumnFilterType[]) => { lastFilters.value = clone(filters) }) @@ -131,7 +133,7 @@ export function useViewFilters( } const isComparisonOpAllowed = ( - filter: FilterType, + filter: ColumnFilterType, compOp: { text: string value: string @@ -141,7 +143,7 @@ export function useViewFilters( }, ) => { const isNullOrEmptyOp = ['empty', 'notempty', 'null', 'notnull'].includes(compOp.value) - const uidt = types.value[filter.fk_column_id] + const uidt = types.value[filter.fk_column_id!] if (compOp.includedTypes) { // include allowed values only if selected column type matches @@ -167,7 +169,7 @@ export function useViewFilters( } const isComparisonSubOpAllowed = ( - filter: FilterType, + filter: ColumnFilterType, compOp: { text: string value: string @@ -176,7 +178,7 @@ export function useViewFilters( excludedTypes?: UITypes[] }, ) => { - const uidt = types.value[filter.fk_column_id] + const uidt = types.value[filter.fk_column_id!] if (compOp.includedTypes) { // include allowed values only if selected column type matches @@ -187,7 +189,7 @@ export function useViewFilters( } } - const placeholderFilter = (): Filter => { + const placeholderFilter = (): ColumnFilterType => { const logicalOps = new Set(filters.value.slice(1).map((filter) => filter.logical_op)) return { @@ -200,7 +202,7 @@ export function useViewFilters( } } - const placeholderGroupFilter = (): Filter => { + const placeholderGroupFilter = (): ColumnFilterType => { const logicalOps = new Set(filters.value.slice(1).map((filter) => filter.logical_op)) return { @@ -210,12 +212,12 @@ export function useViewFilters( } } - const loadAllChildFilters = async (filters: Filter[]) => { + const loadAllChildFilters = async (filters: ColumnFilterType[]) => { // Array to store promises of child filter loading const promises = [] // Array to store all child filters - const allChildFilters: Filter[] = [] + const allChildFilters: ColumnFilterType[] = [] // Iterate over all filters for (const filter of filters) { @@ -223,7 +225,7 @@ export function useViewFilters( if (filter.id && filter.is_group) { // Load children filters from the backend const childFilterPromise = $api.dbTableFilter.childrenRead(filter.id).then((response) => { - const childFilters = response.list as Filter[] + const childFilters = response.list as ColumnFilterType[] allChildFilters.push(...childFilters) return loadAllChildFilters(childFilters) }) @@ -235,7 +237,7 @@ export function useViewFilters( await Promise.all(promises) // Push all child filters into the allFilters array - if (!isLink && !isWebhook) allFilters.value.push(...allChildFilters) + if (!isLink && !isWebhook) allFilters.value.push(...(allChildFilters as FilterType[])) } const loadFilters = async ({ @@ -259,25 +261,25 @@ export function useViewFilters( try { if (isWebhook || hookId) { if (parentId.value) { - filters.value = (await $api.dbTableFilter.childrenRead(parentId.value)).list as Filter[] + filters.value = (await $api.dbTableFilter.childrenRead(parentId.value)).list as ColumnFilterType[] } else if (hookId && !isNestedRoot) { - filters.value = (await $api.dbTableWebhookFilter.read(hookId)).list as Filter[] + filters.value = (await $api.dbTableWebhookFilter.read(hookId)).list as ColumnFilterType[] } } else { if (isLink || linkColId?.value) { if (parentId.value) { - filters.value = (await $api.dbTableFilter.childrenRead(parentId.value)).list as Filter[] + filters.value = (await $api.dbTableFilter.childrenRead(parentId.value)).list as ColumnFilterType[] } else if (linkColId?.value && !isNestedRoot) { - filters.value = (await $api.dbTableLinkFilter.read(linkColId?.value)).list as Filter[] + filters.value = (await $api.dbTableLinkFilter.read(linkColId?.value)).list as ColumnFilterType[] } } else { if (parentId.value) { - filters.value = (await $api.dbTableFilter.childrenRead(parentId.value)).list as Filter[] + filters.value = (await $api.dbTableFilter.childrenRead(parentId.value)).list as ColumnFilterType[] } else { - filters.value = (await $api.dbTableFilter.read(view.value!.id!)).list as Filter[] + filters.value = (await $api.dbTableFilter.read(view.value!.id!)).list as ColumnFilterType[] if (loadAllFilters) { - allFilters.value = [...filters.value] - await loadAllChildFilters(allFilters.value) + allFilters.value = [...filters.value] as FilterType[] + await loadAllChildFilters(allFilters.value as ColumnFilterType[]) } } } @@ -311,23 +313,26 @@ export function useViewFilters( ...filter, children: undefined, fk_parent_id: parentId.value, - })) as unknown as FilterType + } as FilterType)) as ColumnFilterType } else if (linkId || linkColId?.value) { filters.value[+i] = (await $api.dbTableLinkFilter.create(linkId || linkColId.value, { ...filter, children: undefined, fk_parent_id: parentId.value, - })) as unknown as FilterType + } as FilterType)) as ColumnFilterType } else { - filters.value[+i] = await $api.dbTableFilter.create(view?.value?.id as string, { - ...filter, - fk_parent_id: parentId.value, - }) + filters.value[+i] = (await $api.dbTableFilter.create( + view?.value?.id as string, + { + ...filter, + fk_parent_id: parentId.value, + } as FilterType, + )) as ColumnFilterType } if (children) filters.value[+i].children = children - if (!isLink && !isWebhook) allFilters.value.push(filters.value[+i]) + if (!isLink && !isWebhook) allFilters.value.push(filters.value[+i] as FilterType) } } @@ -338,9 +343,9 @@ export function useViewFilters( } } - const saveOrUpdateDebounced = useCachedDebouncedFunction(saveOrUpdate, 500, (_filter: Filter, i: number) => i) + const saveOrUpdateDebounced = useCachedDebouncedFunction(saveOrUpdate, 500, (_filter: ColumnFilterType, i: number) => i) - async function saveOrUpdate(filter: Filter, i: number, force = false, undo = false, skipDataReload = false) { + async function saveOrUpdate(filter: ColumnFilterType, i: number, force = false, undo = false, skipDataReload = false) { // if already in progress the debounced function which will call this function again with 500ms delay until it's not saving if (savingStatus[i]) { return saveOrUpdateDebounced(filter, i, force, undo, skipDataReload) @@ -360,7 +365,7 @@ export function useViewFilters( fn: (prop: string, data: any) => { const f = filters.value[i] if (f) { - f[prop as keyof Filter] = data + f[prop as keyof ColumnFilterType] = data saveOrUpdate(f, i, force, true) } }, @@ -370,11 +375,11 @@ export function useViewFilters( fn: (prop: string, data: any) => { const f = filters.value[i] if (f) { - f[prop as keyof Filter] = data + f[prop as keyof ColumnFilterType] = data saveOrUpdate(f, i, force, true) } }, - args: [Object.keys(delta)[0], filter[Object.keys(delta)[0] as keyof Filter]], + args: [Object.keys(delta)[0], filter[Object.keys(delta)[0] as keyof ColumnFilterType]], }, scope: defineViewScope({ view: activeView.value }), }) @@ -402,13 +407,13 @@ export function useViewFilters( if (linkColId?.value) { const savedFilter = await $api.dbTableLinkFilter.create(linkColId.value, { ...filter, - fk_parent_id: parentId, + fk_parent_id: parentId.value, }) // extract id from saved filter and update the filter object // avoiding whole object update to prevent overwriting of current filter object changes filters.value[i] = { ...filters.value[i], - fk_parent_id: parentId, + fk_parent_id: parentId.value, id: savedFilter.id, status: undefined, } @@ -422,12 +427,12 @@ export function useViewFilters( // avoiding whole object update to prevent overwriting of current filter object changes filters.value[i] = { ...filters.value[i], - fk_parent_id: parentId, + fk_parent_id: parentId.value, id: savedFilter.id, status: undefined, } } - if (!isLink && !isWebhook) allFilters.value.push(filters.value[+i]) + if (!isLink && !isWebhook) allFilters.value.push(filters.value[+i] as FilterType) } } catch (e: any) { console.log(e) @@ -441,7 +446,7 @@ export function useViewFilters( if (!isWebhook && !skipDataReload && !isLink) reloadData?.() } - function deleteFilterGroupFromAllFilters(filter: Filter) { + function deleteFilterGroupFromAllFilters(filter: ColumnFilterType) { if (!isLink && !isWebhook) return // Find all child filters of the specified parentId @@ -458,11 +463,13 @@ export function useViewFilters( allFilters.value = allFilters.value.filter((f) => f.id !== filter.id && f.fk_parent_id !== filter.id) } - const deleteFilter = async (filter: Filter, i: number, undo = false) => { + const deleteFilter = async (filter: ColumnFilterType, i: number, undo = false) => { + // update the filter status + filter.status = 'delete' if (!undo && !filter.is_group) { addUndo({ undo: { - fn: async (fl: Filter) => { + fn: async (fl: ColumnFilterType) => { fl.status = 'create' filters.value.splice(i, 0, fl) await saveOrUpdate(fl, i, false, true) @@ -515,7 +522,9 @@ export function useViewFilters( } } const addFilter = async (undo = false, draftFilter: Partial = {}) => { - filters.value.push(draftFilter?.fk_column_id ? { ...placeholderFilter(), ...draftFilter } : placeholderFilter()) + filters.value.push( + (draftFilter?.fk_column_id ? { ...placeholderFilter(), ...draftFilter } : placeholderFilter()) as ColumnFilterType, + ) if (!undo) { addUndo({ undo: { @@ -526,7 +535,7 @@ export function useViewFilters( args: [filters.value.length - 1], }, redo: { - fn: async (i: number, fl: Filter) => { + fn: async (i: number, fl: ColumnFilterType) => { fl.status = 'create' filters.value.splice(i, 0, fl) await saveOrUpdate(fl, i, false, true) @@ -545,7 +554,7 @@ export function useViewFilters( const addFilterGroup = async () => { const child = placeholderFilter() - const placeHolderGroupFilter: Filter = placeholderGroupFilter() + const placeHolderGroupFilter: ColumnFilterType = placeholderGroupFilter() if (nestedMode.value) placeHolderGroupFilter.children = [child] @@ -577,28 +586,28 @@ export function useViewFilters( // method to extract looked up column meta for all bt lookup columns // it helps to decide the condition operations for the column const loadBtLookupTypes = async () => { - const btLookupTypes = {} + const btLookupTypes: Record = {} try { for (const col of meta.value?.columns || []) { if (col.uidt !== UITypes.Lookup) continue - let nextCol = col + let nextCol: ColumnType | undefined = col // check all the relation of nested lookup columns is bt or not // include the column only if all only if all relations are bt while (nextCol && nextCol.uidt === UITypes.Lookup) { // extract the relation column meta - const lookupRelation = (await getMeta(nextCol.fk_model_id))?.columns?.find( - (c) => c.id === (nextCol.colOptions as LookupType).fk_relation_column_id, + const lookupRelation = (await getMeta(nextCol.fk_model_id!))?.columns?.find( + (c) => c.id === (nextCol!.colOptions as LookupType).fk_relation_column_id, ) - const relatedTableMeta = await getMeta((lookupRelation.colOptions as LinkToAnotherRecordType).fk_related_model_id) - nextCol = relatedTableMeta?.columns?.find((c) => c.id === (nextCol.colOptions as LookupType).fk_lookup_column_id) + const relatedTableMeta = await getMeta((lookupRelation?.colOptions as LinkToAnotherRecordType).fk_related_model_id!) + nextCol = relatedTableMeta?.columns?.find((c) => c.id === (nextCol!.colOptions as LookupType).fk_lookup_column_id) // if next column is same as root lookup column then break the loop // since it's going to be a circular loop - if (nextCol.id === col.id) { + if (nextCol?.id === col.id) { break } } - btLookupTypes[col.id] = nextCol + btLookupTypes[col.id!] = nextCol } btLookupTypesMap.value = btLookupTypes } catch (e) { From 03017f31abb7112f1ebb7e17db8c829372a20c5a Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 14 Jun 2024 08:35:58 +0000 Subject: [PATCH 2/2] refactor: handle if lookuprelation not found --- packages/nc-gui/composables/useViewFilters.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/nc-gui/composables/useViewFilters.ts b/packages/nc-gui/composables/useViewFilters.ts index f4d3c302bb..5b834476d8 100644 --- a/packages/nc-gui/composables/useViewFilters.ts +++ b/packages/nc-gui/composables/useViewFilters.ts @@ -418,8 +418,7 @@ export function useViewFilters( status: undefined, } } else { - console.log(parentId.value) - const savedFilter = await $api.dbTableFilter.create(view.value.id!, { + const savedFilter = await $api.dbTableFilter.create(view.value!.id!, { ...filter, fk_parent_id: parentId.value, }) @@ -455,7 +454,7 @@ export function useViewFilters( // Recursively delete child filter of child filter childFilters.forEach((childFilter) => { if (childFilter.is_group) { - deleteFilterGroupFromAllFilters(childFilter) + deleteFilterGroupFromAllFilters(childFilter as ColumnFilterType) } }) @@ -598,6 +597,12 @@ export function useViewFilters( const lookupRelation = (await getMeta(nextCol.fk_model_id!))?.columns?.find( (c) => c.id === (nextCol!.colOptions as LookupType).fk_relation_column_id, ) + + // this is less likely to happen but if relation column is not found then break the loop + if (!lookupRelation) { + break + } + const relatedTableMeta = await getMeta((lookupRelation?.colOptions as LinkToAnotherRecordType).fk_related_model_id!) nextCol = relatedTableMeta?.columns?.find((c) => c.id === (nextCol!.colOptions as LookupType).fk_lookup_column_id)