Something went wrong. Try again.
🔥 🔥 🔥 A Free & Self-hostable Airtable Alternative nocodb.com
Something went wrong. Try again.
6.7 kB · 214 lines
TypeScript
at develop
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215import type { ComputedRef, Ref } from 'vue'import type { ColumnType, MapType, PaginatedType, TableType, ViewType } from 'nocodb-sdk'
const formatData = (list: Record<string, any>[]) => list.map( (row) => ({ row: { ...row }, oldRow: { ...row }, rowMeta: {}, } as Row), )
const [useProvideMapViewStore, useMapViewStore] = useInjectionState( ( meta: Ref<(MapType & { id: string }) | undefined>, viewMeta: Ref<(ViewType | MapType | undefined) & { id: string }> | ComputedRef<(ViewType & { id: string }) | undefined>, shared = false, where?: ComputedRef<string | undefined>, ) => { if (!meta) { throw new Error('Table meta is not available') }
const defaultPageSize = 1000
const formattedData = ref<Row[]>([])
const { t } = useI18n()
const { api } = useApi()
const { base } = storeToRefs(useBase())
const { $api } = useNuxtApp()
const { internalGet } = useInternalBatch()
const { isUIAllowed } = useRoles()
const isPublic = ref(shared) || inject(IsPublicInj, ref(false))
const { sorts, nestedFilters } = useSmartsheetStoreOrThrow()
const { sharedView, fetchSharedViewData } = useSharedView()
const mapMetaData = ref<MapType>({})
const geoDataFieldColumn = ref<ColumnType | undefined>()
const paginationData = ref<PaginatedType>({ page: 1, pageSize: defaultPageSize })
const queryParams = computed(() => ({ limit: paginationData.value.pageSize ?? defaultPageSize, where: where?.value ?? '', }))
async function syncCount() { const { count } = await $api.dbViewRow.count( NOCO, meta.value?.base_id ?? (base?.value?.title as string), meta?.value?.id as string, viewMeta?.value?.id as string, ) paginationData.value.totalRows = count }
async function loadMapMeta() { if (!viewMeta?.value?.id || !meta?.value?.columns) return mapMetaData.value = isPublic.value ? (sharedView.value?.view as MapType) : await internalGet(viewMeta.value.fk_workspace_id!, viewMeta.value.base_id!, { operation: 'mapViewGet', mapViewId: viewMeta.value.id, })
geoDataFieldColumn.value = (meta.value.columns as ColumnType[]).filter((f) => f.id === mapMetaData.value.fk_geo_data_col_id)[0] || {} }
async function loadMapData() { if ((!base?.value?.id || !meta.value?.id || !viewMeta.value?.id) && !isPublic?.value) return
const res = !isPublic.value ? await api.dbViewRow.list('noco', base.value.id!, meta.value!.id!, viewMeta.value!.id!, { ...queryParams.value, ...(isUIAllowed('filterSync') ? {} : { filterArrJson: JSON.stringify(nestedFilters.value) }), where: where?.value, }) : await fetchSharedViewData({ sortsArr: sorts.value, filtersArr: nestedFilters.value })
formattedData.value = formatData(res!.list) }
async function updateMapMeta(updateObj: Partial<MapType>) { if (!viewMeta?.value?.id || !isUIAllowed('dataEdit', { skipSourceCheck: true })) return await $api.internal.postOperation( viewMeta.value.fk_workspace_id!, viewMeta.value.base_id!, { operation: 'mapViewUpdate', viewId: viewMeta.value.id, }, updateObj, ) }
const { getMeta } = useMetas()
async function insertRow( currentRow: Row, ltarState: Record<string, any> = {}, { metaValue = meta.value, viewMetaValue = viewMeta.value, }: { metaValue?: MapType & { id: string }; viewMetaValue?: (ViewType | MapType) & { id: string } } = {}, ) { const row = currentRow.row if (currentRow.rowMeta) currentRow.rowMeta.saving = true try { // Table meta can be transiently undefined (e.g. cleared during navigation/teardown // while a deferred save fires) — bail gracefully instead of firing a doomed create. if (!(metaValue as TableType)?.columns) { return }
const { missingRequiredColumns, insertObj } = await populateInsertObject({ meta: metaValue as TableType, ltarState, getMeta, row, })
if (missingRequiredColumns.size) { const missingFields = [...missingRequiredColumns].filter((f): f is string => typeof f === 'string') if (currentRow.rowMeta) { currentRow.rowMeta.saveError = { reason: 'missingRequired', missingFields, } } // Map view has no inline ⚠️ marker (the canvas-only saveError // glyph lives in useCanvasRender), so the toast is the only // user-visible feedback. This path is one-shot per submit; if // it ever gets wired into a retry loop, de-dup against the // previous saveError to avoid the spam useInfiniteData was // refactored around. const fieldList = missingFields.join(', ') message.error( missingFields.length === 1 ? t('msg.error.requiredFieldMissing', { fields: fieldList }) : t('msg.error.requiredFieldsMissing', { fields: fieldList }), ) return }
const insertedData = await $api.dbViewRow.create( NOCO, metaValue?.base_id ?? (base?.value.id as string), metaValue?.id as string, viewMetaValue?.id as string, insertObj, )
Object.assign(currentRow, { row: { ...insertedData, ...row }, rowMeta: { ...(currentRow.rowMeta || {}), new: undefined }, oldRow: { ...insertedData }, })
syncCount()
return insertedData } catch (error: any) { message.error(await extractSdkResponseErrorMsg(error)) } finally { if (currentRow.rowMeta) currentRow.rowMeta.saving = false } }
function addEmptyRow(addAfter = formattedData.value.length) { formattedData.value.splice(addAfter, 0, { row: {}, oldRow: {}, rowMeta: { new: true }, })
return formattedData.value[addAfter] }
return { formattedData, loadMapData, loadMapMeta, updateMapMeta, mapMetaData, geoDataFieldColumn, addEmptyRow, insertRow, syncCount, paginationData, } },)
export { useProvideMapViewStore }
export function useMapViewStoreOrThrow() { const mapViewStore = useMapViewStore()
if (mapViewStore == null) throw new Error('Please call `useProvideMapViewStore` on the appropriate parent component')
return mapViewStore}