|
|
|
@ -1,15 +1,21 @@
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
|
import type { FunctionalComponent, SVGAttributes } from 'vue' |
|
|
|
|
import AppStore from './AppStore.vue' |
|
|
|
|
import DataSources from './DataSources.vue' |
|
|
|
|
import { resolveComponent, useI18n, useNuxtApp, useUIPermission, useVModel, watch } from '#imports' |
|
|
|
|
import Misc from './Misc.vue' |
|
|
|
|
import { useNuxtApp } from '#app' |
|
|
|
|
import { useI18n, useUIPermission, useVModel, watch } from '#imports' |
|
|
|
|
import StoreFrontOutline from '~icons/mdi/storefront-outline' |
|
|
|
|
import TeamFillIcon from '~icons/ri/team-fill' |
|
|
|
|
import MultipleTableIcon from '~icons/mdi/table-multiple' |
|
|
|
|
import NotebookOutline from '~icons/mdi/notebook-outline' |
|
|
|
|
import FolderCog from '~icons/mdi/folder-cog' |
|
|
|
|
import { DataSourcesSubTab } from '~~/lib' |
|
|
|
|
|
|
|
|
|
interface Props { |
|
|
|
|
modelValue: boolean |
|
|
|
|
openKey?: string |
|
|
|
|
dataSourcesState?: string |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
interface SubTabGroup { |
|
|
|
@ -41,6 +47,9 @@ const { t } = useI18n()
|
|
|
|
|
|
|
|
|
|
const { $e } = useNuxtApp() |
|
|
|
|
|
|
|
|
|
const dataSourcesState = ref(props.dataSourcesState) |
|
|
|
|
const dataSourcesReload = ref(false) |
|
|
|
|
|
|
|
|
|
const tabsInfo: TabGroup = { |
|
|
|
|
teamAndAuth: { |
|
|
|
|
title: t('title.teamAndAuth'), |
|
|
|
@ -69,8 +78,6 @@ const tabsInfo: TabGroup = {
|
|
|
|
|
$e('c:settings:team-auth') |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
...(isUIAllowed('appStore') |
|
|
|
|
? { |
|
|
|
|
appStore: { |
|
|
|
|
// App Store |
|
|
|
|
title: t('title.appStore'), |
|
|
|
@ -78,51 +85,26 @@ const tabsInfo: TabGroup = {
|
|
|
|
|
subTabs: { |
|
|
|
|
new: { |
|
|
|
|
title: 'Apps', |
|
|
|
|
body: resolveComponent('DashboardSettingsAppStore'), |
|
|
|
|
body: AppStore, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
onClick: () => { |
|
|
|
|
$e('c:settings:appstore') |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
: {}), |
|
|
|
|
projMetaData: { |
|
|
|
|
// Project Metadata |
|
|
|
|
title: t('title.projMeta'), |
|
|
|
|
dataSources: { |
|
|
|
|
// Data Sources |
|
|
|
|
title: 'Data Sources', |
|
|
|
|
icon: MultipleTableIcon, |
|
|
|
|
subTabs: { |
|
|
|
|
dataSources: { |
|
|
|
|
title: 'Data Sources', |
|
|
|
|
body: DataSources, |
|
|
|
|
}, |
|
|
|
|
metaData: { |
|
|
|
|
// Metadata |
|
|
|
|
title: t('title.metadata'), |
|
|
|
|
body: resolveComponent('DashboardSettingsMetadata'), |
|
|
|
|
}, |
|
|
|
|
acl: { |
|
|
|
|
// UI Access Control |
|
|
|
|
title: t('title.uiACL'), |
|
|
|
|
body: resolveComponent('DashboardSettingsUIAcl'), |
|
|
|
|
onClick: () => { |
|
|
|
|
$e('c:table:ui-acl') |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
erd: { |
|
|
|
|
title: t('title.erdView'), |
|
|
|
|
body: resolveComponent('DashboardSettingsErd'), |
|
|
|
|
onClick: () => { |
|
|
|
|
$e('c:settings:erd') |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
misc: { |
|
|
|
|
title: t('general.misc'), |
|
|
|
|
body: resolveComponent('DashboardSettingsMisc'), |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
onClick: () => { |
|
|
|
|
$e('c:settings:proj-metadata') |
|
|
|
|
dataSourcesState.value = '' |
|
|
|
|
$e('c:settings:data-sources') |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
audit: { |
|
|
|
@ -140,6 +122,21 @@ const tabsInfo: TabGroup = {
|
|
|
|
|
$e('c:settings:audit') |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
projectSettings: { |
|
|
|
|
// Project Settings |
|
|
|
|
title: 'Project Settings', |
|
|
|
|
icon: FolderCog, |
|
|
|
|
subTabs: { |
|
|
|
|
misc: { |
|
|
|
|
// Misc |
|
|
|
|
title: 'Misc', |
|
|
|
|
body: Misc, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
onClick: () => { |
|
|
|
|
$e('c:settings:project-settings') |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
const firstKeyOfObject = (obj: object) => Object.keys(obj)[0] |
|
|
|
|
|
|
|
|
@ -163,6 +160,15 @@ watch(
|
|
|
|
|
selectedTabKeys = [Object.keys(tabsInfo).find((key) => key === nextOpenKey) || firstKeyOfObject(tabsInfo)] |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
watch( |
|
|
|
|
() => props.modelValue, |
|
|
|
|
() => { |
|
|
|
|
dataSourcesState.value = props.dataSourcesState || '' |
|
|
|
|
selectedTabKeys = [Object.keys(tabsInfo).find((key) => key === props.openKey) || firstKeyOfObject(tabsInfo)] |
|
|
|
|
}, |
|
|
|
|
{ immediate: true }, |
|
|
|
|
) |
|
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
<template> |
|
|
|
@ -215,7 +221,12 @@ watch(
|
|
|
|
|
|
|
|
|
|
<!-- Sub Tabs --> |
|
|
|
|
<a-layout-content class="h-auto px-4 scrollbar-thumb-gray-500"> |
|
|
|
|
<a-menu v-model:selectedKeys="selectedSubTabKeys" :open-keys="[]" mode="horizontal"> |
|
|
|
|
<a-menu |
|
|
|
|
v-if="selectedTabKeys[0] !== 'dataSources'" |
|
|
|
|
v-model:selectedKeys="selectedSubTabKeys" |
|
|
|
|
:open-keys="[]" |
|
|
|
|
mode="horizontal" |
|
|
|
|
> |
|
|
|
|
<a-menu-item |
|
|
|
|
v-for="(tab, key) of selectedTab.subTabs" |
|
|
|
|
:key="key" |
|
|
|
@ -225,8 +236,52 @@ watch(
|
|
|
|
|
{{ tab.title }} |
|
|
|
|
</a-menu-item> |
|
|
|
|
</a-menu> |
|
|
|
|
<div v-else> |
|
|
|
|
<div class="flex items-center"> |
|
|
|
|
<a-breadcrumb class="w-full cursor-pointer"> |
|
|
|
|
<a-breadcrumb-item v-if="dataSourcesState !== ''" @click="dataSourcesState = ''"> |
|
|
|
|
<a class="!no-underline">Data Sources</a> |
|
|
|
|
</a-breadcrumb-item> |
|
|
|
|
<a-breadcrumb-item v-else @click="dataSourcesState = ''">Data Sources</a-breadcrumb-item> |
|
|
|
|
<a-breadcrumb-item v-if="dataSourcesState !== ''">{{ dataSourcesState }}</a-breadcrumb-item> |
|
|
|
|
</a-breadcrumb> |
|
|
|
|
<div v-if="dataSourcesState === ''" class="flex flex-row justify-end items-center w-full"> |
|
|
|
|
<a-button class="self-start nc-btn-new-datasource" @click="dataSourcesState = DataSourcesSubTab.New"> |
|
|
|
|
<div v-if="dataSourcesState === ''" class="flex items-center gap-2 text-gray-600 font-light"> |
|
|
|
|
<MdiDatabaseOutline class="text-lg group-hover:text-accent" /> |
|
|
|
|
New |
|
|
|
|
</div> |
|
|
|
|
</a-button> |
|
|
|
|
<!-- Reload --> |
|
|
|
|
<a-button |
|
|
|
|
v-e="['a:proj-meta:data-sources:reload']" |
|
|
|
|
class="self-start nc-btn-metasync-reload" |
|
|
|
|
@click="dataSourcesReload = true" |
|
|
|
|
> |
|
|
|
|
<div class="flex items-center gap-2 text-gray-600 font-light"> |
|
|
|
|
<MdiReload :class="{ 'animate-infinite animate-spin !text-success': dataSourcesReload }" /> |
|
|
|
|
{{ $t('general.reload') }} |
|
|
|
|
</div> |
|
|
|
|
</a-button> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
<a-divider style="margin: 10px 0" /> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<component :is="selectedSubTab?.body" class="px-2 py-6" :data-testid="`nc-settings-subtab-${selectedSubTab.title}`" /> |
|
|
|
|
<component |
|
|
|
|
:is="selectedSubTab?.body" |
|
|
|
|
v-if="selectedSubTabKeys[0] === 'dataSources'" |
|
|
|
|
v-model:state="dataSourcesState" |
|
|
|
|
v-model:reload="dataSourcesReload" |
|
|
|
|
class="px-2 pb-2" |
|
|
|
|
:data-testid="`nc-settings-subtab-${selectedSubTab.title}`" |
|
|
|
|
/> |
|
|
|
|
<component |
|
|
|
|
:is="selectedSubTab?.body" |
|
|
|
|
v-else |
|
|
|
|
class="px-2 py-6" |
|
|
|
|
:data-testid="`nc-settings-subtab-${selectedSubTab.title}`" |
|
|
|
|
/> |
|
|
|
|
</a-layout-content> |
|
|
|
|
</a-layout> |
|
|
|
|
</a-modal> |
|
|
|
|