Browse Source

fix: group by reload

pull/8354/head
mertmit 7 months ago
parent
commit
1a1d81a792
  1. 23
      packages/nc-gui/components/smartsheet/grid/GroupBy.vue
  2. 161
      packages/nc-gui/composables/useViewGroupBy.ts

23
packages/nc-gui/components/smartsheet/grid/GroupBy.vue

@ -108,16 +108,21 @@ onBeforeUnmount(async () => {
reloadViewDataHook?.off(reloadViewDataHandler) reloadViewDataHook?.off(reloadViewDataHandler)
}) })
watch( watch([() => vGroup.value.key], async (n, o) => {
[() => vGroup.value.key], if (n !== o) {
async (n, o) => { if (!vGroup.value.nested) {
if (n !== o) { await _loadGroupData(vGroup.value, true)
if (!vGroup.value.nested) { } else if (vGroup.value.nested) {
await _loadGroupData(vGroup.value, true) await props.loadGroups({}, vGroup.value)
}
} }
}, }
) })
onMounted(async () => {
if (vGroup.value.root === true) {
await props.loadGroups({}, vGroup.value)
}
})
if (vGroup.value.root === true) provide(ScrollParentInj, wrapper) if (vGroup.value.root === true) provide(ScrollParentInj, wrapper)

161
packages/nc-gui/composables/useViewGroupBy.ts

