From 6e3663cf7ff07db4a6589ab282ca01a55dc0e539 Mon Sep 17 00:00:00 2001 From: Pepe Ziberi Date: Sun, 19 Jul 2026 15:20:27 +0200 Subject: [PATCH] =?UTF-8?q?feat(mobile):=20Handy-Optimierung=20=E2=80=93?= =?UTF-8?q?=20Tap-to-place,=20Bottom-Nav,=20kompakte=20Banner=20(v1.4.12)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tap-to-place: Symbol antippen → auf Karte tippen platziert es (Alternative zu Drag&Drop, mobil-tauglich); schwebender Hinweis mit Abbrechen - Mobile Bottom-Navigation: Karte / Journal(Auswertung) / Symbole ohne Menü erreichbar - FAB entfernt; Karten-Steuerung (Zoom/Standort/Massstab) hebt sich mobil über die Bottom-Nav - Touch-Ziele in der Werkzeugleiste auf 44px vergrössert - "Einsatz/Übung beenden" jetzt auch im Mobile-Overflow-Menü - Banner (Übung/Offline/E-Mail/Live) mobil kompakt: kleinere Höhe, gekürzte Texte, truncate - Einsatz-beenden-Bestätigung als In-App-Dialog statt Browser-confirm() Co-Authored-By: Claude Opus 4.8 (1M context) --- package.json | 2 +- src/app/app/page.tsx | 125 +++++++++++++++++++----- src/app/globals.css | 9 ++ src/components/layout/left-toolbar.tsx | 32 +++--- src/components/layout/right-sidebar.tsx | 24 ++--- src/components/layout/topbar.tsx | 6 ++ src/components/map/map-view.tsx | 18 ++++ 7 files changed, 161 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index 9e6f2e4..3fe06c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lageplan", - "version": "1.4.11", + "version": "1.4.12", "description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation", "private": true, "scripts": { diff --git a/src/app/app/page.tsx b/src/app/app/page.tsx index 91c62ac..04ca8f0 100644 --- a/src/app/app/page.tsx +++ b/src/app/app/page.tsx @@ -17,9 +17,9 @@ import { LineLabelDialog } from '@/components/dialogs/line-label-dialog' import { useToast } from '@/components/ui/use-toast' import { useAuth } from '@/components/providers/auth-provider' import { Button } from '@/components/ui/button' -import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog' import { JournalView } from '@/components/journal/journal-view' -import { Lock, Unlock, Eye, AlertTriangle, WifiOff, GraduationCap } from 'lucide-react' +import { Lock, Unlock, Eye, AlertTriangle, WifiOff, GraduationCap, Map as MapIcon, ClipboardList, LayoutGrid, X } from 'lucide-react' import { CustomDragLayer } from '@/components/map/custom-drag-layer' import { OnboardingTour, resetOnboardingTour } from '@/components/onboarding/onboarding-tour' import { useKeyboardShortcuts } from '@/hooks/use-keyboard-shortcuts' @@ -47,6 +47,9 @@ export default function AppPage() { const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false) const [newProjectMode, setNewProjectMode] = useState('EINSATZ') + const [isEndConfirmOpen, setIsEndConfirmOpen] = useState(false) + // Tap-to-place: angetipptes Symbol wartet auf Platzierung per Karten-Tipp (Mobil) + const [pendingSymbol, setPendingSymbol] = useState<{ id: string; imageUrl: string } | null>(null) const [isSaving, setIsSaving] = useState(false) const [isDeleteAllConfirmOpen, setIsDeleteAllConfirmOpen] = useState(false) const [isFullscreen, setIsFullscreen] = useState(false) @@ -627,9 +630,10 @@ export default function AppPage() { } // Einsatz beenden: speichern, Bearbeitungs-Lock lösen und zurück zur Übersicht. - const handleEndProject = async () => { + // Bestätigung erfolgt über einen In-App-Dialog (kein natives confirm()). + const performEndProject = async () => { if (!currentProject) return - if (!confirm(`Einsatz "${currentProject.title}" beenden? Alle Änderungen werden gespeichert und der Einsatz geschlossen.`)) return + setIsEndConfirmOpen(false) const title = currentProject.title try { await handleSaveProject() } catch { /* Save meldet Fehler selbst */ } if (isEditingByMe) { try { await handleStopEditing() } catch { /* ignore */ } } @@ -701,6 +705,15 @@ export default function AppPage() { handleFeaturesChange([...featuresRef.current, newFeature]) }, [handleFeaturesChange, defaultSymbolScale]) + // Tap-to-place: Symbol antippen → Sidebar schliessen, Karte zeigen, nächster Tipp platziert + const handleSymbolSelect = useCallback((symbol: { id: string; imageUrl: string }) => { + setPendingSymbol(symbol) + setIsSidebarOpen(false) + setActiveTab('map') + setDrawMode('select') + toast({ title: 'Symbol platzieren', description: 'Tippe auf die Karte, um das Symbol zu setzen.' }) + }, [setIsSidebarOpen, setActiveTab, toast]) + const handleTextPlace = useCallback((coordinates: [number, number]) => { setTextPlaceCoords(coordinates) setIsTextDialogOpen(true) @@ -852,7 +865,7 @@ export default function AppPage() { setIsEndConfirmOpen(true)} onSaveProject={handleSaveProject} onLoadProject={handleProjectLoaded} onDeleteProject={handleDeleteProject} @@ -882,7 +895,7 @@ export default function AppPage() { {/* Übungs-Banner — verhindert Verwechslung mit echtem Einsatz */} {currentProject?.mode === 'UEBUNG' && ( -
+
ÜBUNG — kein realer Einsatz
@@ -890,10 +903,10 @@ export default function AppPage() { {/* Offline banner */} {isOffline && ( -
+
- Offline-Modus — Änderungen werden lokal gespeichert und beim Reconnect synchronisiert. + Offline-Modus — Änderungen werden lokal gespeichert und beim Reconnect synchronisiert. {syncQueueCount > 0 && ` (${syncQueueCount} ausstehend)`}
@@ -901,9 +914,9 @@ export default function AppPage() { {/* Email verification banner */} {user && user.emailVerified === false && ( -
+
- Ihre E-Mail-Adresse wurde noch nicht bestätigt. Bitte prüfen Sie Ihren Posteingang. + E-Mail noch nicht bestätigt. Bitte prüfen Sie Ihren Posteingang.
{/* Journal / Übungs-Cockpit — always mounted, hidden via CSS */} -
+
{currentProject?.mode === 'UEBUNG' ? ( setIsSidebarOpen(!isSidebarOpen)} @@ -1041,6 +1057,48 @@ export default function AppPage() { />
+ {/* Tap-to-place Hinweis — schwebt über der Bottom-Nav, mit Abbrechen */} + {pendingSymbol && ( +
+ + Auf die Karte tippen zum Platzieren + +
+ )} + + {/* Mobile Bottom-Navigation — Karte / Journal / Symbole erreichbar ohne Menü */} + {currentProject && ( + + )} + + {/* Einsatz beenden — In-App-Bestätigung statt native confirm() */} + + + + {currentProject?.mode === 'UEBUNG' ? 'Übung beenden?' : 'Einsatz beenden?'} + +

+ {currentProject?.title + ? <>„{currentProject.title}" wird gespeichert und geschlossen. + : 'Der aktuelle Eintrag wird gespeichert und geschlossen.'} +

+ + + + +
+
+ -