Browse Source

feat(guiv2): save record(new) with LTAR cell using expanded form

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/3025/head
Pranav C 2 years ago
parent
commit
6ac0bf8d39
  1. 15
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  2. 15
      packages/nc-gui-v2/components/smartsheet/Row.vue
  3. 16
      packages/nc-gui-v2/components/smartsheet/expanded-form/Header.vue
  4. 18
      packages/nc-gui-v2/components/smartsheet/expanded-form/index.vue
  5. 2
      packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue
  6. 5
      packages/nc-gui-v2/composables/useExpandedFormStore.ts
  7. 72
      packages/nc-gui-v2/composables/useSmartsheetRowStore.ts

15
packages/nc-gui-v2/components/smartsheet/Grid.vue

@ -60,6 +60,7 @@ const contextMenu = ref(false)
const contextMenuTarget = ref(false)
const expandedFormDlg = ref(false)
const expandedFormRow = ref<Row>()
const expandedFormRowState = ref<Record<string, any>>()
const visibleColLength = $computed(() => fields.value?.length)
@ -274,8 +275,9 @@ const onNavigate = (dir: NavigateDir) => {
}
}
const expandForm = (row: Row) => {
const expandForm = (row: Row, state: Record<string, any>) => {
expandedFormRow.value = row
expandedFormRowState.value = state
expandedFormDlg.value = true
}
</script>
@ -330,6 +332,7 @@ const expandForm = (row: Row) => {
</thead>
<tbody>
<SmartsheetRow v-for="(row, rowIndex) of data" :key="rowIndex" :row="row">
<template #default="{ state }">
<tr class="nc-grid-row">
<td key="row-index" class="caption nc-grid-cell group">
<div class="flex items-center w-[80px]">
@ -343,7 +346,7 @@ const expandForm = (row: Row) => {
<div class="cursor-pointer flex items-center border-1 active:ring rounded p-1 hover:bg-primary/10">
<MdiArrowExpand
class="select-none transform hover:(text-pink-500 scale-120)"
@click="expandedFormDlg = true"
@click="expandForm(row, state)"
/>
</div>
</div>
@ -389,6 +392,7 @@ const expandForm = (row: Row) => {
</div>
</td>
</tr>
</template>
</SmartsheetRow>
<tr v-if="!isLocked">
@ -427,7 +431,12 @@ const expandForm = (row: Row) => {
</div>
<SmartsheetPagination />
<SmartsheetExpandedForm v-if="expandedFormRow" v-model="expandedFormDlg" :row="expandedFormRow" />
<SmartsheetExpandedForm
v-if="expandedFormRow"
v-model="expandedFormDlg"
:row="expandedFormRow"
:state="expandedFormRowState"
/>
</div>
</template>

15
packages/nc-gui-v2/components/smartsheet/Row.vue

@ -12,12 +12,19 @@ const emit = defineEmits(['expandForm', 'selectCell', 'updateOrSaveRow', 'naviga
const row = toRef(props, 'row')
const { meta } = useSmartsheetStoreOrThrow()
const { isNew, state } = useProvideSmartsheetRowStore(meta, row)
watch(row, () => {
state.value = {}
const { isNew, state, syncLTARRefs } = useProvideSmartsheetRowStore(meta, row)
// on changing isNew(new record insert) status sync LTAR cell values
watch(isNew, async (nextVal, prevVal) => {
if (prevVal && !nextVal) {
await syncLTARRefs(row.value.row)
// update row values without invoking api
row.value.row = { ...row.value.row, ...state.value }
row.value.oldRow = { ...row.value.row, ...state.value }
}
})
</script>
<template>
<slot />
<slot :state="state" />
</template>

16
packages/nc-gui-v2/components/smartsheet/expanded-form/Header.vue

@ -1,12 +1,24 @@
<script lang="ts" setup>
import { computed, useExpandedFormStoreOrThrow, useSmartsheetStoreOrThrow, useUIPermission } from '#imports'
import { computed, useSmartsheetStoreOrThrow, useUIPermission } from '#imports'
import { useSmartsheetRowStoreOrThrow } from '~/composables/useSmartsheetRowStore'
import { useExpandedFormStoreOrThrow } from '~/composables/useExpandedFormStore'
import MdiDoorOpen from '~icons/mdi/door-open'
import MdiDoorClosed from '~icons/mdi/door-closed'
const { meta } = useSmartsheetStoreOrThrow()
const { commentsDrawer, row, primaryValue, save } = useExpandedFormStoreOrThrow()
const { commentsDrawer, row, primaryValue, save: _save } = useExpandedFormStoreOrThrow()
const { isNew, syncLTARRefs } = useSmartsheetRowStoreOrThrow()
const { isUIAllowed } = useUIPermission()
const save = async () => {
if (isNew.value) {
const data = await _save()
await syncLTARRefs(data)
} else {
await _save()
}
}
const drawerToggleIcon = computed(() => (commentsDrawer.value ? MdiDoorOpen : MdiDoorClosed))
// todo: accept as a prop / inject

18
packages/nc-gui-v2/components/smartsheet/expanded-form/index.vue

@ -1,7 +1,7 @@
<script setup lang="ts">
import { isVirtualCol } from 'nocodb-sdk'
import { useVModel } from '@vueuse/core'
import { Ref, computed, provide, toRef } from 'vue'
import { Ref, computed, provide, toRef, watch } from 'vue'
import Comments from './Comments.vue'
import Header from './Header.vue'
import { useSmartsheetStoreOrThrow } from '~/composables'
@ -12,12 +12,14 @@ import { FieldsInj, IsFormInj, MetaInj } from '~/context'
interface Props {
modelValue: string | null
row: Row
state?: Record<string, any> | null
}
const props = defineProps<Props>()
const emits = defineEmits(['update:modelValue'])
const fields = inject(FieldsInj, ref([]))
const row = toRef(props, 'row')
const state = toRef(props, 'state')
const { meta } = useSmartsheetStoreOrThrow()
@ -26,7 +28,19 @@ provide(IsFormInj, true)
// accept as a prop
// const row: Row = { row: {}, rowMeta: {}, oldRow: {} }
const { commentsDrawer, changedColumns } = useProvideExpandedFormStore(meta, row)
const { commentsDrawer, changedColumns, state: rowState } = useProvideExpandedFormStore(meta, row)
watch(
state,
() => {
if (state.value) {
rowState.value = state.value
} else {
rowState.value = {}
}
},
{ immediate: true },
)
const isExpanded = useVModel(props, 'modelValue', emits)
</script>

2
packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue

@ -60,7 +60,7 @@ const container = computed(() =>
<MdiReload v-if="!isForm" class="cursor-pointer text-gray-500" @click="loadChildrenList" />
<a-button type="primary" size="small" @click="emit('attachRecord')">
<a-button type="primary" class="!text-xs" size="small" @click="emit('attachRecord')">
<div class="flex align-center gap-1">
<MdiLinkVariantRemove class="text-xs text-white" @click="unlinkRow(row)" />
Link to '{{ meta.title }}'

5
packages/nc-gui-v2/composables/useExpandedFormStore.ts

@ -101,6 +101,7 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
}
const save = async () => {
let data;
try {
// todo:
// if (this.presetValues) {
@ -116,7 +117,7 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
}, {} as Record<string, any>)
if (row.value.rowMeta.new) {
const data = await $api.dbTableRow.create('noco', project.value.title as string, meta.value.title, updateOrInsertObj)
data = await $api.dbTableRow.create('noco', project.value.title as string, meta.value.title, updateOrInsertObj)
/* todo:
// save hasmany and manytomany relations from local state
@ -127,7 +128,6 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
}
}
} */
row.value = {
row: data,
rowMeta: {},
@ -174,6 +174,7 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
notification.error({ message: `Failed to update row`, description: await extractSdkResponseErrorMsg(e) })
}
$e('a:row-expand:add')
return data
}
return {

72
packages/nc-gui-v2/composables/useSmartsheetRowStore.ts

@ -1,10 +1,21 @@
import type { ColumnType, TableType } from 'nocodb-sdk'
import { notification } from 'ant-design-vue'
import { UITypes } from 'nocodb-sdk'
import type { ColumnType, LinkToAnotherRecordType, TableType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { useNuxtApp } from '#app'
import { useInjectionState } from '#imports'
import { useMetas } from '~/composables/useMetas'
import { useProject } from '~/composables/useProject'
import type { Row } from '~/composables/useViewData'
import { useVirtualCell } from '~/composables/useVirtualCell'
import { NOCO } from '~/lib'
import { extractPkFromRow, extractSdkResponseErrorMsg } from '~/utils'
const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState((meta: Ref<TableType>, row: Ref<Row>) => {
const { $api } = useNuxtApp()
const { project } = useProject()
const { metas } = useMetas()
// state
const state = ref<Record<string, Record<string, any> | Record<string, any>[] | null>>({})
@ -13,25 +24,75 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
// actions
const addLTARRef = async (value: Record<string, any>, column: ColumnType) => {
const { isHm, isMm } = $(useVirtualCell(ref(column)))
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
if (isHm || isMm) {
state.value[column.title!] = state.value[column.title!] || []
state.value[column.title!]!.push(value)
} else {
} else if (isBt) {
state.value[column.title!] = value
}
}
// actions
const removeLTARRef = async (value: Record<string, any>, column: ColumnType) => {
const { isHm, isMm } = $(useVirtualCell(ref(column)))
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
if (isHm || isMm) {
state.value[column.title!]?.splice(state.value[column.title!]?.indexOf(value), 1)
} else {
} else if (isBt) {
state.value[column.title!] = null
}
}
const linkRecord = async (rowId: string, relatedRowId: string, column: ColumnType, type) => {
try {
await $api.dbTableRow.nestedAdd(
NOCO,
project.value.title as string,
meta.value.title as string,
rowId,
type,
column.title as string,
relatedRowId,
)
} catch (e) {
notification.error({
message: 'Linking failed',
description: await extractSdkResponseErrorMsg(e),
})
}
}
/** sync LTAR relations kept in local state */
const syncLTARRefs = async (row: Record<string, any>) => {
const id = extractPkFromRow(row, meta.value.columns as ColumnType[])
for (const column of meta?.value?.columns ?? []) {
if (column.uidt !== UITypes.LinkToAnotherRecord) continue
const colOptions = column?.colOptions as LinkToAnotherRecordType
const { isHm, isMm, isBt } = $(useVirtualCell(ref(column)))
const relatedTableMeta = metas.value?.[colOptions?.fk_related_model_id as string]
if (isHm || isMm) {
const relatedRows = (state.value?.[column.title!] ?? []) as Record<string, any>[]
for (const relatedRow of relatedRows) {
await linkRecord(
id,
extractPkFromRow(relatedRow, relatedTableMeta.columns as ColumnType[]),
column,
colOptions.type,
)
}
} else if (isBt && state.value?.[column.title!]) {
await linkRecord(
id,
extractPkFromRow(state.value?.[column.title!], relatedTableMeta.columns as ColumnType[]),
column,
colOptions.type,
)
}
}
}
return {
row,
state,
@ -39,6 +100,7 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
// todo: use better name
addLTARRef,
removeLTARRef,
syncLTARRefs,
}
}, 'smartsheet-row-store')

Loading…
Cancel
Save