feat(rapport): eigene Cockpit-Module erscheinen im Einsatzrapport (v1.5.5)
- Rapport öffnen sammelt jetzt auch die Daten der eigenen Tabellen-Module (Atemschutz, Kräfte vor Ort, Lagemeldungen, eigene Checklisten) - Neue Rapport-Sektion rendert jede Modul-Tabelle mit ihren Spalten (Text / Haken / Zeit) im PDF unter Pendenzen - Vorschau der Module auch im Rapport-Dialog - Zeit-Spalten: ISO → HH:MM, Haken → ✓/— Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -414,6 +414,75 @@ export function JournalView({ projectId, projectTitle, projectLocation, mode, ei
|
||||
window.print()
|
||||
}, [])
|
||||
|
||||
// Rapport öffnen: Journal + SOMA + Pendenzen UND die eigenen Tabellen-Module
|
||||
// einsammeln, damit alles im PDF-Rapport erscheint.
|
||||
const [rapportLoading, setRapportLoading] = useState(false)
|
||||
const handleOpenRapport = useCallback(async () => {
|
||||
if (!projectId) return
|
||||
setRapportLoading(true)
|
||||
try {
|
||||
// Daten der aktiven eigenen Module (type 'table') laden
|
||||
const tableModules = modules.filter(m => m.enabled && m.type === 'table' && m.columns?.length)
|
||||
const moduleTables = await Promise.all(tableModules.map(async (mod) => {
|
||||
try {
|
||||
const res = await fetch(`/api/projects/${projectId}/modules/${mod.id}/items`)
|
||||
const data = res.ok ? await res.json() : { items: [] }
|
||||
return {
|
||||
name: mod.name,
|
||||
columns: mod.columns,
|
||||
rows: (data.items || []).map((it: any) => ({ ...it.data, _createdAt: it.createdAt })),
|
||||
}
|
||||
} catch {
|
||||
return { name: mod.name, columns: mod.columns, rows: [] }
|
||||
}
|
||||
}))
|
||||
|
||||
const now = new Date()
|
||||
setRapportForm({
|
||||
organisation: tenantName || '',
|
||||
abteilung: '',
|
||||
datum: now.toLocaleDateString('de-CH'),
|
||||
uhrzeit: now.toLocaleTimeString('de-CH', { hour: '2-digit', minute: '2-digit' }),
|
||||
einsatzNr: einsatzNr || '',
|
||||
alarmzeit: entries.length > 0 ? formatTime(entries[0].time) : '',
|
||||
prioritaet: '',
|
||||
einsatzort: projectLocation || '',
|
||||
koordinaten: '',
|
||||
objekt: '',
|
||||
alarmierungsart: '',
|
||||
stichwort: projectTitle || '',
|
||||
zeitAlarm: entries.length > 0 ? formatTime(entries[0].time) : '',
|
||||
zeitAusruecken: '', zeitEintreffen: '', zeitBereit: '',
|
||||
zeitKontrolle: '', zeitAus: '', zeitEinruecken: '', zeitEnde: '',
|
||||
lageEintreffen: '',
|
||||
massnahmen: entries.map(e => `${formatTime(e.time)} ${e.what}${e.who ? ` (${e.who})` : ''}`),
|
||||
somaItems: checkItems.map(c => ({
|
||||
label: c.label,
|
||||
confirmed: c.confirmed,
|
||||
ok: c.ok,
|
||||
confirmedAt: c.confirmedAt ? formatTime(c.confirmedAt) : null,
|
||||
})),
|
||||
pendenzenItems: pendenzen.map(p => ({
|
||||
what: p.what,
|
||||
who: p.who || '',
|
||||
whenHow: p.whenHow || '',
|
||||
done: p.done,
|
||||
doneAt: p.doneAt ? formatTime(p.doneAt) : null,
|
||||
})),
|
||||
moduleTables,
|
||||
fahrzeuge: [] as any[],
|
||||
bemerkungen: '',
|
||||
einsatzleiter: einsatzleiter || '',
|
||||
rapporteur: journalfuehrer || '',
|
||||
reportNumber: '',
|
||||
logoUrl: tenantLogoUrl || '',
|
||||
})
|
||||
setShowRapportDialog(true)
|
||||
} finally {
|
||||
setRapportLoading(false)
|
||||
}
|
||||
}, [projectId, modules, tenantName, einsatzNr, entries, projectLocation, projectTitle, checkItems, pendenzen, einsatzleiter, journalfuehrer, tenantLogoUrl])
|
||||
|
||||
if (!projectId) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full text-muted-foreground">
|
||||
@@ -456,53 +525,10 @@ export function JournalView({ projectId, projectTitle, projectLocation, mode, ei
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={!projectId}
|
||||
onClick={() => {
|
||||
if (!projectId) return
|
||||
const now = new Date()
|
||||
// Pre-fill form with available data
|
||||
setRapportForm({
|
||||
organisation: tenantName || '',
|
||||
abteilung: '',
|
||||
datum: now.toLocaleDateString('de-CH'),
|
||||
uhrzeit: now.toLocaleTimeString('de-CH', { hour: '2-digit', minute: '2-digit' }),
|
||||
einsatzNr: einsatzNr || '',
|
||||
alarmzeit: entries.length > 0 ? formatTime(entries[0].time) : '',
|
||||
prioritaet: '',
|
||||
einsatzort: projectLocation || '',
|
||||
koordinaten: '',
|
||||
objekt: '',
|
||||
alarmierungsart: '',
|
||||
stichwort: projectTitle || '',
|
||||
zeitAlarm: entries.length > 0 ? formatTime(entries[0].time) : '',
|
||||
zeitAusruecken: '', zeitEintreffen: '', zeitBereit: '',
|
||||
zeitKontrolle: '', zeitAus: '', zeitEinruecken: '', zeitEnde: '',
|
||||
lageEintreffen: '',
|
||||
massnahmen: entries.map(e => `${formatTime(e.time)} ${e.what}${e.who ? ` (${e.who})` : ''}`),
|
||||
somaItems: checkItems.map(c => ({
|
||||
label: c.label,
|
||||
confirmed: c.confirmed,
|
||||
ok: c.ok,
|
||||
confirmedAt: c.confirmedAt ? formatTime(c.confirmedAt) : null,
|
||||
})),
|
||||
pendenzenItems: pendenzen.map(p => ({
|
||||
what: p.what,
|
||||
who: p.who || '',
|
||||
whenHow: p.whenHow || '',
|
||||
done: p.done,
|
||||
doneAt: p.doneAt ? formatTime(p.doneAt) : null,
|
||||
})),
|
||||
fahrzeuge: [] as any[],
|
||||
bemerkungen: '',
|
||||
einsatzleiter: einsatzleiter || '',
|
||||
rapporteur: journalfuehrer || '',
|
||||
reportNumber: '',
|
||||
logoUrl: tenantLogoUrl || '',
|
||||
})
|
||||
setShowRapportDialog(true)
|
||||
}}
|
||||
disabled={!projectId || rapportLoading}
|
||||
onClick={handleOpenRapport}
|
||||
>
|
||||
<FileText className="w-4 h-4 mr-1.5" />
|
||||
{rapportLoading ? <Loader2 className="w-4 h-4 mr-1.5 animate-spin" /> : <FileText className="w-4 h-4 mr-1.5" />}
|
||||
Rapport
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={() => setShowSendDialog(!showSendDialog)}>
|
||||
|
||||
Reference in New Issue
Block a user