import type { ColumnType, LinkToAnotherRecordType, PaginatedType, TableType } from 'nocodb-sdk' import type { ComputedRef, Ref } from 'vue' import { Modal, notification } from 'ant-design-vue' import { useInjectionState, useMetas, useProject } from '#imports' import { NOCO } from '~/lib' import type { Row } from '~/composables' import { extractSdkResponseErrorMsg } from '~/utils' interface DataApiResponse { list: Record pageInfo: PaginatedType } /** Store for managing Link to another cells */ const [useProvideLTARStore, useLTARStore] = useInjectionState( (column: Ref>, row?: Ref, isNewRow: ComputedRef | Ref, reloadData = () => {}) => { // state const { metas, getMeta } = useMetas() const { project } = useProject() const { $api } = useNuxtApp() const childrenExcludedList: Ref = ref() const childrenList: Ref = ref() const childrenExcludedListPagination = reactive({ page: 1, query: '', size: 10, }) const childrenListPagination = reactive({ page: 1, query: '', size: 10, }) const colOptions = $computed(() => column?.value.colOptions as LinkToAnotherRecordType) // getters const meta = computed(() => metas?.value?.[column?.value?.fk_model_id as string]) const relatedTableMeta = computed(() => { return metas.value?.[colOptions?.fk_related_model_id as string] }) const rowId = computed(() => meta.value.columns .filter((c: Required) => c.pk) .map((c: Required) => row?.value?.row?.[c.title]) .join('___'), ) // actions const getRelatedTableRowId = (row: Record) => { return relatedTableMeta.value?.columns ?.filter((c) => c.pk) .map((c) => row?.[c.title as string]) .join('___') } const loadRelatedTableMeta = async () => { await getMeta(colOptions?.fk_related_model_id as string) } const relatedTablePrimaryValueProp = computed(() => { return (relatedTableMeta?.value?.columns?.find((c) => c.pv) || relatedTableMeta?.value?.columns?.[0])?.title }) const relatedTablePrimaryKeyProps = computed(() => { return relatedTableMeta?.value?.columns?.filter((c) => c.pk)?.map((c) => c.title) ?? [] }) const primaryValueProp = computed(() => { return (meta?.value?.columns?.find((c: Required) => c.pv) || relatedTableMeta?.value?.columns?.[0])?.title }) const loadChildrenExcludedList = async () => { try { /** if new row load all records */ if (isNewRow?.value) { childrenExcludedList.value = await $api.dbTableRow.list( NOCO, project.value.id as string, relatedTableMeta?.value?.id as string, { limit: childrenExcludedListPagination.size, offset: childrenExcludedListPagination.size * (childrenExcludedListPagination.page - 1), where: childrenExcludedListPagination.query && `(${relatedTablePrimaryValueProp.value},like,${childrenExcludedListPagination.query})`, fields: [relatedTablePrimaryValueProp.value, ...relatedTablePrimaryKeyProps.value], } as any, ) } else { childrenExcludedList.value = await $api.dbTableRow.nestedChildrenExcludedList( NOCO, project.value.id as string, meta.value.id, rowId.value, colOptions.type as 'mm' | 'hm', column?.value?.title, { limit: childrenExcludedListPagination.size, offset: childrenExcludedListPagination.size * (childrenExcludedListPagination.page - 1), where: childrenExcludedListPagination.query && `(${relatedTablePrimaryValueProp.value},like,${childrenExcludedListPagination.query})`, }, ) } } catch (e: any) { notification.error({ message: 'Failed to load list', description: await extractSdkResponseErrorMsg(e), }) } } const loadChildrenList = async () => { try { if (colOptions.type === 'bt') return childrenList.value = await $api.dbTableRow.nestedList( NOCO, project.value.id as string, meta.value.id, rowId.value, colOptions.type as 'mm' | 'hm', column?.value?.title, { limit: childrenListPagination.size, offset: childrenListPagination.size * (childrenListPagination.page - 1), where: childrenListPagination.query && `(${relatedTablePrimaryValueProp.value},like,${childrenListPagination.query})`, }, ) } catch (e: any) { notification.error({ message: 'Failed to load children list', description: await extractSdkResponseErrorMsg(e), }) } } const deleteRelatedRow = async (row: Record) => { Modal.confirm({ title: 'Do you want to delete the record?', type: 'warning', onOk: async () => { const id = getRelatedTableRowId(row) try { $api.dbTableRow.delete(NOCO, project.value.id as string, relatedTableMeta.value.id as string, id as string) reloadData?.() await loadChildrenList() } catch (e: any) { notification.error({ message: 'Delete failed', description: await extractSdkResponseErrorMsg(e), }) } }, }) } const unlink = async (row: Record) => { // const column = meta.columns.find(c => c.id === this.column.colOptions.fk_child_column_id); // todo: handle if new record // if (this.isNew) { // this.$emit('updateCol', this.row, _cn, null); // this.localState = null; // this.$emit('update:localState', this.localState); // return; // } // todo: handle bt column if required // if (column.rqd) { // this.$toast.info('Unlink is not possible, instead map to another parent.').goAway(3000); // return; // } try { // todo: audit await $api.dbTableRow.nestedRemove( NOCO, project.value.title as string, meta.value.title, rowId.value, colOptions.type as 'mm' | 'hm', column?.value?.title, getRelatedTableRowId(row) as string, ) } catch (e: any) { notification.error({ message: 'Unlink failed', description: await extractSdkResponseErrorMsg(e), }) } reloadData?.() // todo: reload table data and children list // this.$emit('loadTableData'); // if (this.isForm && this.$refs.childList) { // this.$refs.childList.loadData(); // } } const link = async (row: Record) => { // todo: handle new record // const pid = this._extractRowId(parent, this.parentMeta); // const id = this._extractRowId(this.row, this.meta); // const _cn = this.meta.columns.find(c => c.id === this.column.colOptions.fk_child_column_id).title; // // if (this.isNew) { // const _rcn = this.parentMeta.columns.find(c => c.id === this.column.colOptions.fk_parent_column_id).title; // this.localState = parent; // this.$emit('update:localState', this.localState); // this.$emit('updateCol', this.row, _cn, parent[_rcn]); // this.newRecordModal = false; // return; // } try { await $api.dbTableRow.nestedAdd( NOCO, project.value.title as string, meta.value.title as string, rowId.value, colOptions.type as 'mm' | 'hm', column?.value?.title, getRelatedTableRowId(row) as string, ) await loadChildrenList() } catch (e: any) { notification.error({ message: 'Linking failed', description: await extractSdkResponseErrorMsg(e), }) } // todo: reload table data and child list // this.pid = pid; // // this.newRecordModal = false; // // this.$emit('loadTableData'); // if (this.isForm && this.$refs.childList) { // this.$refs.childList.loadData(); // } reloadData?.() } // watchers watch(childrenExcludedListPagination, async () => { await loadChildrenExcludedList() }) watch(childrenListPagination, async () => { await loadChildrenList() }) return { relatedTableMeta, loadRelatedTableMeta, relatedTablePrimaryValueProp, childrenExcludedList, childrenList, rowId, childrenExcludedListPagination, childrenListPagination, primaryValueProp, meta, unlink, link, loadChildrenExcludedList, loadChildrenList, row, deleteRelatedRow, getRelatedTableRowId, } }, 'ltar-store', ) export { useProvideLTARStore } export function useLTARStoreOrThrow() { const ltarStore = useLTARStore() if (ltarStore == null) throw new Error('Please call `useLTARStore` on the appropriate parent component') return ltarStore }