feat(frontend): group dashboard sidebar navigation into sections
Add section-based grouping to the sidebar navigation with headers and dividers. Menu items are now organized into 4 sections: - Allgemein (Übersicht, Zählpunkte, Mein Profil) - Community (Community beitreten, Meine Mitgliedschaften, Verbrauch) - Tarife & Einladungen (Meine Tarife, Einladungen) - Verwaltung (Admin-only: Mitgliedschaften, Benutzerverwaltung, etc.) Only sections with visible items for the current role are rendered. All 107 frontend tests passing.
This commit is contained in:
parent
fb53b169a2
commit
b1f38e752b
@ -21,15 +21,25 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="flex-1 px-4 py-6 space-y-1 overflow-y-auto">
|
<nav class="flex-1 px-4 py-6 space-y-4 overflow-y-auto">
|
||||||
@for (item of visibleNavItems(); track item.path) {
|
@for (group of navSections(); track group.section; let last = $last) {
|
||||||
<a [routerLink]="item.path"
|
<div>
|
||||||
routerLinkActive="bg-emerald-50 text-emerald-700 font-medium"
|
<p class="px-4 mb-1 text-xs font-semibold uppercase tracking-wider text-slate-400">
|
||||||
[routerLinkActiveOptions]="{exact: item.path === '/dashboard'}"
|
{{ group.section }}
|
||||||
(click)="closeMobileMenu()"
|
</p>
|
||||||
class="block px-4 py-2.5 rounded-lg text-slate-600 hover:bg-slate-50 hover:text-slate-900 transition-colors">
|
@for (item of group.items; track item.path) {
|
||||||
{{ item.label }}
|
<a [routerLink]="item.path"
|
||||||
</a>
|
routerLinkActive="bg-emerald-50 text-emerald-700 font-medium"
|
||||||
|
[routerLinkActiveOptions]="{exact: item.path === '/dashboard'}"
|
||||||
|
(click)="closeMobileMenu()"
|
||||||
|
class="block px-4 py-2 rounded-lg text-slate-600 hover:bg-slate-50 hover:text-slate-900 transition-colors">
|
||||||
|
{{ item.label }}
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (!last) {
|
||||||
|
<div class="border-t border-slate-100 mx-4"></div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {Component, computed, inject, signal} from '@angular/core';
|
import {Component, computed, inject, signal} from '@angular/core';
|
||||||
import {Router, RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router';
|
import {Router, RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router';
|
||||||
import {AuthService} from '../../services/auth';
|
import {AuthService} from '../../services/auth';
|
||||||
import {NAV_ITEMS} from './dashboard-nav-items';
|
import {NAV_ITEMS, NAV_SECTIONS, NavItem} from './dashboard-nav-items';
|
||||||
import {NotificationComponent} from '../../components/notification/notification';
|
import {NotificationComponent} from '../../components/notification/notification';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -17,9 +17,17 @@ export class DashboardLayout {
|
|||||||
|
|
||||||
isMobileMenuOpen = signal(false);
|
isMobileMenuOpen = signal(false);
|
||||||
|
|
||||||
visibleNavItems = computed(() => {
|
navSections = computed(() => {
|
||||||
const role = this.authService.currentUserRole();
|
const role = this.authService.currentUserRole();
|
||||||
return NAV_ITEMS.filter(item => role !== null && item.roles.includes(role));
|
if (role === null) return [];
|
||||||
|
|
||||||
|
const visible = NAV_ITEMS.filter(item => item.roles.includes(role));
|
||||||
|
return NAV_SECTIONS
|
||||||
|
.map(section => ({
|
||||||
|
section,
|
||||||
|
items: visible.filter(item => item.section === section)
|
||||||
|
}))
|
||||||
|
.filter(group => group.items.length > 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
toggleMobileMenu() {
|
toggleMobileMenu() {
|
||||||
|
|||||||
@ -2,21 +2,31 @@ export interface NavItem {
|
|||||||
label: string;
|
label: string;
|
||||||
path: string;
|
path: string;
|
||||||
roles: string[];
|
roles: string[];
|
||||||
|
section: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const NAV_SECTIONS = ['Allgemein', 'Community', 'Tarife & Einladungen', 'Verwaltung'] as const;
|
||||||
|
|
||||||
export const NAV_ITEMS: NavItem[] = [
|
export const NAV_ITEMS: NavItem[] = [
|
||||||
{label: 'Übersicht', path: '/dashboard', roles: ['ADMIN', 'MEMBER']},
|
// Allgemein
|
||||||
{label: 'Zählpunkte', path: '/dashboard/metering-points', roles: ['ADMIN', 'MEMBER']},
|
{label: 'Übersicht', path: '/dashboard', roles: ['ADMIN', 'MEMBER'], section: 'Allgemein'},
|
||||||
{label: 'Community beitreten', path: '/dashboard/join-community', roles: ['MEMBER']},
|
{label: 'Zählpunkte', path: '/dashboard/metering-points', roles: ['ADMIN', 'MEMBER'], section: 'Allgemein'},
|
||||||
{label: 'Meine Mitgliedschaften', path: '/dashboard/my-memberships', roles: ['MEMBER']},
|
{label: 'Mein Profil', path: '/dashboard/profile', roles: ['ADMIN', 'MEMBER'], section: 'Allgemein'},
|
||||||
{label: 'Verbrauch', path: '/dashboard/consumption', roles: ['MEMBER']},
|
|
||||||
{label: 'Mitgliedschaften', path: '/dashboard/admin-memberships', roles: ['ADMIN']},
|
// Community
|
||||||
{label: 'Mein Profil', path: '/dashboard/profile', roles: ['ADMIN', 'MEMBER']},
|
{label: 'Community beitreten', path: '/dashboard/join-community', roles: ['MEMBER'], section: 'Community'},
|
||||||
{label: 'Benutzerverwaltung', path: '/dashboard/approvals', roles: ['ADMIN']},
|
{label: 'Meine Mitgliedschaften', path: '/dashboard/my-memberships', roles: ['MEMBER'], section: 'Community'},
|
||||||
{label: 'Energiegemeinschaften', path: '/dashboard/energy-communities', roles: ['ADMIN']},
|
{label: 'Verbrauch', path: '/dashboard/consumption', roles: ['MEMBER'], section: 'Community'},
|
||||||
{label: 'Tarife verwalten', path: '/dashboard/tariffs', roles: ['ADMIN']},
|
|
||||||
{label: 'Messdaten-Upload', path: '/dashboard/upload-metering-data', roles: ['ADMIN']},
|
// Tarife & Einladungen
|
||||||
{label: 'Meine Tarife', path: '/dashboard/my-tariffs', roles: ['MEMBER']},
|
{label: 'Meine Tarife', path: '/dashboard/my-tariffs', roles: ['MEMBER'], section: 'Tarife & Einladungen'},
|
||||||
{label: 'Meine Einladungen', path: '/dashboard/producer-invites', roles: ['MEMBER']},
|
{label: 'Meine Einladungen', path: '/dashboard/producer-invites', roles: ['MEMBER'], section: 'Tarife & Einladungen'},
|
||||||
{label: 'Offene Einladungen', path: '/dashboard/consumer-invites', roles: ['MEMBER']}
|
{label: 'Offene Einladungen', path: '/dashboard/consumer-invites', roles: ['MEMBER'], section: 'Tarife & Einladungen'},
|
||||||
|
|
||||||
|
// Verwaltung
|
||||||
|
{label: 'Mitgliedschaften', path: '/dashboard/admin-memberships', roles: ['ADMIN'], section: 'Verwaltung'},
|
||||||
|
{label: 'Benutzerverwaltung', path: '/dashboard/approvals', roles: ['ADMIN'], section: 'Verwaltung'},
|
||||||
|
{label: 'Energiegemeinschaften', path: '/dashboard/energy-communities', roles: ['ADMIN'], section: 'Verwaltung'},
|
||||||
|
{label: 'Tarife verwalten', path: '/dashboard/tariffs', roles: ['ADMIN'], section: 'Verwaltung'},
|
||||||
|
{label: 'Messdaten-Upload', path: '/dashboard/upload-metering-data', roles: ['ADMIN'], section: 'Verwaltung'},
|
||||||
];
|
];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user