mirror of
https://github.com/nocodb/nocodb.git
synced 2026-04-30 03:56:51 +00:00
feat: team & settings modal data sources tab revised
Signed-off-by: mertmit <mertmit99@gmail.com>
This commit is contained in:
@@ -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,60 +78,33 @@ const tabsInfo: TabGroup = {
|
||||
$e('c:settings:team-auth')
|
||||
},
|
||||
},
|
||||
...(isUIAllowed('appStore')
|
||||
? {
|
||||
appStore: {
|
||||
// App Store
|
||||
title: t('title.appStore'),
|
||||
icon: StoreFrontOutline,
|
||||
subTabs: {
|
||||
new: {
|
||||
title: 'Apps',
|
||||
body: resolveComponent('DashboardSettingsAppStore'),
|
||||
},
|
||||
},
|
||||
onClick: () => {
|
||||
$e('c:settings:appstore')
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
projMetaData: {
|
||||
// Project Metadata
|
||||
title: t('title.projMeta'),
|
||||
appStore: {
|
||||
// App Store
|
||||
title: t('title.appStore'),
|
||||
icon: StoreFrontOutline,
|
||||
subTabs: {
|
||||
new: {
|
||||
title: 'Apps',
|
||||
body: AppStore,
|
||||
},
|
||||
},
|
||||
onClick: () => {
|
||||
$e('c:settings:appstore')
|
||||
},
|
||||
},
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user