refactor(topbar): Handy-Menü entwirrt – ein einziges Menü statt zwei (v1.4.15)

- Mobil nur noch EIN ⋮-Menü (früher ☰ links + ⋮ rechts → verwirrend)
- Topbar mobil reduziert auf: Logo · Speichern · Neuer Einsatz · Menü
- Selten gebrauchte Buttons (Neue Übung, Präsentationsmodus) wandern ins Menü
- Menü gruppiert mit Trennern: Einsatz/Übung · Export/Karte · Ansicht · System/Konto
- Voller Funktionsumfang mobil erreichbar (Einsätze verwalten, Export, Admin,
  Schlaucheinstellungen, Tour, Kennwort, Abmelden)
- Desktop-Layout unverändert

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pepe Ziberi
2026-07-19 17:41:52 +02:00
parent 2556a5c4a4
commit efedbdba4d
2 changed files with 81 additions and 13 deletions

View File

@@ -13,6 +13,8 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import {
@@ -184,7 +186,7 @@ export function Topbar({
<Button
variant={presentationLocked ? 'default' : 'outline'}
className={`h-9 md:h-10 px-2 md:px-3 text-sm ${presentationLocked ? 'bg-amber-600 hover:bg-amber-700 text-white border-amber-600' : ''}`}
className={`hidden md:flex h-9 md:h-10 px-2 md:px-3 text-sm ${presentationLocked ? 'bg-amber-600 hover:bg-amber-700 text-white border-amber-600' : ''}`}
onClick={onTogglePresentationLock}
title={presentationLocked ? 'Präsentationsmodus deaktivieren' : 'Präsentationsmodus aktivieren'}
>
@@ -205,7 +207,7 @@ export function Topbar({
<Button
onClick={() => onNewProject('UEBUNG')}
variant="outline"
className="h-9 md:h-10 px-2 md:px-3 text-sm border-blue-500 text-blue-600 hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-blue-950/40"
className="hidden md:flex h-9 md:h-10 px-2 md:px-3 text-sm border-blue-500 text-blue-600 hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-blue-950/40"
title="Neue Übung erstellen"
>
<GraduationCap className="w-5 h-5 md:mr-1" />
@@ -214,7 +216,7 @@ export function Topbar({
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="h-9 md:h-10 px-2 md:px-3 text-sm" title="Menü">
<Button variant="outline" className="hidden md:flex h-9 md:h-10 px-2 md:px-3 text-sm" title="Menü">
<MoreVertical className="w-5 h-5 md:mr-1" />
<span className="hidden lg:inline">Menü</span>
</Button>
@@ -347,36 +349,102 @@ export function Topbar({
</div>
)}
{/* Mobile: overflow menu with all secondary actions */}
{/* Mobile: EIN Menü mit allen Aktionen (ersetzt die früheren zwei getrennten Menüs) */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="md:hidden h-9 px-2">
<Button variant="outline" className="md:hidden h-9 px-2" title="Menü">
<MoreVertical className="w-5 h-5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuContent align="end" className="w-56 max-h-[80vh] overflow-y-auto">
{userName && (
<>
<DropdownMenuLabel className="text-xs text-muted-foreground font-normal">
{userName}{userRole ? ` · ${userRole}` : ''}
</DropdownMenuLabel>
<DropdownMenuSeparator />
</>
)}
{/* Einsatz / Übung */}
{project && onEndProject && (
<DropdownMenuItem onClick={onEndProject} className="text-red-600 focus:text-red-700">
<CheckCircle2 className="w-4 h-4 mr-2" />
{project.mode === 'UEBUNG' ? 'Übung beenden' : 'Einsatz beenden'}
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={onToggleTheme}>
{isDarkMode ? <Sun className="w-4 h-4 mr-2" /> : <Moon className="w-4 h-4 mr-2" />}
{isDarkMode ? 'Tagmodus' : 'Nachtmodus'}
<DropdownMenuItem onClick={() => onNewProject('UEBUNG')}>
<GraduationCap className="w-4 h-4 mr-2" />
Neue Übung
</DropdownMenuItem>
<DropdownMenuItem onClick={handleOpenLoadDialog}>
<List className="w-4 h-4 mr-2" />
Einsätze verwalten
</DropdownMenuItem>
<DropdownMenuSeparator />
{/* Karte / Export */}
<DropdownMenuItem onClick={() => handleExport('png')}>
<Download className="w-4 h-4 mr-2" />
Als PNG exportieren
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleExport('pdf')}>
<Download className="w-4 h-4 mr-2" />
Als PDF exportieren
</DropdownMenuItem>
{onClearAll && (
<DropdownMenuItem onClick={onClearAll} className="text-destructive focus:text-destructive">
<Eraser className="w-4 h-4 mr-2" />
Zeichnung leeren
</DropdownMenuItem>
)}
{onTogglePresentationLock && (
<DropdownMenuItem onClick={onTogglePresentationLock}>
{presentationLocked ? <Lock className="w-4 h-4 mr-2" /> : <Unlock className="w-4 h-4 mr-2" />}
{presentationLocked ? 'Präsentation: gesperrt' : 'Präsentationsmodus'}
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
{/* Ansicht */}
<DropdownMenuItem onClick={onToggleFullscreen}>
{isFullscreen ? <Minimize className="w-4 h-4 mr-2" /> : <Maximize className="w-4 h-4 mr-2" />}
{isFullscreen ? 'Vollbild verlassen' : 'Vollbild'}
</DropdownMenuItem>
<DropdownMenuItem onClick={onToggleTheme}>
{isDarkMode ? <Sun className="w-4 h-4 mr-2" /> : <Moon className="w-4 h-4 mr-2" />}
{isDarkMode ? 'Tagmodus' : 'Nachtmodus'}
</DropdownMenuItem>
<DropdownMenuSeparator />
{/* System / Konto */}
{(userRole === 'SERVER_ADMIN' || userRole === 'TENANT_ADMIN') && (
<DropdownMenuItem onClick={() => window.location.href = '/admin'}>
<Shield className="w-4 h-4 mr-2" />
Administration
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={() => setIsHoseSettingsOpen(true)}>
<Settings className="w-4 h-4 mr-2" />
Einstellungen
Schlaucheinstellungen
</DropdownMenuItem>
{onStartTour && (
<DropdownMenuItem onClick={onStartTour}>
<HelpCircle className="w-4 h-4 mr-2" />
Tour starten
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={() => setShowPasswordDialog(true)}>
<Key className="w-4 h-4 mr-2" />
Kennwort ändern
</DropdownMenuItem>
{onLogout && (
<DropdownMenuItem onClick={onLogout} className="text-destructive">
<DropdownMenuItem onClick={onLogout} className="text-destructive focus:text-destructive">
<LogOut className="w-4 h-4 mr-2" />
Abmelden {userName && `(${userName})`}
Abmelden
</DropdownMenuItem>
)}
</DropdownMenuContent>