1 Commits

Author SHA1 Message Date
Sebastian 4567b08c42 chore(deps): update dependency vue-i18n to v11.4.8
JS Unit Tests / test (pull_request) Successful in 24s
Build Test / build (pull_request) Successful in 24s
PHP Unit Tests / test (pull_request) Successful in 1m40s
PHP Integration Tests / Integration Tests (pull_request) Failing after 1m46s
2026-08-10 03:01:54 +00:00
5 changed files with 81 additions and 80 deletions
+39 -50
View File
@@ -1,8 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, watch } from 'vue'; import { computed } 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';
@@ -13,33 +12,25 @@ 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(() => integrationStore.getPoint(menuPointByMode[layoutStore.menuMode])); const menuEntries = computed(() => {
switch (layoutStore.menuMode) {
// Only show menu modes that currently have visible items. case 'user-settings':
const menuModes = computed((): Array<{ value: MenuMode; icon: string; label: string }> => { return integrationStore.getPoint('user_settings_menu');
const modes: Array<{ value: MenuMode; icon: string; label: string }> = [ case 'admin-settings':
{ value: 'apps', icon: 'mdi-view-dashboard', label: t('systemMenu.apps', 'Applications') }, return integrationStore.getPoint('admin_settings_menu');
{ value: 'user-settings', icon: 'mdi-account-cog', label: t('systemMenu.personalSettings', 'Settings') }, case 'apps':
{ value: 'admin-settings', icon: 'mdi-shield-crown', label: t('systemMenu.adminSettings', 'System') }, default:
]; return integrationStore.getPoint('app_menu');
}
return modes.filter(mode => integrationStore.getPoint(menuPointByMode[mode.value]).length > 0);
}); });
// If the active menu becomes empty (for example, after a module is disabled), // Static list of menu modes shown as icon buttons
// move to the first menu that still has content. const menuModes = computed((): Array<{ value: MenuMode; icon: string; label: string }> => [
watch(menuModes, (availableModes) => { { value: 'apps', icon: 'mdi-view-dashboard', label: t('systemMenu.apps', 'Applications') },
if (availableModes.length > 0 && !availableModes.some(mode => mode.value === layoutStore.menuMode)) { { value: 'user-settings', icon: 'mdi-account-cog', label: t('systemMenu.personalSettings', 'Settings') },
layoutStore.setMenuMode(availableModes[0].value); { value: 'admin-settings', icon: 'mdi-shield-crown', label: t('systemMenu.adminSettings', 'System') },
} ]);
}, { immediate: true });
</script> </script>
<script lang="ts"> <script lang="ts">
@@ -93,30 +84,28 @@ export default {
<!-- Menu Mode Switcher --> <!-- Menu Mode Switcher -->
<template v-slot:append> <template v-slot:append>
<div v-if="menuModes.length"> <v-divider />
<v-divider /> <div class="menu-mode-switcher d-flex justify-space-around align-center py-2">
<div class="menu-mode-switcher d-flex justify-space-around align-center py-2"> <v-tooltip
<v-tooltip v-for="mode in menuModes"
v-for="mode in menuModes" :key="mode.value"
:key="mode.value" location="right"
location="right" >
> <template v-slot:activator="{ props }">
<template v-slot:activator="{ props }"> <v-btn
<v-btn v-bind="props"
v-bind="props" icon
icon variant="text"
variant="text" density="comfortable"
density="comfortable" :color="layoutStore.menuMode === mode.value ? 'primary' : undefined"
:color="layoutStore.menuMode === mode.value ? 'primary' : undefined" :class="['menu-mode-btn', { 'menu-mode-btn--active': layoutStore.menuMode === mode.value }]"
:class="['menu-mode-btn', { 'menu-mode-btn--active': layoutStore.menuMode === mode.value }]" @click="layoutStore.setMenuMode(mode.value)"
@click="layoutStore.setMenuMode(mode.value)" >
> <v-icon>{{ mode.icon }}</v-icon>
<v-icon>{{ mode.icon }}</v-icon> </v-btn>
</v-btn> </template>
</template> <span>{{ mode.label }}</span>
<span>{{ mode.label }}</span> </v-tooltip>
</v-tooltip>
</div>
</div> </div>
</template> </template>
</v-navigation-drawer> </v-navigation-drawer>
+16
View File
@@ -3,11 +3,13 @@ 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();
@@ -41,6 +43,12 @@ 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>
@@ -90,6 +98,14 @@ const cycleTheme = () => {
<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 -->
-4
View File
@@ -156,10 +156,6 @@ 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));
}, },
+2 -2
View File
@@ -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', true)); const miniSidebar = ref<boolean>(booleanSetting('mini_sidebar', false));
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', true); miniSidebar.value = booleanSetting('mini_sidebar', false);
theme.value = initialTheme(); theme.value = initialTheme();
} finally { } finally {
hydratingFromSettings = false; hydratingFromSettings = false;
+24 -24
View File
@@ -642,14 +642,14 @@
} }
}, },
"node_modules/@intlify/core-base": { "node_modules/@intlify/core-base": {
"version": "11.4.7", "version": "11.4.8",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.7.tgz", "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.8.tgz",
"integrity": "sha512-MSB/sBKwEWJTILvQIhg2rnIcwPpLayo3wGwvVA+dJTNeUBD9GoqQgAaSOLdI9iOPDHCm9YoVnLqpfzza98MpkQ==", "integrity": "sha512-A+Q7SKm5oEcy1E/cghqd7n/St4XjTqLhiiyDuieNcMrJcrHlkY5n0jp7Q9dD3txvVHzvsmBVV5M9wD5/s1zfzw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@intlify/devtools-types": "11.4.7", "@intlify/devtools-types": "11.4.8",
"@intlify/message-compiler": "11.4.7", "@intlify/message-compiler": "11.4.8",
"@intlify/shared": "11.4.7" "@intlify/shared": "11.4.8"
}, },
"engines": { "engines": {
"node": ">= 22" "node": ">= 22"
@@ -659,13 +659,13 @@
} }
}, },
"node_modules/@intlify/devtools-types": { "node_modules/@intlify/devtools-types": {
"version": "11.4.7", "version": "11.4.8",
"resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.7.tgz", "resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.8.tgz",
"integrity": "sha512-GSz+J+hqH+AEpAHIYya6fSufS30OaMnG39HiZX7DmGKi3+aaLvassCfsXENEc4Wr4m68q2YP0QdMdB3D9UeAXg==", "integrity": "sha512-MGpID+rlfzGUbNcnC20bm5NMSBHPrvx0atLTfv9dftn3kjXw1hGKDcIcwrO99tSrZEc2i+hczRL7ks8qXsHPkQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@intlify/core-base": "11.4.7", "@intlify/core-base": "11.4.8",
"@intlify/shared": "11.4.7" "@intlify/shared": "11.4.8"
}, },
"engines": { "engines": {
"node": ">= 22" "node": ">= 22"
@@ -675,12 +675,12 @@
} }
}, },
"node_modules/@intlify/message-compiler": { "node_modules/@intlify/message-compiler": {
"version": "11.4.7", "version": "11.4.8",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.7.tgz", "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.8.tgz",
"integrity": "sha512-bHxmh7n94N4N1evADeb7XTkc3jTw6Ki5biMFZVSX6Jmk+iehy8/maeH2XUsBI27rtKIK+Hzc6QnVAKggUwylKw==", "integrity": "sha512-vbzk17dYwduYiv52EK61+FDCyhfVg1uPUtPmiD/d45W99uJIcXywrweOBcHv7n9/iEqmXiMGT52bgJbZDQqK3w==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@intlify/shared": "11.4.7", "@intlify/shared": "11.4.8",
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
}, },
"engines": { "engines": {
@@ -691,9 +691,9 @@
} }
}, },
"node_modules/@intlify/shared": { "node_modules/@intlify/shared": {
"version": "11.4.7", "version": "11.4.8",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.7.tgz", "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.8.tgz",
"integrity": "sha512-OtjPZan3No2OZZFnMUiCVsXC6+j+XRwEywaFDk0AoayAbLuPesyDloXhJZLl9JUl5vHZeQUkYSbEA8VX+CWMjg==", "integrity": "sha512-XbRgrv+XEuvDr7UCY55oibVrh+o4u+A0VB6nSL0F5Z8LcZxE/8j573LYG6bCrOigIcHdGpSNI7Rh5UpC5/B/eg==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 22" "node": ">= 22"
@@ -6537,14 +6537,14 @@
} }
}, },
"node_modules/vue-i18n": { "node_modules/vue-i18n": {
"version": "11.4.7", "version": "11.4.8",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.7.tgz", "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.8.tgz",
"integrity": "sha512-j6RyshdPPzqLiMAUpnpvZGFPM+rRoWi14Sl5yTsquvoW0/56DWyvhAj2o9TO2YXGvb6teg8T0xrYO9jR3urvdw==", "integrity": "sha512-0ULeHP6Z9CGvAm67S77ZEp41cfGXIREGL8qfhos2BMgcQQewtQcDKuojt6jjasAD/S8GwfTp2ySPmDSpwvrCMQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@intlify/core-base": "11.4.7", "@intlify/core-base": "11.4.8",
"@intlify/devtools-types": "11.4.7", "@intlify/devtools-types": "11.4.8",
"@intlify/shared": "11.4.7", "@intlify/shared": "11.4.8",
"@vue/devtools-api": "^6.5.0" "@vue/devtools-api": "^6.5.0"
}, },
"engines": { "engines": {