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

@@ -1,6 +1,6 @@
{ {
"name": "lageplan", "name": "lageplan",
"version": "1.4.14", "version": "1.4.15",
"description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation", "description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation",
"private": true, "private": true,
"scripts": { "scripts": {

View File

@@ -13,6 +13,8 @@ import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu' } from '@/components/ui/dropdown-menu'
import { import {
@@ -184,7 +186,7 @@ export function Topbar({
<Button <Button
variant={presentationLocked ? 'default' : 'outline'} 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} onClick={onTogglePresentationLock}
title={presentationLocked ? 'Präsentationsmodus deaktivieren' : 'Präsentationsmodus aktivieren'} title={presentationLocked ? 'Präsentationsmodus deaktivieren' : 'Präsentationsmodus aktivieren'}
> >
@@ -205,7 +207,7 @@ export function Topbar({
<Button <Button
onClick={() => onNewProject('UEBUNG')} onClick={() => onNewProject('UEBUNG')}
variant="outline" 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" title="Neue Übung erstellen"
> >
<GraduationCap className="w-5 h-5 md:mr-1" /> <GraduationCap className="w-5 h-5 md:mr-1" />
@@ -214,7 +216,7 @@ export function Topbar({
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <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" /> <MoreVertical className="w-5 h-5 md:mr-1" />
<span className="hidden lg:inline">Menü</span> <span className="hidden lg:inline">Menü</span>
</Button> </Button>
@@ -347,36 +349,102 @@ export function Topbar({
</div> </div>
)} )}
{/* Mobile: overflow menu with all secondary actions */} {/* Mobile: EIN Menü mit allen Aktionen (ersetzt die früheren zwei getrennten Menüs) */}
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <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" /> <MoreVertical className="w-5 h-5" />
</Button> </Button>
</DropdownMenuTrigger> </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 && ( {project && onEndProject && (
<DropdownMenuItem onClick={onEndProject} className="text-red-600 focus:text-red-700"> <DropdownMenuItem onClick={onEndProject} className="text-red-600 focus:text-red-700">
<CheckCircle2 className="w-4 h-4 mr-2" /> <CheckCircle2 className="w-4 h-4 mr-2" />
{project.mode === 'UEBUNG' ? 'Übung beenden' : 'Einsatz beenden'} {project.mode === 'UEBUNG' ? 'Übung beenden' : 'Einsatz beenden'}
</DropdownMenuItem> </DropdownMenuItem>
)} )}
<DropdownMenuItem onClick={onToggleTheme}> <DropdownMenuItem onClick={() => onNewProject('UEBUNG')}>
{isDarkMode ? <Sun className="w-4 h-4 mr-2" /> : <Moon className="w-4 h-4 mr-2" />} <GraduationCap className="w-4 h-4 mr-2" />
{isDarkMode ? 'Tagmodus' : 'Nachtmodus'} Neue Übung
</DropdownMenuItem> </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}> <DropdownMenuItem onClick={onToggleFullscreen}>
{isFullscreen ? <Minimize className="w-4 h-4 mr-2" /> : <Maximize className="w-4 h-4 mr-2" />} {isFullscreen ? <Minimize className="w-4 h-4 mr-2" /> : <Maximize className="w-4 h-4 mr-2" />}
{isFullscreen ? 'Vollbild verlassen' : 'Vollbild'} {isFullscreen ? 'Vollbild verlassen' : 'Vollbild'}
</DropdownMenuItem> </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)}> <DropdownMenuItem onClick={() => setIsHoseSettingsOpen(true)}>
<Settings className="w-4 h-4 mr-2" /> <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> </DropdownMenuItem>
{onLogout && ( {onLogout && (
<DropdownMenuItem onClick={onLogout} className="text-destructive"> <DropdownMenuItem onClick={onLogout} className="text-destructive focus:text-destructive">
<LogOut className="w-4 h-4 mr-2" /> <LogOut className="w-4 h-4 mr-2" />
Abmelden {userName && `(${userName})`} Abmelden
</DropdownMenuItem> </DropdownMenuItem>
)} )}
</DropdownMenuContent> </DropdownMenuContent>