fix(map+edit): erster Zeichen-Punkt sofort sichtbar + Bearbeiten-Status klarer (v1.6.8)
- Zeichnen: gesetzte Eckpunkte (Linie/Polygon/Pfeil/Gefahrenzone) erscheinen sofort als Marker beim Klick — bisher sah man auf dem Handy erst ab dem zweiten Punkt etwas (Vorschau nur bei Mausbewegung) - Gemeinsames Bearbeiten: alle drei Zustände explizit in der Statuszeile — "Ansichtsmodus frei · Bearbeiten übernehmen", "Sie bearbeiten · freigeben", "X bearbeitet · nur Ansicht" Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lageplan",
|
||||
"version": "1.6.7",
|
||||
"version": "1.6.8",
|
||||
"description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -19,7 +19,7 @@ import { useAuth } from '@/components/providers/auth-provider'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
||||
import { JournalView } from '@/components/journal/journal-view'
|
||||
import { Lock, Eye, AlertTriangle, WifiOff, GraduationCap, Map as MapIcon, ClipboardList, LayoutGrid } from 'lucide-react'
|
||||
import { Lock, Eye, Pencil, AlertTriangle, WifiOff, GraduationCap, Map as MapIcon, ClipboardList, LayoutGrid } 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'
|
||||
@@ -1038,7 +1038,7 @@ export default function AppPage() {
|
||||
{/* Kompakte Status-Zeile — eine Zeile mit Chips statt bis zu vier gestapelten Bannern.
|
||||
Zeigt nur, was gerade zutrifft; im Normalfall (Einsatz, online, verifiziert,
|
||||
niemand fremd am Zeichnen) erscheint sie gar nicht. */}
|
||||
{(currentProject?.mode === 'UEBUNG' || isOffline || isReadOnly || isEditingByMe || (user && user.emailVerified === false)) && (
|
||||
{(currentProject?.mode === 'UEBUNG' || isOffline || isReadOnly || isEditingByMe || (currentProject && roleCanEdit && !presentationLocked) || (user && user.emailVerified === false)) && (
|
||||
<div data-tour="edit-toggle" className="flex flex-wrap items-center gap-1.5 px-3 py-1 border-b bg-muted/30 text-xs">
|
||||
{currentProject?.mode === 'UEBUNG' && (
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-blue-600 text-white font-semibold px-2 py-0.5">
|
||||
@@ -1052,17 +1052,27 @@ export default function AppPage() {
|
||||
)}
|
||||
{isReadOnly && (
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-orange-100 text-orange-800 dark:bg-orange-950/50 dark:text-orange-300 font-medium px-2 py-0.5">
|
||||
<Eye className="w-3.5 h-3.5" /> <strong className="font-semibold">{editingBy?.name}</strong> bearbeitet · Live-Ansicht
|
||||
<Lock className="w-3.5 h-3.5" /> <strong className="font-semibold">{editingBy?.name}</strong> bearbeitet gerade · Sie sehen live zu (nur Ansicht)
|
||||
</span>
|
||||
)}
|
||||
{isEditingByMe && (
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-green-100 text-green-800 dark:bg-green-950/50 dark:text-green-300 font-medium px-2 py-0.5">
|
||||
<Lock className="w-3.5 h-3.5" /> Sie bearbeiten
|
||||
<button onClick={handleStopEditing} disabled={editingLoading} className="ml-1 underline hover:no-underline disabled:opacity-50" title="Karte für andere freigeben (speichert)">
|
||||
<Pencil className="w-3.5 h-3.5" /> Sie bearbeiten — andere sehen nur zu
|
||||
<button onClick={handleStopEditing} disabled={editingLoading} className="ml-1.5 font-semibold underline hover:no-underline disabled:opacity-50" title="Karte speichern & für andere freigeben">
|
||||
freigeben
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{/* Karte ist frei und ich dürfte bearbeiten — expliziter Hinweis + Übernehmen,
|
||||
damit klar ist, dass man gerade nur zuschaut, bis man selbst startet. */}
|
||||
{currentProject && roleCanEdit && !isReadOnly && !isEditingByMe && !presentationLocked && (
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-muted text-muted-foreground font-medium px-2 py-0.5">
|
||||
<Eye className="w-3.5 h-3.5" /> Ansichtsmodus — frei
|
||||
<button onClick={ensureEditing} disabled={editingLoading} className="ml-1.5 font-semibold text-foreground underline hover:no-underline disabled:opacity-50" title="Bearbeitung übernehmen — sperrt die Karte für andere">
|
||||
Bearbeiten übernehmen
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
{user && user.emailVerified === false && (
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-amber-100 text-amber-800 dark:bg-amber-950/50 dark:text-amber-300 font-medium px-2 py-0.5">
|
||||
<AlertTriangle className="w-3.5 h-3.5" /> E-Mail unbestätigt
|
||||
|
||||
@@ -943,6 +943,21 @@ export function MapView({
|
||||
},
|
||||
})
|
||||
|
||||
// Preview vertex points — jeder gesetzte Eckpunkt bekommt sofort einen sichtbaren
|
||||
// Marker (sonst sieht man beim ersten Klick nichts, v.a. auf dem Handy ohne Hover)
|
||||
m.addLayer({
|
||||
id: 'draw-preview-points',
|
||||
type: 'circle',
|
||||
source: 'draw-preview',
|
||||
filter: ['==', ['geometry-type'], 'Point'],
|
||||
paint: {
|
||||
'circle-radius': ['coalesce', ['get', 'r'], 5],
|
||||
'circle-color': ['coalesce', ['get', 'fill'], '#ffffff'],
|
||||
'circle-stroke-color': ['coalesce', ['get', 'color'], '#2563eb'],
|
||||
'circle-stroke-width': 2,
|
||||
},
|
||||
})
|
||||
|
||||
// --- Helper: update preview source ---
|
||||
function setPreview(data: any) {
|
||||
const mapRef = map.current
|
||||
@@ -953,6 +968,34 @@ export function MapView({
|
||||
function clearPreviewData() {
|
||||
setPreview({ type: 'FeatureCollection', features: [] })
|
||||
}
|
||||
// Baut die Live-Vorschau für mehrpunkt-Werkzeuge (Linie/Polygon/Pfeil/Gefahrenzone):
|
||||
// gesetzte Eckpunkte als Marker + optionale Gummiband-Linie/Fläche zum Cursor.
|
||||
function buildVertexPreview(mode: string, verts: [number, number][], hover?: [number, number]) {
|
||||
if (verts.length === 0) return { type: 'FeatureCollection', features: [] as any[] }
|
||||
const previewColor = mode === 'dangerzone' ? '#dc2626' : selectedColorRef.current
|
||||
const feats: any[] = []
|
||||
const isArea = mode === 'polygon' || mode === 'dangerzone'
|
||||
// Verbindungsgeometrie (nur wenn mind. 1 Punkt gesetzt)
|
||||
if (hover) {
|
||||
if (isArea) {
|
||||
const ring = [...verts, hover, verts[0]]
|
||||
feats.push({ type: 'Feature', geometry: { type: 'Polygon', coordinates: [ring] }, properties: { color: previewColor } })
|
||||
} else {
|
||||
feats.push({ type: 'Feature', geometry: { type: 'LineString', coordinates: [...verts, hover] }, properties: { color: previewColor } })
|
||||
}
|
||||
} else if (verts.length >= 2) {
|
||||
feats.push({ type: 'Feature', geometry: { type: 'LineString', coordinates: verts }, properties: { color: previewColor } })
|
||||
}
|
||||
// Eckpunkt-Marker — der erste etwas grösser/gefüllt, damit der Start klar ist
|
||||
verts.forEach((v, i) => {
|
||||
feats.push({
|
||||
type: 'Feature',
|
||||
geometry: { type: 'Point', coordinates: v },
|
||||
properties: { color: previewColor, fill: i === 0 ? previewColor : '#ffffff', r: i === 0 ? 6 : 5 },
|
||||
})
|
||||
})
|
||||
return { type: 'FeatureCollection', features: feats }
|
||||
}
|
||||
|
||||
// --- ALL drawing event handlers registered directly in map load ---
|
||||
m.on('click', (e: maplibregl.MapMouseEvent) => {
|
||||
@@ -1102,6 +1145,8 @@ export function MapView({
|
||||
drawingRef.current.currentCoords.push(coords)
|
||||
setDrawingPointCount(drawingRef.current.currentCoords.length)
|
||||
}
|
||||
// Sofort-Feedback: gesetzte Eckpunkte anzeigen, auch ohne Mausbewegung (Touch!)
|
||||
setPreview(buildVertexPreview(mode, drawingRef.current.currentCoords as [number, number][]))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1255,29 +1300,9 @@ export function MapView({
|
||||
|
||||
if (!drawingRef.current.isDrawing && !drawingRef.current.startPoint) return
|
||||
|
||||
if (mode === 'linestring' || mode === 'arrow') {
|
||||
if (mode === 'linestring' || mode === 'arrow' || mode === 'polygon' || mode === 'dangerzone') {
|
||||
if (drawingRef.current.currentCoords.length > 0) {
|
||||
setPreview({
|
||||
type: 'FeatureCollection',
|
||||
features: [{
|
||||
type: 'Feature',
|
||||
geometry: { type: 'LineString', coordinates: [...drawingRef.current.currentCoords, coords] },
|
||||
properties: { color },
|
||||
}],
|
||||
})
|
||||
}
|
||||
} else if (mode === 'polygon' || mode === 'dangerzone') {
|
||||
if (drawingRef.current.currentCoords.length > 0) {
|
||||
const pc = [...drawingRef.current.currentCoords, coords, drawingRef.current.currentCoords[0]]
|
||||
const previewColor = mode === 'dangerzone' ? '#dc2626' : color
|
||||
setPreview({
|
||||
type: 'FeatureCollection',
|
||||
features: [{
|
||||
type: 'Feature',
|
||||
geometry: { type: 'Polygon', coordinates: [pc] },
|
||||
properties: { color: previewColor },
|
||||
}],
|
||||
})
|
||||
setPreview(buildVertexPreview(mode, drawingRef.current.currentCoords as [number, number][], coords))
|
||||
}
|
||||
} else if (mode === 'rectangle' && drawingRef.current.startPoint) {
|
||||
const s = drawingRef.current.startPoint
|
||||
|
||||
Reference in New Issue
Block a user