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

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>
)