diff --git a/package.json b/package.json index 4d84253..defd68a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lageplan", - "version": "1.5.4", + "version": "1.5.5", "description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation", "private": true, "scripts": { diff --git a/src/app/rapport/[token]/page.tsx b/src/app/rapport/[token]/page.tsx index ead7bc5..7db092f 100644 --- a/src/app/rapport/[token]/page.tsx +++ b/src/app/rapport/[token]/page.tsx @@ -260,6 +260,38 @@ export default function RapportViewerPage({ params }: { params: Promise<{ token: )} + {/* 6b. Eigene Cockpit-Module (Modul-Baukasten) */} + {Array.isArray(d.moduleTables) && d.moduleTables.map((mt: any, ti: number) => ( + Array.isArray(mt.rows) && mt.rows.length > 0 ? ( +
+ + + + {(mt.columns || []).map((c: any) => ( + + ))} + + + + {mt.rows.map((row: any, ri: number) => ( + + {(mt.columns || []).map((c: any) => ( + + ))} + + ))} + +
{c.label}
+ {c.type === 'check' + ? (row[c.key] ? '✓' : '—') + : c.type === 'time' + ? formatCellTime(row[c.key] || row._createdAt) + : (row[c.key] ?? '')} +
+
+ ) : null + ))} + {/* 7. Eingesetzte Mittel */} {d.fahrzeuge?.length > 0 && (
@@ -330,6 +362,16 @@ export default function RapportViewerPage({ params }: { params: Promise<{ token: ) } +// Zeit-Zellen der Modul-Tabellen: ISO-Datum → HH:MM, sonst Rohwert +function formatCellTime(value: any): string { + if (!value) return '' + const d = new Date(value) + if (!isNaN(d.getTime()) && typeof value === 'string' && value.includes('T')) { + return d.toLocaleTimeString('de-CH', { hour: '2-digit', minute: '2-digit' }) + } + return String(value) +} + function Section({ num, title, children }: { num: string; title: string; children: React.ReactNode }) { return (
diff --git a/src/components/journal/journal-view.tsx b/src/components/journal/journal-view.tsx index 6c9ddf2..7e11a6d 100644 --- a/src/components/journal/journal-view.tsx +++ b/src/components/journal/journal-view.tsx @@ -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 (
@@ -456,53 +525,10 @@ export function JournalView({ projectId, projectTitle, projectLocation, mode, ei
)} + {/* Eigene Cockpit-Module (read-only, aus dem Journal) */} + {Array.isArray(rapportForm.moduleTables) && rapportForm.moduleTables.filter((mt: any) => mt.rows?.length > 0).map((mt: any, i: number) => ( +
+ +
+ {mt.rows.map((row: any, ri: number) => ( +
+ {(mt.columns || []).filter((c: any) => c.type !== 'time').map((c: any) => ( + + {c.label}:{' '} + {c.type === 'check' ? (row[c.key] ? '✓' : '—') : (row[c.key] || '—')} + + ))} +
+ ))} +
+
+ ))} {/* Bemerkungen */}