diff --git a/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue b/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue new file mode 100644 index 0000000000..7c3e8a4466 --- /dev/null +++ b/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/packages/nc-gui/components/dashboard/TreeView/TableList.vue b/packages/nc-gui/components/dashboard/TreeView/TableList.vue index 6f8beef505..c372fea1cd 100644 --- a/packages/nc-gui/components/dashboard/TreeView/TableList.vue +++ b/packages/nc-gui/components/dashboard/TreeView/TableList.vue @@ -26,10 +26,6 @@ const tables = computed(() => projectTables.value.get(project.value.id!) ?? []) const { $api } = useNuxtApp() -const { openTable } = useTableNew({ - projectId: project.value.id!, -}) - const tablesById = computed(() => tables.value.reduce>((acc, table) => { acc[table.id!] = table @@ -153,7 +149,7 @@ const availableTables = computed(() => { v-for="table of availableTables" :key="table.id" v-e="['a:table:open']" - class="nc-tree-item text-sm cursor-pointer group" + class="nc-tree-item text-sm" :data-order="table.order" :data-id="table.id" :data-testid="`tree-view-table-${table.title}`" @@ -163,7 +159,6 @@ const availableTables = computed(() => { :data-title="table.title" :data-base-id="base?.id" :data-type="table.type" - @click="openTable(table)" > diff --git a/packages/nc-gui/components/dashboard/TreeView/TableNode.vue b/packages/nc-gui/components/dashboard/TreeView/TableNode.vue index 95d2930103..7d4e2b1192 100644 --- a/packages/nc-gui/components/dashboard/TreeView/TableNode.vue +++ b/packages/nc-gui/components/dashboard/TreeView/TableNode.vue @@ -20,6 +20,10 @@ const project = toRef(props, 'project') const table = toRef(props, 'table') const baseIndex = toRef(props, 'baseIndex') +const { openTable } = useTableNew({ + projectId: project.value.id!, +}) + const route = useRoute() const { isUIAllowed } = useRoles() @@ -34,9 +38,13 @@ useTableNew({ }) const projectRole = inject(ProjectRoleInj) +provide(SidebarTableInj, table) const { setMenuContext, openRenameTableDialog, duplicateTable } = inject(TreeViewInj)! +const { loadViews: _loadViews } = useViewsStore() +const { activeView } = storeToRefs(useViewsStore()) + // todo: temp const { projectTables } = storeToRefs(useTablesStore()) const tables = computed(() => projectTables.value.get(project.value.id!) ?? []) @@ -73,80 +81,127 @@ const { isSharedBase } = useProject() const canUserEditEmote = computed(() => { return isUIAllowed('tableIconEdit', { roles: projectRole?.value }) }) + +const isExpanded = ref(false) +const isLoading = ref(false) + +const onExpand = async () => { + if (isExpanded.value) { + isExpanded.value = false + return + } + + isLoading.value = true + try { + await _loadViews({ tableId: table.value.id, ignoreLoading: true }) + } catch (e) { + message.error(await extractSdkResponseErrorMsg(e)) + } finally { + isLoading.value = false + isExpanded.value = true + } +} + +watch( + () => activeView.value?.id, + () => { + if (!activeView.value) return + + if (activeView.value?.fk_model_id === table.value?.id) { + isExpanded.value = true + } + }, + { + immediate: true, + }, +) + +const isTableOpened = computed(() => { + return openedTableId.value === table.value?.id && activeView.value?.is_default +}) diff --git a/packages/nc-gui/components/dashboard/TreeView/ViewsList.vue b/packages/nc-gui/components/dashboard/TreeView/ViewsList.vue new file mode 100644 index 0000000000..1a586c1cc1 --- /dev/null +++ b/packages/nc-gui/components/dashboard/TreeView/ViewsList.vue @@ -0,0 +1,392 @@ + + + + + diff --git a/packages/nc-gui/components/dashboard/TreeView/ViewsNode.vue b/packages/nc-gui/components/dashboard/TreeView/ViewsNode.vue new file mode 100644 index 0000000000..74daf9a809 --- /dev/null +++ b/packages/nc-gui/components/dashboard/TreeView/ViewsNode.vue @@ -0,0 +1,296 @@ + + + diff --git a/packages/nc-gui/components/dlg/ViewCreate.vue b/packages/nc-gui/components/dlg/ViewCreate.vue index 185d5bb2e2..1e1ba17cad 100644 --- a/packages/nc-gui/components/dlg/ViewCreate.vue +++ b/packages/nc-gui/components/dlg/ViewCreate.vue @@ -14,7 +14,7 @@ interface Props { groupingFieldColumnId?: string geoDataFieldColumnId?: string views: ViewType[] - meta: TableType + tableId: string } interface Emits { @@ -31,10 +31,20 @@ interface Form { fk_geo_data_col_id: string | null } -const { views = [], meta, selectedViewId, groupingFieldColumnId, geoDataFieldColumnId, ...props } = defineProps() +const props = withDefaults(defineProps(), { + selectedViewId: undefined, + groupingFieldColumnId: undefined, + geoDataFieldColumnId: undefined, +}) const emits = defineEmits() +const { getMeta } = useMetas() + +const { views, selectedViewId, groupingFieldColumnId, geoDataFieldColumnId, tableId } = toRefs(props) + +const meta = ref() + const inputEl = ref() const formValidator = ref() @@ -64,7 +74,7 @@ const viewNameRules = [ { validator: (_: unknown, v: string) => new Promise((resolve, reject) => { - views.every((v1) => v1.title !== v) ? resolve(true) : reject(new Error(`View name should be unique`)) + views.value.every((v1) => v1.title !== v) ? resolve(true) : reject(new Error(`View name should be unique`)) }), message: 'View name should be unique', }, @@ -97,52 +107,13 @@ watch( function init() { form.title = `Untitled ${capitalize(typeAlias.value)}` - const repeatCount = views.filter((v) => v.title.startsWith(form.title)).length + const repeatCount = views.value.filter((v) => v.title.startsWith(form.title)).length if (repeatCount) { form.title = `${form.title} ${repeatCount}` } - if (selectedViewId) { - form.copy_from_id = selectedViewId - } - - // preset the grouping field column - if (props.type === ViewTypes.KANBAN) { - viewSelectFieldOptions.value = meta - .columns!.filter((el) => el.uidt === UITypes.SingleSelect) - .map((field) => { - return { - value: field.id, - label: field.title, - } - }) - - if (groupingFieldColumnId) { - // take from the one from copy view - form.fk_grp_col_id = groupingFieldColumnId - } else { - // take the first option - form.fk_grp_col_id = viewSelectFieldOptions.value?.[0]?.value as string - } - } - - if (props.type === ViewTypes.MAP) { - viewSelectFieldOptions.value = meta - .columns!.filter((el) => el.uidt === UITypes.GeoData) - .map((field) => { - return { - value: field.id, - label: field.title, - } - }) - - if (geoDataFieldColumnId) { - // take from the one from copy view - form.fk_geo_data_col_id = geoDataFieldColumnId - } else { - // take the first option - form.fk_geo_data_col_id = viewSelectFieldOptions.value?.[0]?.value as string - } + if (selectedViewId.value) { + form.copy_from_id = selectedViewId?.value } nextTick(() => { @@ -165,9 +136,7 @@ async function onSubmit() { } if (isValid && form.type) { - const _meta = unref(meta) - - if (!_meta || !_meta.id) return + if (!tableId.value) return try { let data: GridType | KanbanType | GalleryType | FormType | MapType | null = null @@ -176,19 +145,19 @@ async function onSubmit() { switch (form.type) { case ViewTypes.GRID: - data = await api.dbView.gridCreate(_meta.id, form) + data = await api.dbView.gridCreate(tableId.value, form) break case ViewTypes.GALLERY: - data = await api.dbView.galleryCreate(_meta.id, form) + data = await api.dbView.galleryCreate(tableId.value, form) break case ViewTypes.FORM: - data = await api.dbView.formCreate(_meta.id, form) + data = await api.dbView.formCreate(tableId.value, form) break case ViewTypes.KANBAN: - data = await api.dbView.kanbanCreate(_meta.id, form) + data = await api.dbView.kanbanCreate(tableId.value, form) break case ViewTypes.MAP: - data = await api.dbView.mapCreate(_meta.id, form) + data = await api.dbView.mapCreate(tableId.value, form) } if (data) { @@ -208,6 +177,60 @@ async function onSubmit() { }, 500) } } + +const isMetaLoading = ref(false) + +onMounted(async () => { + if (props.type === ViewTypes.KANBAN || props.type === ViewTypes.MAP) { + isMetaLoading.value = true + try { + meta.value = (await getMeta(tableId.value))! + + if (props.type === ViewTypes.MAP) { + viewSelectFieldOptions.value = meta + .value!.columns!.filter((el) => el.uidt === UITypes.GeoData) + .map((field) => { + return { + value: field.id, + label: field.title, + } + }) + + if (geoDataFieldColumnId.value) { + // take from the one from copy view + form.fk_geo_data_col_id = geoDataFieldColumnId.value + } else { + // take the first option + form.fk_geo_data_col_id = viewSelectFieldOptions.value?.[0]?.value as string + } + } + + // preset the grouping field column + if (props.type === ViewTypes.KANBAN) { + viewSelectFieldOptions.value = meta.value + .columns!.filter((el) => el.uidt === UITypes.SingleSelect) + .map((field) => { + return { + value: field.id, + label: field.title, + } + }) + + if (groupingFieldColumnId.value) { + // take from the one from copy view + form.fk_grp_col_id = groupingFieldColumnId.value + } else { + // take the first option + form.fk_grp_col_id = viewSelectFieldOptions.value?.[0]?.value as string + } + } + } catch (e) { + console.error(e) + } finally { + isMetaLoading.value = false + } + } +}) -