feat: Präsentationsmodus (Schloss-Button) + Version 1.3.5
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 16m59s

This commit is contained in:
Pepe Ziberi
2026-05-20 08:35:52 +02:00
parent 902e730cd3
commit 9b96de0a21
3 changed files with 28 additions and 3 deletions

View File

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

View File

@@ -50,7 +50,8 @@ export default function AppPage() {
const [isFullscreen, setIsFullscreen] = useState(false) const [isFullscreen, setIsFullscreen] = useState(false)
const [auditLog, setAuditLog] = useState<{ time: string; action: string }[]>([]) const [auditLog, setAuditLog] = useState<{ time: string; action: string }[]>([])
const [isAuditOpen, setIsAuditOpen] = useState(false) const [isAuditOpen, setIsAuditOpen] = useState(false)
const [presentationLocked, setPresentationLocked] = useState(false)
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false) const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false)
const [lastMapScreenshot, setLastMapScreenshot] = useState<string>('') const [lastMapScreenshot, setLastMapScreenshot] = useState<string>('')
const [defaultSymbolScale, setDefaultSymbolScale] = useState(1.5) const [defaultSymbolScale, setDefaultSymbolScale] = useState(1.5)
@@ -375,7 +376,7 @@ export default function AppPage() {
const roleCanEdit = user ? (user.role === 'SERVER_ADMIN' || user.role === 'TENANT_ADMIN' || user.role === 'OPERATOR') : false const roleCanEdit = user ? (user.role === 'SERVER_ADMIN' || user.role === 'TENANT_ADMIN' || user.role === 'OPERATOR') : false
// User can only edit if they have the role AND they hold the editing lock (or no one is editing) // User can only edit if they have the role AND they hold the editing lock (or no one is editing)
const canEdit = roleCanEdit && (isEditingByMe || !editingBy) const canEdit = !presentationLocked && roleCanEdit && (isEditingByMe || !editingBy)
const isReadOnly = !!editingBy && !isEditingByMe const isReadOnly = !!editingBy && !isEditingByMe
// Auto-save: localStorage persistence + debounced API save + beacon on unload // Auto-save: localStorage persistence + debounced API save + beacon on unload
@@ -806,6 +807,14 @@ export default function AppPage() {
userRole={user?.role} userRole={user?.role}
onLogout={logout} onLogout={logout}
onStartTour={() => { resetOnboardingTour(); setShowTour(true) }} onStartTour={() => { resetOnboardingTour(); setShowTour(true) }}
presentationLocked={presentationLocked}
onTogglePresentationLock={() => {
const next = !presentationLocked
setPresentationLocked(next)
if (next && isEditingByMe) {
handleStopEditing()
}
}}
/> />
{/* Offline banner */} {/* Offline banner */}

View File

@@ -39,6 +39,8 @@ import {
Shield, Shield,
MapPin, MapPin,
HelpCircle, HelpCircle,
Lock,
Unlock,
} from 'lucide-react' } from 'lucide-react'
import { HoseSettingsDialog } from '@/components/dialogs/hose-settings-dialog' import { HoseSettingsDialog } from '@/components/dialogs/hose-settings-dialog'
import type { Project, DrawFeature } from '@/types' import type { Project, DrawFeature } from '@/types'
@@ -66,6 +68,8 @@ interface TopbarProps {
userRole?: string userRole?: string
onLogout?: () => void onLogout?: () => void
onStartTour?: () => void onStartTour?: () => void
presentationLocked?: boolean
onTogglePresentationLock?: () => void
} }
export function Topbar({ export function Topbar({
@@ -89,6 +93,8 @@ export function Topbar({
userRole, userRole,
onLogout, onLogout,
onStartTour, onStartTour,
presentationLocked,
onTogglePresentationLock,
}: TopbarProps) { }: TopbarProps) {
const [isLoadDialogOpen, setIsLoadDialogOpen] = useState(false) const [isLoadDialogOpen, setIsLoadDialogOpen] = useState(false)
const [isHoseSettingsOpen, setIsHoseSettingsOpen] = useState(false) const [isHoseSettingsOpen, setIsHoseSettingsOpen] = useState(false)
@@ -172,6 +178,16 @@ export function Topbar({
<span className="hidden lg:inline">{isSaving ? 'Speichern...' : 'Speichern'}</span> <span className="hidden lg:inline">{isSaving ? 'Speichern...' : 'Speichern'}</span>
</Button> </Button>
<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' : ''}`}
onClick={onTogglePresentationLock}
title={presentationLocked ? 'Präsentationsmodus deaktivieren' : 'Präsentationsmodus aktivieren'}
>
{presentationLocked ? <Lock className="w-5 h-5 md:mr-1" /> : <Unlock className="w-5 h-5 md:mr-1" />}
<span className="hidden lg:inline">{presentationLocked ? 'Gesperrt' : 'Frei'}</span>
</Button>
<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="h-9 md:h-10 px-2 md:px-3 text-sm" title="Menü">