@ -27,19 +27,19 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
const groupByLimit: number = 3 const groupByLimit: number = 3
const { api } = useApi() const { api } = useApi()
const { appInfo } = useGlobal() const { appInfo } = useGlobal()
const { base } = storeToRefs(useBase()) const { base } = storeToRefs(useBase())
const { sharedView, fetchSharedViewData } = useSharedView() const { sharedView, fetchSharedViewData } = useSharedView()
const { gridViewCols } = useViewColumnsOrThrow() const { gridViewCols } = useViewColumnsOrThrow()
const { getMeta } = useMetas() const { getMeta } = useMetas()
const sharedViewPassword = inject(SharedViewPasswordInj, ref(null)) const sharedViewPassword = inject(SharedViewPasswordInj, ref(null))
const groupBy = computed<{ column: ColumnType; sort: string; order?: number }[]>(() => { const groupBy = computed<{ column: ColumnType; sort: string; order?: number }[]>(() => {
const tempGroupBy: { column: ColumnType; sort: string; order?: number }[] = [] const tempGroupBy: { column: ColumnType; sort: string; order?: number }[] = []
Object.values(gridViewCols.value).forEach((col) => { Object.values(gridViewCols.value).forEach((col) => {
@ -57,37 +57,37 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
tempGroupBy.sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity)) tempGroupBy.sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity))
return tempGroupBy return tempGroupBy
}) })
const isGroupBy = computed(() => !!groupBy.value.length) const isGroupBy = computed(() => !!groupBy.value.length)
const { isUIAllowed } = useRoles() const { isUIAllowed } = useRoles()
const { sorts, nestedFilters } = useSmartsheetStoreOrThrow() const { sorts, nestedFilters } = useSmartsheetStoreOrThrow()
const reloadViewDataHook = inject(ReloadViewDataHookInj, createEventHook()) const reloadViewDataHook = inject(ReloadViewDataHookInj, createEventHook())
const groupByGroupLimit = computed(() => { const groupByGroupLimit = computed(() => {
return appInfo.value.defaultGroupByLimit?.limitGroup || 10 return appInfo.value.defaultGroupByLimit?.limitGroup || 10
}) })
const groupByRecordLimit = computed(() => { const groupByRecordLimit = computed(() => {
return appInfo.value.defaultGroupByLimit?.limitRecord || 10 return appInfo.value.defaultGroupByLimit?.limitRecord || 10
}) })
const supportedLookups = ref<string[]>([]) const supportedLookups = ref<string[]>([])
const fieldsToGroupBy = computed(() => const fieldsToGroupBy = computed(() =>
(meta?.value?.columns || []).filter((field) => { (meta?.value?.columns || []).filter((field) => {
if (excludedGroupingUidt.includes(field.uidt as UITypes)) return false if (excludedGroupingUidt.includes(field.uidt as UITypes)) return false
if (field.uidt === UITypes.Lookup) { if (field.uidt === UITypes.Lookup) {
return field.id && supportedLookups.value.includes(field.id) return field.id && supportedLookups.value.includes(field.id)
} }
return true return true
}), }),
) )
const rootGroup = ref<Group>({ const rootGroup = ref<Group>({
key: 'root', key: 'root',
color: 'root', color: 'root',
@ -99,12 +99,12 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
children: [], children: [],
root: true, root: true,
}) })
async function groupWrapperChangePage(page: number, groupWrapper?: Group) { async function groupWrapperChangePage(page: number, groupWrapper?: Group) {
groupWrapper = groupWrapper || rootGroup.value groupWrapper = groupWrapper || rootGroup.value
if (!groupWrapper) return if (!groupWrapper) return
groupWrapper.paginationData.page = page groupWrapper.paginationData.page = page
await loadGroups( await loadGroups(
{ {
@ -113,37 +113,37 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
groupWrapper, groupWrapper,
) )
} }
const formatData = (list: Record<string, any>[]) => const formatData = (list: Record<string, any>[]) =>
list.map((row) => ({ list.map((row) => ({
row: { ...row }, row: { ...row },
oldRow: { ...row }, oldRow: { ...row },
rowMeta: {}, rowMeta: {},
})) }))
const valueToTitle = (value: string, col: ColumnType) => { const valueToTitle = (value: string, col: ColumnType) => {
if (col.uidt === UITypes.Checkbox) { if (col.uidt === UITypes.Checkbox) {
return value ? GROUP_BY_VARS.TRUE : GROUP_BY_VARS.FALSE return value ? GROUP_BY_VARS.TRUE : GROUP_BY_VARS.FALSE
} }
if ([UITypes.User, UITypes.CreatedBy, UITypes.LastModifiedBy].includes(col.uidt as UITypes)) { if ([UITypes.User, UITypes.CreatedBy, UITypes.LastModifiedBy].includes(col.uidt as UITypes)) {
if (!value) { if (!value) {
return GROUP_BY_VARS.NULL return GROUP_BY_VARS.NULL
} }
} }
// convert to JSON string if non-string value // convert to JSON string if non-string value
if (value && typeof value === 'object') { if (value && typeof value === 'object') {
value = JSON.stringify(value) value = JSON.stringify(value)
} }
return value ?? GROUP_BY_VARS.NULL return value ?? GROUP_BY_VARS.NULL
} }
const colors = ref(enumColor.light) const colors = ref(enumColor.light)
const nextGroupColor = ref(colors.value[0]) const nextGroupColor = ref(colors.value[0])
const getNextColor = () => { const getNextColor = () => {
const tempColor = nextGroupColor.value const tempColor = nextGroupColor.value
const index = colors.value.indexOf(nextGroupColor.value) const index = colors.value.indexOf(nextGroupColor.value)
@ -154,7 +154,7 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
} }
return tempColor return tempColor
} }
const findKeyColor = (key?: string, col?: ColumnType): string => { const findKeyColor = (key?: string, col?: ColumnType): string => {
if (col) { if (col) {
switch (col.uidt) { switch (col.uidt) {
@ -188,7 +188,7 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
} }
return key ? getNextColor() : 'gray' return key ? getNextColor() : 'gray'
} }
const calculateNestedWhere = (nestedIn: GroupNestedIn[], existing = '') => { const calculateNestedWhere = (nestedIn: GroupNestedIn[], existing = '') => {
return nestedIn.reduce((acc, curr) => { return nestedIn.reduce((acc, curr) => {
if (curr.key === GROUP_BY_VARS.NULL) { if (curr.key === GROUP_BY_VARS.NULL) {
@ -214,30 +214,30 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
return acc return acc
}, existing) }, existing)
} }
async function loadGroups(params: any = {}, group?: Group) { async function loadGroups(params: any = {}, group?: Group) {
try { try {
group = group || rootGroup.value group = group || rootGroup.value
if (!base?.value?.id || !view.value?.id || !view.value?.fk_model_id || !group) return if (!base?.value?.id || !view.value?.id || !view.value?.fk_model_id || !group) return
if (groupBy.value.length === 0) { if (groupBy.value.length === 0) {
group.children = [] group.children = []
return return
} }
if (group.nestedIn.length > groupBy.value.length) return if (group.nestedIn.length > groupBy.value.length) return
if (group.nestedIn.length === 0) nextGroupColor.value = colors.value[0] if (group.nestedIn.length === 0) nextGroupColor.value = colors.value[0]
const groupby = groupBy.value[group.nestedIn.length] const groupby = groupBy.value[group.nestedIn.length]
const nestedWhere = calculateNestedWhere(group.nestedIn, where?.value) const nestedWhere = calculateNestedWhere(group.nestedIn, where?.value)
if (!groupby || !groupby.column.title) return if (!groupby || !groupby.column.title) return
if (isPublic && !sharedView.value?.uuid) { if (isPublic && !sharedView.value?.uuid) {
return return
} }
const response = !isPublic const response = !isPublic
? await api.dbViewRow.groupBy('noco', base.value.id, view.value.fk_model_id, view.value.id, { ? await api.dbViewRow.groupBy('noco', base.value.id, view.value.fk_model_id, view.value.id, {
offset: ((group.paginationData.page ?? 0) - 1) * (group.paginationData.pageSize ?? groupByGroupLimit.value), offset: ((group.paginationData.page ?? 0) - 1) * (group.paginationData.pageSize ?? groupByGroupLimit.value),
@ -267,7 +267,7 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
}, },
}, },
) )
const tempList: Group[] = response.list.reduce((acc: Group[], curr: Record<string, any>) => { const tempList: Group[] = response.list.reduce((acc: Group[], curr: Record<string, any>) => {
const keyExists = acc.find( const keyExists = acc.find(
(a) => a.key === valueToTitle(curr[groupby.column.column_name!] ?? curr[groupby.column.title!], groupby.column), (a) => a.key === valueToTitle(curr[groupby.column.column_name!] ?? curr[groupby.column.title!], groupby.column),
@ -302,9 +302,9 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
} }
return acc return acc
}, []) }, [])
if (!group.children) group.children = [] if (!group.children) group.children = []
for (const temp of tempList) { for (const temp of tempList) {
const keyExists = group.children?.find((a) => a.key === temp.key) const keyExists = group.children?.find((a) => a.key === temp.key)
if (keyExists) { if (keyExists) {
@ -320,10 +320,10 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
} }
group.children.push(temp) group.children.push(temp)
} }
// clear rest of the children // clear rest of the children
group.children = group.children.filter((c) => tempList.find((t) => t.key === c.key)) group.children = group.children.filter((c) => tempList.find((t) => t.key === c.key))
if (group.count <= (group.paginationData.pageSize ?? groupByGroupLimit.value)) { if (group.count <= (group.paginationData.pageSize ?? groupByGroupLimit.value)) {
group.children.sort((a, b) => { group.children.sort((a, b) => {
const orderA = tempList.findIndex((t) => t.key === a.key) const orderA = tempList.findIndex((t) => t.key === a.key)
@ -331,9 +331,9 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
return orderA - orderB return orderA - orderB
}) })
} }
group.paginationData = response.pageInfo group.paginationData = response.pageInfo
// to cater the case like when querying with a non-zero offset // to cater the case like when querying with a non-zero offset
// the result page may point to the target page where the actual returned data don't display on // the result page may point to the target page where the actual returned data don't display on
const expectedPage = Math.max(1, Math.ceil(group.paginationData.totalRows! / group.paginationData.pageSize!)) const expectedPage = Math.max(1, Math.ceil(group.paginationData.totalRows! / group.paginationData.pageSize!))
@ -344,25 +344,25 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
message.error(await extractSdkResponseErrorMsg(e)) message.error(await extractSdkResponseErrorMsg(e))
} }
} }
async function loadGroupData(group: Group, force = false, params: any = {}) { async function loadGroupData(group: Group, force = false, params: any = {}) {
try { try {
if (!base?.value?.id || !view.value?.id || !view.value?.fk_model_id) return if (!base?.value?.id || !view.value?.id || !view.value?.fk_model_id) return
if (group.children && !force) return if (group.children && !force) return
if (!group.paginationData) { if (!group.paginationData) {
group.paginationData = { page: 1, pageSize: groupByRecordLimit.value } group.paginationData = { page: 1, pageSize: groupByRecordLimit.value }
} }
const nestedWhere = calculateNestedWhere(group.nestedIn, where?.value) const nestedWhere = calculateNestedWhere(group.nestedIn, where?.value)
const query = { const query = {
offset: ((group.paginationData.page ?? 0) - 1) * (group.paginationData.pageSize ?? groupByRecordLimit.value), offset: ((group.paginationData.page ?? 0) - 1) * (group.paginationData.pageSize ?? groupByRecordLimit.value),
limit: group.paginationData.pageSize ?? groupByRecordLimit.value, limit: group.paginationData.pageSize ?? groupByRecordLimit.value,
where: `${nestedWhere}`, where: `${nestedWhere}`,
} }
const response = !isPublic const response = !isPublic
? await api.dbViewRow.list('noco', base.value.id, view.value.fk_model_id, view.value.id, { ? await api.dbViewRow.list('noco', base.value.id, view.value.fk_model_id, view.value.id, {
...query, ...query,
@ -371,7 +371,7 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
...(isUIAllowed('filterSync') ? {} : { filterArrJson: JSON.stringify(nestedFilters.value) }), ...(isUIAllowed('filterSync') ? {} : { filterArrJson: JSON.stringify(nestedFilters.value) }),
} as any) } as any)
: await fetchSharedViewData({ sortsArr: sorts.value, filtersArr: nestedFilters.value, ...query }) : await fetchSharedViewData({ sortsArr: sorts.value, filtersArr: nestedFilters.value, ...query })
group.count = response.pageInfo.totalRows ?? 0 group.count = response.pageInfo.totalRows ?? 0
group.rows = formatData(response.list) group.rows = formatData(response.list)
group.paginationData = response.pageInfo group.paginationData = response.pageInfo
@ -379,7 +379,7 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
message.error(await extractSdkResponseErrorMsg(e)) message.error(await extractSdkResponseErrorMsg(e))
} }
} }
const loadGroupPage = async (group: Group, p: number) => { const loadGroupPage = async (group: Group, p: number) => {
if (!group.paginationData) { if (!group.paginationData) {
group.paginationData = { page: 1, pageSize: groupByRecordLimit.value } group.paginationData = { page: 1, pageSize: groupByRecordLimit.value }
@ -387,17 +387,17 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
group.paginationData.page = p group.paginationData.page = p
await loadGroupData(group, true) await loadGroupData(group, true)
} }
const refreshNested = (group?: Group, nestLevel = 0) => { const refreshNested = (group?: Group, nestLevel = 0) => {
group = group || rootGroup.value group = group || rootGroup.value
if (!group) return if (!group) return
if (nestLevel < groupBy.value.length) { if (nestLevel < groupBy.value.length) {
group.nested = true group.nested = true
} else { } else {
group.nested = false group.nested = false
} }
if (group.nested) { if (group.nested) {
if (group?.rows) { if (group?.rows) {
group.rows = [] group.rows = []
@ -407,28 +407,26 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
group.children = [] group.children = []
} }
} }
if (nestLevel > groupBy.value.length) return if (nestLevel > groupBy.value.length) return
for (const child of group.children || []) { for (const child of group.children || []) {
refreshNested(child, nestLevel + 1) refreshNested(child, nestLevel + 1)
} }
} }
watch( watch(
() => groupBy.value.length, () => groupBy.value.length,
async () => { async () => {
if (groupBy.value.length > 0) { if (groupBy.value.length > 0) {
rootGroup.value.paginationData = { page: 1, pageSize: groupByGroupLimit.value } rootGroup.value.paginationData = { page: 1, pageSize: groupByGroupLimit.value }
rootGroup.value.column = {} as any rootGroup.value.column = {} as any
await loadGroups()
refreshNested() refreshNested()
nextTick(() => reloadViewDataHook?.trigger()) nextTick(() => reloadViewDataHook?.trigger())
} }
}, },
{ immediate: true },
) )
const findGroupByNestedIn = (nestedIn: GroupNestedIn[], group?: Group, nestLevel = 0): Group => { const findGroupByNestedIn = (nestedIn: GroupNestedIn[], group?: Group, nestLevel = 0): Group => {
group = group || rootGroup.value group = group || rootGroup.value
if (nestLevel >= nestedIn.length) return group if (nestLevel >= nestedIn.length) return group
@ -441,12 +439,12 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
} }
return group return group
} }
const parentGroup = (group: Group) => { const parentGroup = (group: Group) => {
const parent = findGroupByNestedIn(group.nestedIn.slice(0, -1)) const parent = findGroupByNestedIn(group.nestedIn.slice(0, -1))
return parent return parent
} }
const modifyCount = (group: Group, countEffect: number) => { const modifyCount = (group: Group, countEffect: number) => {
if (!group) return if (!group) return
group.count += countEffect group.count += countEffect
@ -460,7 +458,7 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
if (group.root) return if (group.root) return
modifyCount(parentGroup(group), countEffect) modifyCount(parentGroup(group), countEffect)
} }
const findGroupForRow = (row: Row, group?: Group, nestLevel = 0): { found: boolean; group: Group } => { const findGroupForRow = (row: Row, group?: Group, nestLevel = 0): { found: boolean; group: Group } => {
group = group || rootGroup.value group = group || rootGroup.value
if (group.nested) { if (group.nested) {
@ -475,7 +473,7 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
} }
return { found: true, group } return { found: true, group }
} }
const redistributeRows = (group?: Group) => { const redistributeRows = (group?: Group) => {
group = group || rootGroup.value group = group || rootGroup.value
if (!group) return if (!group) return
@ -505,52 +503,51 @@ const [useProvideViewGroupBy, useViewGroupBy] = useInjectionState(
group.children?.forEach((g) => redistributeRows(g)) group.children?.forEach((g) => redistributeRows(g))
} }
} }
const loadAllowedLookups = async () => { const loadAllowedLookups = async () => {
const filteredLookupCols = [] const filteredLookupCols = []
try { try {
for (const col of meta?.value?.columns || []) { for (const col of meta?.value?.columns || []) {
if (col.uidt !== UITypes.Lookup) continue if (col.uidt !== UITypes.Lookup) continue
let nextCol: ColumnType = col let nextCol: ColumnType = col
// check the lookup column is supported type or not // check the lookup column is supported type or not
while (nextCol && nextCol.uidt === UITypes.Lookup) { while (nextCol && nextCol.uidt === UITypes.Lookup) {
const lookupRelation = (await getMeta(nextCol.fk_model_id as string))?.columns?.find( const lookupRelation = (await getMeta(nextCol.fk_model_id as string))?.columns?.find(
(c) => c.id === (nextCol?.colOptions as LookupType).fk_relation_column_id, (c) => c.id === (nextCol?.colOptions as LookupType).fk_relation_column_id,
) )
const relatedTableMeta = await getMeta( const relatedTableMeta = await getMeta(
(lookupRelation?.colOptions as LinkToAnotherRecordType).fk_related_model_id as string, (lookupRelation?.colOptions as LinkToAnotherRecordType).fk_related_model_id as string,
) )
nextCol = relatedTableMeta?.columns?.find( nextCol = relatedTableMeta?.columns?.find(
(c) => c.id === ((nextCol?.colOptions as LookupType).fk_lookup_column_id as string), (c) => c.id === ((nextCol?.colOptions as LookupType).fk_lookup_column_id as string),
) as ColumnType ) as ColumnType
// if next column is same as root lookup column then break the loop // if next column is same as root lookup column then break the loop
// since it's going to be a circular loop, and ignore the column // since it's going to be a circular loop, and ignore the column
if (nextCol?.id === col.id) { if (nextCol?.id === col.id) {
break break
} }
} }
if (nextCol?.uidt !== UITypes.Attachment && col.id) filteredLookupCols.push(col.id) if (nextCol?.uidt !== UITypes.Attachment && col.id) filteredLookupCols.push(col.id)
} }
supportedLookups.value = filteredLookupCols supportedLookups.value = filteredLookupCols
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
} }
onMounted(async () => { watch([() => view?.value?.id, () => meta.value?.columns], async ([newViewId]) => {
await loadAllowedLookups() // reload only if view belongs to current table
}) if (newViewId && view.value?.fk_model_id === meta.value?.id) {
await loadAllowedLookups()
watch(meta, async () => { }
await loadAllowedLookups()
}) })
return { return {
rootGroup, rootGroup,
groupBy, groupBy,

Loading…
Cancel
Save