From 4be88dfd455326b7eac2b901da683a8c15d2ffb5 Mon Sep 17 00:00:00 2001 From: Thomas Peterson Date: Sat, 6 Jun 2026 08:53:20 +0200 Subject: [PATCH] UI: Zwei-Ebenen-Navigation (Portal/Reseller oben, Firma links) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Klare Trennung der Ebenen gegen Verwechslung: Topbar trägt die Portal-/Reseller- Navigation (Dashboard, Reseller, Firmen, Produkte, Bestellungen, Einstellungen) + Level-Badge (PORTAL/RESELLER/Firmenname). Die linke Sidebar zeigt NUR Firmen- Ebene (Mitarbeiter, Editor, Bestellungen, Standorte, Domains, Design, Wallet, Einstellungen) und nur im Firmen-Kontext (Firmen-Admin oder via „Einloggen als"). Reseller/Portal-Admin → links leer, Inhalt volle Breite. Co-Authored-By: Claude Opus 4.8 --- frontend/src/layouts/DashboardLayout.vue | 200 +++++++++++++---------- 1 file changed, 115 insertions(+), 85 deletions(-) diff --git a/frontend/src/layouts/DashboardLayout.vue b/frontend/src/layouts/DashboardLayout.vue index 7cc091a..16e3790 100644 --- a/frontend/src/layouts/DashboardLayout.vue +++ b/frontend/src/layouts/DashboardLayout.vue @@ -9,24 +9,40 @@ const router = useRouter() interface NavItem { label: string; to: string; icon: string; show: boolean } -const nav = computed(() => [ - { label: 'Dashboard', to: '/app', icon: 'M3 3h7v7H3zM14 3h7v7h-7zM14 14h7v7h-7zM3 14h7v7H3z', show: true }, - { label: 'Reseller', to: '/app/resellers', icon: 'M3 21h18M5 21V7l8-4v18M19 21V11l-6-3', show: auth.isPlatformAdmin }, - { label: 'Firmen', to: '/app/companies', icon: 'M3 7h18v13H3zM8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2', show: auth.isResellerAdmin || auth.isPlatformAdmin }, - { label: 'Mitarbeiter', to: '/app/employees', icon: 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z', show: true }, - { label: 'Produkte', to: '/app/products', icon: 'M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16zM3.27 6.96 12 12.01l8.73-5.05M12 22.08V12', show: auth.isResellerAdmin || auth.isPlatformAdmin }, - { label: 'Editor', to: '/app/card-editor', icon: 'M3 5h18v14H3zM3 10h18M7 15h5', show: auth.isResellerAdmin || auth.isCompanyAdmin }, - { label: 'Bestellungen', to: '/app/orders', icon: 'M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4zM3 6h18M16 10a4 4 0 0 1-8 0', show: auth.isCompanyAdmin || auth.isResellerAdmin || auth.isPlatformAdmin }, - { label: 'Standorte', to: '/app/locations', icon: 'M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0zM12 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6z', show: auth.isCompanyAdmin }, - { label: 'Domains', to: '/app/domains', icon: 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM3 12h18M12 3a15 15 0 0 1 0 18 15 15 0 0 1 0-18z', show: auth.isCompanyAdmin }, - { label: 'Design', to: '/app/design', icon: 'M12 2l7 7a7 7 0 1 1-14 0z', show: auth.isCompanyAdmin }, - { label: 'Wallet', to: '/app/wallet', icon: 'M3 7h18v12H3zM3 10h18M16 14h2', show: auth.isCompanyAdmin }, - { label: 'Einstellungen', to: '/app/settings', icon: 'M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6', show: true }, -].filter((i) => i.show)) +// Portal-/Reseller-Ebene → Topbar +const topNav = computed(() => { + const above = auth.isResellerAdmin || auth.isPlatformAdmin + return [ + { label: 'Dashboard', to: '/app', icon: 'M3 3h7v7H3zM14 3h7v7h-7zM14 14h7v7h-7zM3 14h7v7H3z', show: above }, + { label: 'Reseller', to: '/app/resellers', icon: 'M3 21h18M5 21V7l8-4v18M19 21V11l-6-3', show: auth.isPlatformAdmin }, + { label: 'Firmen', to: '/app/companies', icon: 'M3 7h18v13H3zM8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2', show: above }, + { label: 'Produkte', to: '/app/products', icon: 'M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16zM3.27 6.96 12 12.01l8.73-5.05M12 22.08V12', show: above }, + { label: 'Bestellungen', to: '/app/orders', icon: 'M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4zM3 6h18M16 10a4 4 0 0 1-8 0', show: above }, + { label: 'Einstellungen', to: '/app/settings', icon: 'M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6', show: above }, + ].filter((i) => i.show) +}) -const contextLabel = computed(() => - auth.user?.company?.name ?? auth.user?.reseller?.name ?? 'Plattform', -) +// Firmen-Ebene → linke Sidebar (nur im Firmen-Kontext, auch via „Einloggen als") +const leftNav = computed(() => { + const inCompany = auth.isCompanyAdmin + return [ + { label: 'Dashboard', to: '/app', icon: 'M3 3h7v7H3zM14 3h7v7h-7zM14 14h7v7h-7zM3 14h7v7H3z', show: inCompany }, + { label: 'Mitarbeiter', to: '/app/employees', icon: 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z', show: inCompany }, + { label: 'Editor', to: '/app/card-editor', icon: 'M3 5h18v14H3zM3 10h18M7 15h5', show: inCompany }, + { label: 'Bestellungen', to: '/app/orders', icon: 'M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4zM3 6h18M16 10a4 4 0 0 1-8 0', show: inCompany }, + { label: 'Standorte', to: '/app/locations', icon: 'M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0zM12 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6z', show: inCompany }, + { label: 'Domains', to: '/app/domains', icon: 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM3 12h18M12 3a15 15 0 0 1 0 18 15 15 0 0 1 0-18z', show: inCompany }, + { label: 'Design', to: '/app/design', icon: 'M12 2l7 7a7 7 0 1 1-14 0z', show: inCompany }, + { label: 'Wallet', to: '/app/wallet', icon: 'M3 7h18v12H3zM3 10h18M16 14h2', show: inCompany }, + { label: 'Einstellungen', to: '/app/settings', icon: 'M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6', show: inCompany }, + ].filter((i) => i.show) +}) + +const levelLabel = computed(() => { + if (auth.isPlatformAdmin) return 'Portal' + if (auth.isResellerAdmin) return 'Reseller' + return auth.user?.company?.name ?? 'Firma' +}) const userName = computed(() => auth.user?.name || auth.user?.email || '') const initials = computed(() => userName.value.split(/[\s@.]+/).filter(Boolean).slice(0, 2).map((s) => s[0]).join('').toUpperCase() || 'U', @@ -42,7 +58,6 @@ function logout() { auth.logout() router.push('/login') } - async function stopImpersonation() { await auth.stopImpersonation() router.push('/app') @@ -51,93 +66,108 @@ async function stopImpersonation() {