Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a9efa04b0 | |||
| b3e0c00c87 | |||
| 4f5ea8c442 | |||
| a4c467d54e |
@@ -1,7 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed, watch } from 'vue';
|
||||||
import { useLayoutStore, type MenuMode } from '@KTXC/stores/layoutStore';
|
import { useLayoutStore, type MenuMode } from '@KTXC/stores/layoutStore';
|
||||||
import { useIntegrationStore } from '@KTXC/stores/integrationStore';
|
import { useIntegrationStore } from '@KTXC/stores/integrationStore';
|
||||||
|
import type { IntegrationPointType } from '@KTXC/types/integrationTypes';
|
||||||
import { useL10n } from '@KTXC/composables/useL10n';
|
import { useL10n } from '@KTXC/composables/useL10n';
|
||||||
import Logo from '@KTXC/layouts/logo/LogoDark.vue';
|
import Logo from '@KTXC/layouts/logo/LogoDark.vue';
|
||||||
import SystemMenuGroupStatic from './LayoutSystemMenuGroupStatic.vue';
|
import SystemMenuGroupStatic from './LayoutSystemMenuGroupStatic.vue';
|
||||||
@@ -12,25 +13,33 @@ const layoutStore = useLayoutStore();
|
|||||||
const integrationStore = useIntegrationStore();
|
const integrationStore = useIntegrationStore();
|
||||||
const { t } = useL10n('core');
|
const { t } = useL10n('core');
|
||||||
|
|
||||||
|
const menuPointByMode: Record<MenuMode, IntegrationPointType> = {
|
||||||
|
apps: 'app_menu',
|
||||||
|
'user-settings': 'user_settings_menu',
|
||||||
|
'admin-settings': 'admin_settings_menu',
|
||||||
|
};
|
||||||
|
|
||||||
// Get all entries based on current menu mode
|
// Get all entries based on current menu mode
|
||||||
const menuEntries = computed(() => {
|
const menuEntries = computed(() => integrationStore.getPoint(menuPointByMode[layoutStore.menuMode]));
|
||||||
switch (layoutStore.menuMode) {
|
|
||||||
case 'user-settings':
|
// Only show menu modes that currently have visible items.
|
||||||
return integrationStore.getPoint('user_settings_menu');
|
const menuModes = computed((): Array<{ value: MenuMode; icon: string; label: string }> => {
|
||||||
case 'admin-settings':
|
const modes: Array<{ value: MenuMode; icon: string; label: string }> = [
|
||||||
return integrationStore.getPoint('admin_settings_menu');
|
{ value: 'apps', icon: 'mdi-view-dashboard', label: t('systemMenu.apps', 'Applications') },
|
||||||
case 'apps':
|
{ value: 'user-settings', icon: 'mdi-account-cog', label: t('systemMenu.personalSettings', 'Settings') },
|
||||||
default:
|
{ value: 'admin-settings', icon: 'mdi-shield-crown', label: t('systemMenu.adminSettings', 'System') },
|
||||||
return integrationStore.getPoint('app_menu');
|
];
|
||||||
}
|
|
||||||
|
return modes.filter(mode => integrationStore.getPoint(menuPointByMode[mode.value]).length > 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Static list of menu modes shown as icon buttons
|
// If the active menu becomes empty (for example, after a module is disabled),
|
||||||
const menuModes = computed((): Array<{ value: MenuMode; icon: string; label: string }> => [
|
// move to the first menu that still has content.
|
||||||
{ value: 'apps', icon: 'mdi-view-dashboard', label: t('systemMenu.apps', 'Applications') },
|
watch(menuModes, (availableModes) => {
|
||||||
{ value: 'user-settings', icon: 'mdi-account-cog', label: t('systemMenu.personalSettings', 'Settings') },
|
if (availableModes.length > 0 && !availableModes.some(mode => mode.value === layoutStore.menuMode)) {
|
||||||
{ value: 'admin-settings', icon: 'mdi-shield-crown', label: t('systemMenu.adminSettings', 'System') },
|
layoutStore.setMenuMode(availableModes[0].value);
|
||||||
]);
|
}
|
||||||
|
}, { immediate: true });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -84,28 +93,30 @@ export default {
|
|||||||
|
|
||||||
<!-- Menu Mode Switcher -->
|
<!-- Menu Mode Switcher -->
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<v-divider />
|
<div v-if="menuModes.length">
|
||||||
<div class="menu-mode-switcher d-flex justify-space-around align-center py-2">
|
<v-divider />
|
||||||
<v-tooltip
|
<div class="menu-mode-switcher d-flex justify-space-around align-center py-2">
|
||||||
v-for="mode in menuModes"
|
<v-tooltip
|
||||||
:key="mode.value"
|
v-for="mode in menuModes"
|
||||||
location="right"
|
:key="mode.value"
|
||||||
>
|
location="right"
|
||||||
<template v-slot:activator="{ props }">
|
>
|
||||||
<v-btn
|
<template v-slot:activator="{ props }">
|
||||||
v-bind="props"
|
<v-btn
|
||||||
icon
|
v-bind="props"
|
||||||
variant="text"
|
icon
|
||||||
density="comfortable"
|
variant="text"
|
||||||
:color="layoutStore.menuMode === mode.value ? 'primary' : undefined"
|
density="comfortable"
|
||||||
:class="['menu-mode-btn', { 'menu-mode-btn--active': layoutStore.menuMode === mode.value }]"
|
:color="layoutStore.menuMode === mode.value ? 'primary' : undefined"
|
||||||
@click="layoutStore.setMenuMode(mode.value)"
|
:class="['menu-mode-btn', { 'menu-mode-btn--active': layoutStore.menuMode === mode.value }]"
|
||||||
>
|
@click="layoutStore.setMenuMode(mode.value)"
|
||||||
<v-icon>{{ mode.icon }}</v-icon>
|
>
|
||||||
</v-btn>
|
<v-icon>{{ mode.icon }}</v-icon>
|
||||||
</template>
|
</v-btn>
|
||||||
<span>{{ mode.label }}</span>
|
</template>
|
||||||
</v-tooltip>
|
<span>{{ mode.label }}</span>
|
||||||
|
</v-tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
|
|||||||
@@ -3,13 +3,11 @@ import { computed } from 'vue';
|
|||||||
import { useUserStore } from '@KTXC/stores/userStore';
|
import { useUserStore } from '@KTXC/stores/userStore';
|
||||||
import { useIntegrationStore } from '@KTXC/stores/integrationStore';
|
import { useIntegrationStore } from '@KTXC/stores/integrationStore';
|
||||||
import { useLayoutStore } from '@KTXC/stores/layoutStore';
|
import { useLayoutStore } from '@KTXC/stores/layoutStore';
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import { useL10n, t as tGlobal } from '@KTXC/composables/useL10n';
|
import { useL10n, t as tGlobal } from '@KTXC/composables/useL10n';
|
||||||
import defaultAvatar from '@KTXC/assets/images/users/avatar-1.png';
|
import defaultAvatar from '@KTXC/assets/images/users/avatar-1.png';
|
||||||
|
|
||||||
const { t } = useL10n('core');
|
const { t } = useL10n('core');
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const integrationStore = useIntegrationStore();
|
const integrationStore = useIntegrationStore();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
@@ -43,12 +41,6 @@ const cycleTheme = () => {
|
|||||||
layoutStore.setTheme(nextThemeMode.value);
|
layoutStore.setTheme(nextThemeMode.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Navigate to settings
|
|
||||||
const goToSettings = () => {
|
|
||||||
layoutStore.setMenuMode('settings');
|
|
||||||
// Navigate to first settings item or a default settings route
|
|
||||||
router.push('/modules'); // TODO: Make this dynamic based on first settings menu item
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -98,14 +90,6 @@ const goToSettings = () => {
|
|||||||
<v-list-item-title class="text-h6">{{ t(MODE_PRESENTATION[nextThemeMode].l10n, MODE_PRESENTATION[nextThemeMode].label) }}</v-list-item-title>
|
<v-list-item-title class="text-h6">{{ t(MODE_PRESENTATION[nextThemeMode].l10n, MODE_PRESENTATION[nextThemeMode].label) }}</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
|
||||||
<!-- Go to Settings -->
|
|
||||||
<v-list-item @click="goToSettings" color="primary" rounded="0">
|
|
||||||
<template v-slot:prepend>
|
|
||||||
<v-icon>mdi-cog-outline</v-icon>
|
|
||||||
</template>
|
|
||||||
<v-list-item-title class="text-h6">{{ t('userMenu.settings', 'Settings') }}</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
|
|
||||||
<v-divider class="my-2" />
|
<v-divider class="my-2" />
|
||||||
|
|
||||||
<!-- Logout -->
|
<!-- Logout -->
|
||||||
|
|||||||
@@ -156,6 +156,10 @@ export const useIntegrationStore = defineStore('integrationStore', {
|
|||||||
|
|
||||||
return Array.from(point.items.values())
|
return Array.from(point.items.values())
|
||||||
.filter(entry => entry.visible !== false)
|
.filter(entry => entry.visible !== false)
|
||||||
|
.map(entry => 'items' in entry
|
||||||
|
? { ...entry, items: entry.items.filter(item => item.visible !== false) }
|
||||||
|
: entry)
|
||||||
|
.filter(entry => !('items' in entry) || entry.items.length > 0)
|
||||||
.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
|
.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const useLayoutStore = defineStore('layout', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sidebarDrawer = ref<boolean>(booleanSetting('sidebar_drawer', true));
|
const sidebarDrawer = ref<boolean>(booleanSetting('sidebar_drawer', true));
|
||||||
const miniSidebar = ref<boolean>(booleanSetting('mini_sidebar', false));
|
const miniSidebar = ref<boolean>(booleanSetting('mini_sidebar', true));
|
||||||
const menuMode = ref<MenuMode>('apps');
|
const menuMode = ref<MenuMode>('apps');
|
||||||
|
|
||||||
// Theme mode - user choice, falling back to the tenant default
|
// Theme mode - user choice, falling back to the tenant default
|
||||||
@@ -60,7 +60,7 @@ export const useLayoutStore = defineStore('layout', () => {
|
|||||||
hydratingFromSettings = true;
|
hydratingFromSettings = true;
|
||||||
try {
|
try {
|
||||||
sidebarDrawer.value = booleanSetting('sidebar_drawer', true);
|
sidebarDrawer.value = booleanSetting('sidebar_drawer', true);
|
||||||
miniSidebar.value = booleanSetting('mini_sidebar', false);
|
miniSidebar.value = booleanSetting('mini_sidebar', true);
|
||||||
theme.value = initialTheme();
|
theme.value = initialTheme();
|
||||||
} finally {
|
} finally {
|
||||||
hydratingFromSettings = false;
|
hydratingFromSettings = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user