Merge pull request #729 from touwaeriol/pr/fix-admin-menu-visibility

fix(frontend): admin custom menu items not showing in sidebar
This commit is contained in:
Wesley Liddick
2026-03-03 11:35:59 +08:00
committed by GitHub
3 changed files with 33 additions and 9 deletions

View File

@@ -579,7 +579,7 @@ const customMenuItemsForUser = computed(() => {
}) })
const customMenuItemsForAdmin = computed(() => { const customMenuItemsForAdmin = computed(() => {
const items = appStore.cachedPublicSettings?.custom_menu_items ?? [] const items = adminSettingsStore.customMenuItems ?? []
return items return items
.filter((item) => item.visibility === 'admin') .filter((item) => item.visibility === 'admin')
.sort((a, b) => a.sort_order - b.sort_order) .sort((a, b) => a.sort_order - b.sort_order)

View File

@@ -1,6 +1,7 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue' import { ref } from 'vue'
import { adminAPI } from '@/api' import { adminAPI } from '@/api'
import type { CustomMenuItem } from '@/types'
export const useAdminSettingsStore = defineStore('adminSettings', () => { export const useAdminSettingsStore = defineStore('adminSettings', () => {
const loaded = ref(false) const loaded = ref(false)
@@ -43,6 +44,9 @@ export const useAdminSettingsStore = defineStore('adminSettings', () => {
} }
} }
// Custom menu items (all items including admin-only, loaded from admin settings API)
const customMenuItems = ref<CustomMenuItem[]>([])
// Default open, but honor cached value to reduce UI flicker on first paint. // Default open, but honor cached value to reduce UI flicker on first paint.
const opsMonitoringEnabled = ref(readCachedBool('ops_monitoring_enabled_cached', true)) const opsMonitoringEnabled = ref(readCachedBool('ops_monitoring_enabled_cached', true))
const opsRealtimeMonitoringEnabled = ref(readCachedBool('ops_realtime_monitoring_enabled_cached', true)) const opsRealtimeMonitoringEnabled = ref(readCachedBool('ops_realtime_monitoring_enabled_cached', true))
@@ -64,6 +68,8 @@ export const useAdminSettingsStore = defineStore('adminSettings', () => {
opsQueryModeDefault.value = settings.ops_query_mode_default || 'auto' opsQueryModeDefault.value = settings.ops_query_mode_default || 'auto'
writeCachedString('ops_query_mode_default_cached', opsQueryModeDefault.value) writeCachedString('ops_query_mode_default_cached', opsQueryModeDefault.value)
customMenuItems.value = settings.custom_menu_items ?? []
loaded.value = true loaded.value = true
} catch (err) { } catch (err) {
// Keep cached/default value: do not "flip" the UI based on a transient fetch failure. // Keep cached/default value: do not "flip" the UI based on a transient fetch failure.
@@ -122,6 +128,7 @@ export const useAdminSettingsStore = defineStore('adminSettings', () => {
opsMonitoringEnabled, opsMonitoringEnabled,
opsRealtimeMonitoringEnabled, opsRealtimeMonitoringEnabled,
opsQueryModeDefault, opsQueryModeDefault,
customMenuItems,
fetch, fetch,
setOpsMonitoringEnabledLocal, setOpsMonitoringEnabledLocal,
setOpsRealtimeMonitoringEnabledLocal, setOpsRealtimeMonitoringEnabledLocal,

View File

@@ -70,6 +70,7 @@ import { useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores' import { useAppStore } from '@/stores'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import { useAdminSettingsStore } from '@/stores/adminSettings'
import AppLayout from '@/components/layout/AppLayout.vue' import AppLayout from '@/components/layout/AppLayout.vue'
import Icon from '@/components/icons/Icon.vue' import Icon from '@/components/icons/Icon.vue'
import { buildEmbeddedUrl, detectTheme } from '@/utils/embedded-url' import { buildEmbeddedUrl, detectTheme } from '@/utils/embedded-url'
@@ -78,6 +79,7 @@ const { t } = useI18n()
const route = useRoute() const route = useRoute()
const appStore = useAppStore() const appStore = useAppStore()
const authStore = useAuthStore() const authStore = useAuthStore()
const adminSettingsStore = useAdminSettingsStore()
const loading = ref(false) const loading = ref(false)
const pageTheme = ref<'light' | 'dark'>('light') const pageTheme = ref<'light' | 'dark'>('light')
@@ -86,8 +88,15 @@ let themeObserver: MutationObserver | null = null
const menuItemId = computed(() => route.params.id as string) const menuItemId = computed(() => route.params.id as string)
const menuItem = computed(() => { const menuItem = computed(() => {
const items = appStore.cachedPublicSettings?.custom_menu_items ?? [] const publicItems = appStore.cachedPublicSettings?.custom_menu_items ?? []
const found = items.find((item) => item.id === menuItemId.value) ?? null const adminItems = authStore.isAdmin ? (adminSettingsStore.customMenuItems ?? []) : []
const allItems = [...publicItems]
for (const item of adminItems) {
if (!allItems.some((existing) => existing.id === item.id)) {
allItems.push(item)
}
}
const found = allItems.find((item) => item.id === menuItemId.value) ?? null
if (found && found.visibility === 'admin' && !authStore.isAdmin) { if (found && found.visibility === 'admin' && !authStore.isAdmin) {
return null return null
} }
@@ -122,13 +131,21 @@ onMounted(async () => {
}) })
} }
if (appStore.publicSettingsLoaded) return const promises: Promise<unknown>[] = []
if (!appStore.publicSettingsLoaded) {
promises.push(appStore.fetchPublicSettings())
}
if (authStore.isAdmin) {
promises.push(adminSettingsStore.fetch())
}
if (promises.length > 0) {
loading.value = true loading.value = true
try { try {
await appStore.fetchPublicSettings() await Promise.all(promises)
} finally { } finally {
loading.value = false loading.value = false
} }
}
}) })
onUnmounted(() => { onUnmounted(() => {