Browse Source

feat(gui-v2): children list modal functionalities

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/2990/head
Pranav C 2 years ago
parent
commit
05c10b78e8
  1. 1
      packages/nc-gui-v2/components.d.ts
  2. 11
      packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue
  3. 17
      packages/nc-gui-v2/components/virtual-cell/HasMany.vue
  4. 15
      packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue
  5. 65
      packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue
  6. 43
      packages/nc-gui-v2/components/virtual-cell/components/ListItems.vue
  7. 21
      packages/nc-gui-v2/composables/useLTARStore.ts

1
packages/nc-gui-v2/components.d.ts vendored

@ -20,6 +20,7 @@ declare module '@vue/runtime-core' {
ADivider: typeof import('ant-design-vue/es')['Divider'] ADivider: typeof import('ant-design-vue/es')['Divider']
ADrawer: typeof import('ant-design-vue/es')['Drawer'] ADrawer: typeof import('ant-design-vue/es')['Drawer']
ADropdown: typeof import('ant-design-vue/es')['Dropdown'] ADropdown: typeof import('ant-design-vue/es')['Dropdown']
AEmpty: typeof import('ant-design-vue/es')['Empty']
AForm: typeof import('ant-design-vue/es')['Form'] AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem'] AFormItem: typeof import('ant-design-vue/es')['FormItem']
AInput: typeof import('ant-design-vue/es')['Input'] AInput: typeof import('ant-design-vue/es')['Input']

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

@ -29,16 +29,21 @@ await loadRelatedTableMeta()
<ItemChip :item="value" :value="value[relatedTablePrimaryValueProp]" @unlink="unlink(value || localState)" /> <ItemChip :item="value" :value="value[relatedTablePrimaryValueProp]" @unlink="unlink(value || localState)" />
</template> </template>
</div> </div>
<div class="flex-1" /> <div class="flex-1 flex justify-end gap-1">
<MdiExpandIcon class="nc-action-icon text-md text-gray-500/50 hover:text-gray-500" @click="listItemsDlg = true" /> <MdiExpandIcon
class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 select-none transform group-hover:(text-pink-500 scale-120)"
@click="listItemsDlg = true"
/>
</div>
<ListItems v-model="listItemsDlg" /> <ListItems v-model="listItemsDlg" />
</div> </div>
</template> </template>
<style scoped> <style scoped>
.nc-action-icon { .nc-action-icon {
@apply hidden; @apply hidden cursor-pointer;
} }
.chips-wrapper:hover .nc-action-icon { .chips-wrapper:hover .nc-action-icon {
@apply inline-block; @apply inline-block;
} }

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

@ -32,19 +32,24 @@ await loadRelatedTableMeta()
<span v-if="value?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">more... </span> <span v-if="value?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">more... </span>
</template> </template>
</div> </div>
<div class="flex-1 flex justify-end gap-1">
<MdiExpandIcon class="nc-action-icon w-[20px] text-gray-500/50 hover:text-gray-500" @click="childListDlg = true" /> <MdiExpandIcon
<MdiPlusIcon class="nc-action-icon w-[20px] text-gray-500/50 hover:text-gray-500" @click="listItemsDlg = true" /> class="select-none transform group-hover:(text-pink-500 scale-120) text-sm nc-action-icon text-gray-500/50 hover:text-gray-500"
@click="childListDlg = true"
/>
<MdiPlusIcon class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="listItemsDlg = true" />
</div>
<ListItems v-model="listItemsDlg" /> <ListItems v-model="listItemsDlg" />
<ListChildItems v-model="childListDlg" /> <ListChildItems @attachRecord="childListDlg=false,listItemsDlg=true" v-model="childListDlg" />
</div> </div>
</template> </template>
<style scoped> <style scoped>
.nc-action-icon { .nc-action-icon {
@apply hidden; @apply hidden cursor-pointer;
} }
.chips-wrapper:hover .nc-action-icon { .chips-wrapper:hover .nc-action-icon {
@apply inline-block; @apply flex;
} }
</style> </style>

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

@ -32,21 +32,24 @@ await loadRelatedTableMeta()
<template v-if="value"> <template v-if="value">
<ItemChip v-for="(ch, i) in value" :key="i" :value="ch[relatedTablePrimaryValueProp]" @unlink="unlink(ch)" /> <ItemChip v-for="(ch, i) in value" :key="i" :value="ch[relatedTablePrimaryValueProp]" @unlink="unlink(ch)" />
<span v-if="value?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">more... </span> <span v-if="value?.length === 10" class="caption pointer ml-1 grey--text"
@click="childListDlg = true">more... </span>
</template> </template>
</div> </div>
<div class="flex-1" /> <div class="flex-1 flex justify-end gap-1">
<MdiExpandIcon class="nc-action-icon text-gray-500/50 hover:text-gray-500" @click="childListDlg = true" /> <MdiExpandIcon class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="childListDlg = true" />
<MdiPlusIcon class="nc-action-icon w-[20px] text-gray-500/50 hover:text-gray-500" @click="listItemsDlg = true" /> <MdiPlusIcon class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="listItemsDlg = true" />
</div>
<ListItems v-model="listItemsDlg" /> <ListItems v-model="listItemsDlg" />
<ListChildItems v-model="childListDlg" /> <ListChildItems @attachRecord="childListDlg=false,listItemsDlg=true" v-model="childListDlg" />
</div> </div>
</template> </template>
<style scoped> <style scoped>
.nc-action-icon { .nc-action-icon {
@apply hidden; @apply hidden cursor-pointer;
} }
.chips-wrapper:hover .nc-action-icon { .chips-wrapper:hover .nc-action-icon {
@apply inline-block; @apply inline-block;
} }

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

@ -1,13 +1,25 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { useLTARStoreOrThrow } from '~/composables' import { useLTARStoreOrThrow } from '~/composables'
import MdiReloadIcon from '~icons/mdi/reload'
import MdiDeleteIcon from '~icons/mdi/delete-outline'
import MdiUnlinkIcon from '~icons/mdi/link-variant-remove'
const props = defineProps<{ modelValue?: boolean }>() const props = defineProps<{ modelValue?: boolean }>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue', 'attachRecord'])
const vModel = useVModel(props, 'modelValue', emit) const vModel = useVModel(props, 'modelValue', emit)
const { childrenList, loadChildrenList, childrenListPagination, relatedTablePrimaryValueProp, unlink } = useLTARStoreOrThrow() const {
childrenList,
meta,
deleteRelatedRow,
loadChildrenList,
childrenListPagination,
relatedTablePrimaryValueProp,
unlink,
} =
useLTARStoreOrThrow()
watch(vModel, () => { watch(vModel, () => {
if (vModel.value) { if (vModel.value) {
@ -24,20 +36,43 @@ const unlinkRow = async (row: Record<string, any>) => {
<template> <template>
<a-modal v-model:visible="vModel" :footer="null" title="Child list"> <a-modal v-model:visible="vModel" :footer="null" title="Child list">
<div class="max-h-[max(calc(100vh_-_300px)_,500px)] flex flex-col"> <div class="max-h-[max(calc(100vh_-_300px)_,500px)] flex flex-col">
<div class="flex-1 overflow-auto min-h-0"> <div class="flex mb-4 align-center gap-2">
<a-card v-for="(row, i) of childrenList?.list ?? []" :key="i" class="my-1 cursor-pointer" @click="unlinkRow(row)"> <!-- <a-input v-model:value="childrenListPagination.query" class="max-w-[200px]" size="small"></a-input> -->
{{ row[relatedTablePrimaryValueProp] }} <div class="flex-1" />
</a-card> <MdiReloadIcon class="cursor-pointer text-gray-500" @click="loadChildrenList" />
<a-button type="primary" size="small" @click="emit('attachRecord')">
<div class="flex align-center gap-1">
<MdiUnlinkIcon class="text-xs text-white" @click="unlinkRow(row)" />
Link to '{{ meta.title }}'
</div>
</a-button>
</div> </div>
<a-pagination <template v-if="childrenList?.pageInfo?.totalRows">
v-if="childrenList?.pageInfo" <div class="flex-1 overflow-auto min-h-0">
v-model:current="childrenListPagination.page" <a-card v-for="(row, i) of childrenList?.list ?? []" :key="i" class="ma-2 hover:(!bg-gray-200/50 shadow-md)">
v-model:page-size="childrenListPagination.size" <div class="flex align-center">
class="mt-2 mx-auto" <div class="flex-grow overflow-hidden min-w-0">
size="small" {{ row[relatedTablePrimaryValueProp] }}
:total="childrenList.pageInfo.totalRows" </div>
show-less-items <div class="flex gap-2">
/> <MdiUnlinkIcon class="text-xs text-grey hover:(!text-red-500) cursor-pointer" @click="unlinkRow(row)" />
<MdiDeleteIcon class="text-xs text-grey hover:(!text-red-500) cursor-pointer"
@click="deleteRelatedRow(row)" />
</div>
</div>
</a-card>
</div>
<a-pagination
v-if="childrenList?.pageInfo"
v-model:current="childrenListPagination.page"
v-model:page-size="childrenListPagination.size"
class="mt-2 mx-auto"
size="small"
:total="childrenList.pageInfo.totalRows"
show-less-items
/>
</template>
<a-empty class="my-10" v-else />
</div> </div>
</a-modal> </a-modal>
</template> </template>

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

@ -20,7 +20,8 @@ watch(vModel, () => {
const linkRow = async (row: Record<string, any>) => { const linkRow = async (row: Record<string, any>) => {
await link(row) await link(row)
await loadChildrenExcludedList() vModel.value= false
// await loadChildrenExcludedList()
} }
</script> </script>
@ -28,25 +29,33 @@ const linkRow = async (row: Record<string, any>) => {
<a-modal v-model:visible="vModel" :footer="null" title="Related table rows"> <a-modal v-model:visible="vModel" :footer="null" title="Related table rows">
<div class="max-h-[max(calc(100vh_-_300px)_,500px)] flex flex-col"> <div class="max-h-[max(calc(100vh_-_300px)_,500px)] flex flex-col">
<div class="flex mb-4 align-center gap-2"> <div class="flex mb-4 align-center gap-2">
<a-input v-model:value="childrenExcludedListPagination.query" class="max-w-[200px]" size="small"></a-input> <a-input v-model:value="childrenExcludedListPagination.query" placeholder="Filter query" class="max-w-[200px]" size="small"></a-input>
<div class="flex-1" /> <div class="flex-1" />
<MdiReloadIcon class="cursor-pointer text-gray-500" @click="loadChildrenExcludedList" /> <MdiReloadIcon class="cursor-pointer text-gray-500" @click="loadChildrenExcludedList" />
<a-button type="primary" size="small" @click="$emit('addNewRecord')">Add new record</a-button> <a-button type="primary" size="small" @click="emit('addNewRecord')">Add new record</a-button>
</div> </div>
<div class="flex-1 overflow-auto min-h-0"> <template v-if="childrenExcludedList?.pageInfo?.totalRows">
<a-card v-for="(row, i) in childrenExcludedList?.list ?? []" :key="i" class="my-1 cursor-pointer" @click="linkRow(row)"> <div class="flex-1 overflow-auto min-h-0">
{{ row[relatedTablePrimaryValueProp] }} <a-card
</a-card> v-for="(row, i) in childrenExcludedList?.list ?? []"
</div> :key="i"
<a-pagination class="ma-2 cursor-pointer hover:(!bg-gray-200/50 shadow-md)"
v-if="childrenExcludedList?.pageInfo" @click="linkRow(row)"
v-model:current="childrenExcludedListPagination.page" >
v-model:page-size="childrenExcludedListPagination.size" {{ row[relatedTablePrimaryValueProp] }}
class="mt-2 mx-auto !text-xs" </a-card>
size="small" </div>
:total="childrenExcludedList.pageInfo.totalRows" <a-pagination
show-less-items v-if="childrenExcludedList?.pageInfo"
/> v-model:current="childrenExcludedListPagination.page"
v-model:page-size="childrenExcludedListPagination.size"
class="mt-2 mx-auto !text-xs"
size="small"
:total="childrenExcludedList.pageInfo.totalRows"
show-less-items
/>
</template>
<a-empty class="my-10" v-else />
</div> </div>
</a-modal> </a-modal>
</template> </template>

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

@ -1,8 +1,10 @@
import type { ColumnType, LinkToAnotherRecordType, PaginatedType, TableType } from 'nocodb-sdk' import type { ColumnType, LinkToAnotherRecordType, PaginatedType, TableType } from 'nocodb-sdk'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { Modal, notification } from 'ant-design-vue'
import { useInjectionState, useMetas, useProject } from '#imports' import { useInjectionState, useMetas, useProject } from '#imports'
import { NOCO } from '~/lib' import { NOCO } from '~/lib'
import type { Row } from '~/composables' import type { Row } from '~/composables'
import { extractSdkResponseErrorMsg } from '~/utils'
interface DataApiResponse { interface DataApiResponse {
list: Record<string, any> list: Record<string, any>
@ -97,6 +99,24 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
} as any, } as any,
) )
} }
const deleteRelatedRow = async (row: Record<string, any>) => {
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) {
notification.error(await extractSdkResponseErrorMsg(e))
}
},
})
}
const unlink = async (row: Record<string, any>) => { const unlink = async (row: Record<string, any>) => {
// const column = meta.columns.find(c => c.id === this.column.colOptions.fk_child_column_id); // const column = meta.columns.find(c => c.id === this.column.colOptions.fk_child_column_id);
// todo: handle if new record // todo: handle if new record
@ -192,6 +212,7 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
loadChildrenExcludedList, loadChildrenExcludedList,
loadChildrenList, loadChildrenList,
row, row,
deleteRelatedRow,
} }
}, },
'ltar-store', 'ltar-store',

Loading…
Cancel
Save