import { useState } from 'react' import { useNavigate } from 'react-router' import { Box, ButtonBase, Divider, IconButton, Popover, Tooltip, Typography } from '@mui/material' import { HelpCircle, LayoutGrid, LogOut, Settings, User } from 'lucide-react' import { useSessionStore } from '../../stores/sessionStore' import { useToastStore } from '../../stores/toastStore' import { AgentAvatar } from '../team' import { AGENT_WORKSPACES } from '../../lib/agentWorkspaces' import { HOME_NAV_LABEL, MY_AGENTS_LABEL, ROUTES } from '../../lib/constants' import { DS_BRAND, DS_NEUTRAL, DS_SLATE, DS_TEXT } from '../../lib/ds' const ROLE_LABELS: Record = { PROPERTY_MANAGER: 'Property Manager', DEMAND_USER: 'Demand User', } /** * Globale Navigation in der Kopfzeile (Runde 6, §11–§16). * * Sie ersetzt die entfernte Seitenleiste und das frühere Profilsymbol. Ein * einziges Menüsymbol neben der Glocke öffnet ein Klappmenü, das rechts an der * Kopfzeile ausgerichtet nach unten aufgeht. * * Aufbau: «Startseite», «Meine Agenten» mit allen Agenten als eingerückte, * rechts ausgerichtete Unterpunkte, und «Profil» mit denselben vier Aktionen, * die vorher im Profilmenü lagen. * * Die Agentenliste kommt aus `AGENT_WORKSPACES` — derselben zentralen * Konfiguration, die auch der Meetingraum und die Seitenköpfe verwenden. Namen, * Funktionen und Ziele werden dadurch an genau einer Stelle gepflegt (§24, * Schritt 7). * * `Popover` statt `Menu`: Ein `Menu` erwartet gleichrangige `MenuItem`-Kinder * und übernimmt Tastaturnavigation über eine flache Liste. Hier gibt es zwei * Ebenen mit unterschiedlicher Ausrichtung — das Popover lässt diese Struktur * zu und bringt Klick-ausserhalb und Escape von sich aus mit. */ export function GlobalNavMenu() { const [anchorEl, setAnchorEl] = useState(null) // Beim Öffnen festgehaltene Ankerstelle: oben unter der Kopfzeile, rechts // exakt an der Fensterkante. const [ankerStelle, setAnkerStelle] = useState({ top: 64, left: 0 }) const { currentUser, logout } = useSessionStore() const navigate = useNavigate() const showToast = useToastStore(s => s.showToast) const open = !!anchorEl const close = () => setAnchorEl(null) /** Menüpunkt und Ziel gehören zusammen: erst schliessen, dann navigieren. */ const go = (path: string) => { close() navigate(path) } const handleLogout = () => { close() logout() navigate('/auth/login') } return ( <> { if (open) { setAnchorEl(null); return } const b = e.currentTarget.getBoundingClientRect() setAnkerStelle({ top: b.bottom + 8, left: document.documentElement.clientWidth }) setAnchorEl(e.currentTarget) }} aria-label="Menü öffnen" aria-haspopup="true" aria-expanded={open} sx={{ color: DS_TEXT.primary }} > go(ROUTES.SUPPLY.PROPERTIES)} /> go(ROUTES.SUPPLY.TEAM)} /> {/* Unterpunkte: kleiner, eingerückt und rechts ausgerichtet (§14). */} {AGENT_WORKSPACES.map(agent => ( go(agent.path)}> {agent.name} {agent.role} ))} showProfileHint(showToast, close)} /> {currentUser && ( {currentUser.name} · {ROLE_LABELS[currentUser.role] ?? currentUser.role} {currentUser.organizationName ? ` · ${currentUser.organizationName}` : ''} )} showProfileHint(showToast, close)}> } label="Profil" /> { close(); showToast('Einstellungen sind in Kürze verfügbar.', 'info') }}> } label="Einstellungen" /> { // Bestehende Logik: die Tour zeigt sich erneut, sobald die Merkung // entfernt ist. Nicht neu gebaut (§16). close() localStorage.removeItem('property_match_welcomed') window.location.reload() }} > } label="Produkttour starten" /> } label="Abmelden" /> ) } function showProfileHint(showToast: (m: string, t: 'info') => void, close: () => void) { close() showToast('Profilseite ist in Kürze verfügbar.', 'info') } function MenuSection({ children }: { children: React.ReactNode }) { return {children} } /** Hauptpunkt: linksbündig, kräftig — er trägt die Hierarchie. */ function MainItem({ label, onClick }: { label: string; onClick: () => void }) { return ( {label} ) } /** * Unterpunkt: kleiner, eingerückt und am rechten Rand ausgerichtet (§14/§16). * Die Rechtsausrichtung ist die Vorgabe — sie trennt die Ebenen auch ohne * Einrückungslinien sichtbar voneinander. */ function SubItem({ children, onClick }: { children: React.ReactNode; onClick: () => void }) { return ( {children} ) } function ProfileLabel({ icon, label }: { icon: React.ReactNode; label: string }) { return ( <> {label} {icon} ) }