feat(exercise): Übungs-Cockpit nutzt den Modul-Baukasten + Testplan (v1.5.6)

- Übungen zeigen jetzt zusätzlich zu Zielen/Auswertung dieselben eigenen
  Tabellen-Module wie Einsätze (z.B. Atemschutz, Kräfte vor Ort)
- Live-Sync der Modul-Zeilen auch in der Übung (journal-updated Socket)
- docs/TESTPLAN.md: strukturierter Handy-/Einsatz-Testplan

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pepe Ziberi
2026-07-22 19:53:37 +02:00
parent 0a6c9cbcf2
commit e02ea21c93
3 changed files with 85 additions and 1 deletions

49
docs/TESTPLAN.md Normal file
View File

@@ -0,0 +1,49 @@
# Handy-/Einsatz-Testplan
Kurzer, strukturierter Durchlauf nach jedem grösseren Deploy — am besten einmal
am **Handy** und einmal am **Desktop**. Abhaken, was funktioniert.
## 1. Einsatz anlegen
- [ ] 🔥 **Neu → Neuer Einsatz**: Dialog öffnet, Standort wird automatisch als Ort vorgeschlagen
- [ ] Titel eingeben → **Erstellen** → Karte startet am Einsatzort
- [ ] Kopfzeile zeigt den Einsatz mittig (Status-Punkt rot, Nr, Titel, Ort), Uhr rechts
## 2. Karte & Symbole (der wichtige Handy-Test)
- [ ] Symbol **antippen** → Hinweis erscheint → **auf Karte tippen** platziert es
- [ ] Mehrere gleiche Symbole nacheinander setzen → Zähler steigt → **Fertig**
- [ ] Symbol antippen (auf der Karte) → drehen/skalieren/löschen
- [ ] Zeichnen: Pfeil/Linie ziehen → Dialog fragt **Normal / Leitung / Rettungsachse**
- [ ] Eigene **GPS-Position** sichtbar (Punkt + Genauigkeit), Karte bleibt am Einsatzort
- [ ] **Vollbild-Button** (oben links) blendet die Browser-Leiste aus
## 3. Auto-Bearbeitung
- [ ] Ohne „Bearbeitung starten" direkt zeichnen können (Lock wird automatisch übernommen)
- [ ] Zweites Gerät/Person: sieht Änderungen live, Karte ist dort gesperrt
- [ ] Statuszeile zeigt „Sie bearbeiten" + **freigeben**
## 4. Cockpit-Module (Baukasten)
- [ ] Journal-Tab → **Module** (als Admin): Modul an/aus, Reihenfolge, eigenes Modul erstellen
- [ ] Neues Modul (z.B. Atemschutz) erscheint im Cockpit, Zeilen anlegen/haken/löschen
- [ ] Änderungen live auf zweitem Gerät sichtbar
## 5. Journal & Rapport
- [ ] Journal-Eintrag erfassen (mit Vorschlags-Leiste), Erledigt-Haken, Korrektur
- [ ] SOMA/Pendenzen bedienen
- [ ] **Rapport** erzeugen → PDF enthält Journal, SOMA, Pendenzen **und die eigenen Module**
- [ ] Rapport per E-Mail / Drucken
## 6. Übung
- [ ] 🎓 **Neu → Neue Übung** → Übungs-Cockpit mit Zielen + Auswertung
- [ ] Übungsziele mit Status (Erreicht/Teilweise/…), Auswertung speichert automatisch
- [ ] Eigene Module erscheinen auch in der Übung
## 7. Offline
- [ ] Einsatz öffnen → Toast „Karte offline verfügbar" abwarten
- [ ] Flugmodus an → Karte im Einsatzgebiet bleibt sichtbar, zeichnen möglich
- [ ] Flugmodus aus → Änderungen werden synchronisiert (Statuszeile „Offline" verschwindet)
## 8. Beenden
- [ ] **Einsatz beenden** (Kopfzeile/Menü) → In-App-Bestätigung → gespeichert & geschlossen
---
_Bei Problemen: betroffenen Schritt notieren + ggf. Screenshot, dann gezielt fixen._

View File

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

View File

@@ -4,6 +4,9 @@ import { useState, useEffect, useRef, useCallback } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { useToast } from '@/components/ui/use-toast'
import { getSocket } from '@/lib/socket'
import { TableModule } from '@/components/journal/modules/table-module'
import type { CockpitModule } from '@/lib/modules'
import {
GraduationCap,
Plus,
@@ -15,6 +18,7 @@ import {
Trash2,
ClipboardCheck,
Loader2,
Blocks,
} from 'lucide-react'
type GoalStatus = 'OPEN' | 'REACHED' | 'PARTIAL' | 'MISSED'
@@ -74,6 +78,24 @@ export function ExerciseCockpit({ projectId, projectTitle, canEdit, initialEvalu
const evalDebounce = useRef<NodeJS.Timeout | null>(null)
const { toast } = useToast()
// Modul-Baukasten: dieselben eigenen Tabellen-Module wie im Einsatz
const [tableModules, setTableModules] = useState<CockpitModule[]>([])
useEffect(() => {
fetch('/api/tenant/modules')
.then(r => r.ok ? r.json() : null)
.then(data => {
if (data?.modules) {
setTableModules(data.modules.filter((m: CockpitModule) => m.enabled && m.type === 'table' && m.columns?.length))
}
})
.catch(() => {})
}, [])
const notifyModuleChanged = useCallback(() => {
if (!projectId) return
try { getSocket().emit('journal-updated', { projectId }) } catch { /* offline */ }
}, [projectId])
// Ziele laden
const loadGoals = useCallback(async () => {
if (!projectId) {
@@ -299,6 +321,19 @@ export function ExerciseCockpit({ projectId, projectTitle, canEdit, initialEvalu
/>
<p className="text-xs text-muted-foreground mt-1">Wird automatisch gespeichert.</p>
</section>
{/* Eigene Cockpit-Module (Modul-Baukasten) — z.B. Atemschutz, Kräfte vor Ort */}
{projectId && tableModules.map((mod) => (
<section key={mod.id}>
<h3 className="text-sm font-semibold flex items-center gap-2 mb-3 text-foreground/80">
<Blocks className="w-4 h-4 text-blue-600" />
{mod.name}
</h3>
<div className="border rounded-lg bg-white dark:bg-card overflow-hidden">
<TableModule projectId={projectId} module={mod} canEdit={canEdit} notifyChanged={notifyModuleChanged} />
</div>
</section>
))}
</div>
</div>
)