Browse Source

feat(gui-v2): if new row add linked record to local state of row

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/3025/head
Pranav C 2 years ago
parent
commit
616244570f
  1. 16
      packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue
  2. 51
      packages/nc-gui-v2/components/virtual-cell/HasMany.vue
  3. 38
      packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue
  4. 19
      packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue
  5. 25
      packages/nc-gui-v2/components/virtual-cell/components/ListItems.vue
  6. 55
      packages/nc-gui-v2/composables/useLTARStore.ts
  7. 16
      packages/nc-gui-v2/composables/useSmartsheetRowStore.ts

16
packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue

@ -3,6 +3,9 @@ import type { ColumnType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import ItemChip from './components/ItemChip.vue'
import ListItems from './components/ListItems.vue'
import { useSmartsheetRowStoreOrThrow } from '~/composables/useSmartsheetRowStore'
import { useProvideLTARStore } from '#imports'
import { CellValueInj, ColumnInj, IsFormInj, ReloadViewDataHookInj, RowInj } from '~/context'
import { inject, ref, useProvideLTARStore } from '#imports'
import { CellValueInj,IsFormInj, ColumnInj, ReloadViewDataHookInj, RowInj } from '~/context'
@ -27,13 +30,22 @@ const { relatedTableMeta, loadRelatedTableMeta, relatedTablePrimaryValueProp, un
)
await loadRelatedTableMeta()
const { state, isNew } = useSmartsheetRowStoreOrThrow()
const value = computed(() => {
if (cellValue?.value) {
return cellValue?.value
} else if (isNew.value) {
return state?.value?.[column?.value.title as string]
}
return null
})
</script>
<template>
<div class="flex w-full chips-wrapper align-center" :class="{ active }">
<div class="chips d-flex align-center flex-grow">
<template v-if="cellValue">
<ItemChip :item="cellValue" :value="cellValue[relatedTablePrimaryValueProp]" @unlink="unlink(cellValue)" />
<template v-if="value">
<ItemChip :item="value" :value="cellValue[relatedTablePrimaryValueProp]" @unlink="unlink(value)" />
</template>
</div>
<div class="flex-1 flex justify-end gap-1 min-h-[30px] align-center">

51
packages/nc-gui-v2/components/virtual-cell/HasMany.vue

@ -27,16 +27,26 @@ const listItemsDlg = ref(false)
const childListDlg = ref(false)
const { localState, isNew } = useSmartsheetRowStoreOrThrow()
const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvideLTARStore(
const { state, isNew } = useSmartsheetRowStoreOrThrow()
const { relatedTableMeta, loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvideLTARStore(
column as Ref<Required<ColumnType>>,
row,
reloadTrigger.trigger,
)
await loadRelatedTableMeta()
const localCellValue = computed(() => {
if (cellValue?.value) {
return cellValue?.value
} else if (isNew.value) {
return state?.value?.[column?.value.title as string]
}
return []
})
const cells = computed(() =>
cellValue.value.reduce((acc: any[], curr: any) => {
localCellValue.value.reduce((acc: any[], curr: any) => {
if (!relatedTablePrimaryValueProp.value) return acc
const value = curr[relatedTablePrimaryValueProp.value]
@ -50,20 +60,27 @@ const cells = computed(() =>
<template>
<div class="flex align-center items-center gap-1 w-full chips-wrapper">
<div v-if="!isForm" class="chips flex align-center img-container flex-grow hm-items flex-nowrap min-w-0 overflow-hidden">
<template v-if="cellValue">
<ItemChip v-for="(cell, i) of cells" :key="i" :value="cell.value" @unlink="unlink(cell.item)" />
<span v-if="cellValue?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">more... </span>
</template>
</div>
<div class="flex-grow flex justify-end gap-1 min-h-[30px] align-center">
<MdiArrowExpand
class="select-none transform text-sm nc-action-icon text-gray-500/50 hover:text-gray-500"
@click="childListDlg = true"
/>
<MdiPlus class="select-none text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="listItemsDlg = true" />
</div>
<template v-if="!isForm">
<div class="chips flex align-center img-container flex-grow hm-items flex-nowrap min-w-0 overflow-hidden">
<template v-if="cells">
<ItemChip v-for="(cell, i) of cells" :key="i" :value="cell.value" @unlink="unlink(cell.item)" />
<span v-if="cellValue?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true"
>more...
</span>
</template>
</div>
<div class="flex-grow flex justify-end gap-1 min-h-[30px] align-center">
<MdiArrowExpand
class="select-none transform text-sm nc-action-icon text-gray-500/50 hover:text-gray-500"
@click="childListDlg = true"
/>
<MdiPlus
class="select-none text-sm nc-action-icon text-gray-500/50 hover:text-gray-500"
@click="listItemsDlg = true"
/>
</div>
</template>
<ListItems v-model="listItemsDlg" />
<ListChildItems v-model="childListDlg" @attach-record="() => { childListDlg = false; listItemsDlg = true }" />
</div>

38
packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue

@ -4,6 +4,9 @@ import type { Ref } from 'vue'
import ItemChip from './components/ItemChip.vue'
import ListChildItems from './components/ListChildItems.vue'
import ListItems from './components/ListItems.vue'
import { useSmartsheetRowStoreOrThrow } from '~/composables/useSmartsheetRowStore'
import { useProvideLTARStore } from '#imports'
import { CellValueInj, ColumnInj, IsFormInj, ReloadViewDataHookInj, RowInj } from '~/context'
import { computed, inject, ref, useProvideLTARStore } from '#imports'
import { CellValueInj, ColumnInj, ReloadViewDataHookInj,IsFormInj, RowInj } from '~/context'
@ -29,8 +32,19 @@ const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvid
await loadRelatedTableMeta()
const { state, isNew } = useSmartsheetRowStoreOrThrow()
const localCellValue = computed(() => {
if (cellValue?.value) {
return cellValue?.value
} else if (isNew.value) {
return state?.value?.[column?.value.title as string]
}
return []
})
const cells = computed(() =>
cellValue.value.reduce((acc: any[], curr: any) => {
localCellValue.value.reduce((acc: any[], curr: any) => {
if (!relatedTablePrimaryValueProp.value) return acc
const value = curr[relatedTablePrimaryValueProp.value]
@ -45,24 +59,24 @@ const cells = computed(() =>
<template>
<div class="flex align-center gap-1 w-full h-full chips-wrapper">
<template v-if="!isForm">
<div class="chips flex align-center img-container flex-grow hm-items flex-nowrap min-w-0 overflow-hidden">
<template v-if="cellValue">
<ItemChip v-for="(cell, i) of cells" :key="i" :value="cell.value" @unlink="unlink(cell.item)" />
<div class="chips flex align-center img-container flex-grow hm-items flex-nowrap min-w-0 overflow-hidden">
<template v-if="cells">
<ItemChip v-for="(cell, i) of cells" :key="i" :value="cell.value" @unlink="unlink(cell.item)" />
<span v-if="cellValue?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">more... </span>
</template>
</div>
<span v-if="value?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">more... </span>
</template>
</div>
<div class="flex-1 flex justify-end gap-1 min-h-[30px] align-center">
<MdiArrowExpand class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="childListDlg = true" />
<div class="flex-1 flex justify-end gap-1 min-h-[30px] align-center">
<MdiArrowExpand class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="childListDlg = true" />
<MdiPlus class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="listItemsDlg = true" />
</div>
<MdiPlus class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="listItemsDlg = true" />
</div>
</template>
<ListItems v-model="listItemsDlg" />
<ListChildItems v-model="childListDlg" @attach-record=";(childListDlg = false), (listItemsDlg = true)" />
<ListChildItems v-model="childListDlg" @attach-record="() => { childListDlg = false; listItemsDlg = true; }" />
</div>
</template>

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

@ -1,6 +1,9 @@
<script lang="ts" setup>
import { watchEffect } from '@vue/runtime-core'
import { Modal } from 'ant-design-vue'
import { useLTARStoreOrThrow, useVModel } from '#imports'
import { useSmartsheetRowStoreOrThrow } from '~/composables/useSmartsheetRowStore'
import { ColumnInj, IsFormInj } from '~/context'
import { IsFormInj } from '~/context'
import { useLTARStoreOrThrow, useVModel, watch } from '#imports'
@ -9,6 +12,7 @@ const emit = defineEmits(['update:modelValue', 'attachRecord'])
const vModel = useVModel(props, 'modelValue', emit)
const isForm = inject(IsFormInj, false)
const column = inject(ColumnInj)
const {
childrenList,
@ -21,6 +25,8 @@ const {
getRelatedTableRowId,
} = useLTARStoreOrThrow()
const { isNew, state } = useSmartsheetRowStoreOrThrow()
watch([vModel, isForm], (nextVal) => {
if (nextVal[0] || nextVal[1]) {
loadChildrenList()
@ -51,15 +57,18 @@ const container = computed(() =>
<a-button type="primary" size="small" @click="emit('attachRecord')">
<div class="flex align-center gap-1">
<!-- todo: row is not defined? @click="unlinkRow(row)" -->
<MdiLinkVariantRemove class="text-xs text-white" />
<MdiLinkVariantRemove class="text-xs text-white" @click="unlinkRow(row)" />
Link to '{{ meta.title }}'
</div>
</a-button>
</div>
<template v-if="childrenList?.pageInfo?.totalRows">
<template v-if="(isNew && state?.[column?.title]?.length) || childrenList?.pageInfo?.totalRows">
<div class="flex-1 overflow-auto min-h-0">
<a-card v-for="(row, i) of childrenList?.list ?? []" :key="i" class="ma-2 hover:(!bg-gray-200/50 shadow-md)">
<a-card
v-for="(row, i) of childrenList?.list ?? state?.[column?.title] ?? []"
:key="i"
class="ma-2 hover:(!bg-gray-200/50 shadow-md)"
>
<div class="flex align-center">
<div class="flex-grow overflow-hidden min-w-0">
{{ row[relatedTablePrimaryValueProp]
@ -74,7 +83,7 @@ const container = computed(() =>
</a-card>
</div>
<a-pagination
v-if="childrenList?.pageInfo"
v-if="!isNew && childrenList?.pageInfo"
v-model:current="childrenListPagination.page"
v-model:page-size="childrenListPagination.size"
class="mt-2 mx-auto"

25
packages/nc-gui-v2/components/virtual-cell/components/ListItems.vue

@ -1,5 +1,8 @@
<script lang="ts" setup>
import { useLTARStoreOrThrow, useVModel, watch } from '#imports'
import type { ColumnType } from 'nocodb-sdk'
import { useLTARStoreOrThrow, useVModel } from '#imports'
import { useSmartsheetRowStoreOrThrow } from '~/composables/useSmartsheetRowStore'
import { ColumnInj } from '~/context'
const props = defineProps<{ modelValue: boolean }>()
@ -7,6 +10,8 @@ const emit = defineEmits(['update:modelValue', 'addNewRecord'])
const vModel = useVModel(props, 'modelValue', emit)
const column = inject(ColumnInj)
const {
childrenExcludedList,
loadChildrenExcludedList,
@ -16,16 +21,22 @@ const {
getRelatedTableRowId,
} = useLTARStoreOrThrow()
watch(vModel, (nextVal) => {
if (nextVal) {
loadChildrenExcludedList()
}
})
const { addLTARRef, isNew } = useSmartsheetRowStoreOrThrow()
const linkRow = async (row: Record<string, any>) => {
await link(row)
if (isNew.value) {
addLTARRef(row, column?.value as ColumnType)
} else {
await link(row)
}
vModel.value = false
}
watch(vModel, () => {
if (vModel.value) {
loadChildrenExcludedList(isNew.value)
}
})
</script>
<template>

55
packages/nc-gui-v2/composables/useLTARStore.ts

@ -61,28 +61,49 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
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<ColumnType>) => c.pv) || relatedTableMeta?.value?.columns?.[0])?.title
})
const loadChildrenExcludedList = async () => {
const loadChildrenExcludedList = async (isNewRow = false) => {
try {
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,
// todo: swagger type correction
{
limit: childrenExcludedListPagination.size,
offset: childrenExcludedListPagination.size * (childrenExcludedListPagination.page - 1),
where:
childrenExcludedListPagination.query &&
`(${relatedTablePrimaryValueProp.value},like,${childrenExcludedListPagination.query})`,
} as any,
)
/** if new row load all records */
if (isNewRow) {
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,
// todo: swagger type correction
{
limit: childrenExcludedListPagination.size,
offset: childrenExcludedListPagination.size * (childrenExcludedListPagination.page - 1),
where:
childrenExcludedListPagination.query &&
`(${relatedTablePrimaryValueProp.value},like,${childrenExcludedListPagination.query})`,
} as any,
)
}
} catch (e: any) {
notification.error({
message: 'Failed to load list',

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

@ -1,21 +1,33 @@
import type { TableType } from 'nocodb-sdk'
import type { ColumnType, TableType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { useInjectionState } from '#imports'
import type { Row } from '~/composables/useViewData'
import { useVirtualCell } from '~/composables/useVirtualCell'
const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState((meta: Ref<TableType>, row: Ref<Row>) => {
// state
const state = ref({})
const state = ref<Record<string, Record<string, any> | Record<string, any>[]>>({})
// getters
const isNew = computed(() => row.value?.rowMeta?.new)
// actions
const addLTARRef = async (value: Record<string, any>, column: ColumnType) => {
const { isHm, isMm } = useVirtualCell(ref(column))
if (isHm || isMm) {
state.value[column.title!] = state.value[column.title!] || []
state.value[column.title!].push(value)
} else {
state.value[column.title!] = value
}
}
return {
row,
state,
isNew,
// todo: use better name
addLTARRef,
}
}, 'smartsheet-row-store')

Loading…
Cancel
Save