From 593bef4dcfe8f2868593933caf585aa07e2f3b53 Mon Sep 17 00:00:00 2001 From: colinislit Date: Mon, 8 Dec 2025 14:26:07 +0100 Subject: [PATCH] feat(overdracht): Incident badges + expandable rapportage content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Incident telling: - Nieuwe incident_count in PatientOverzicht alerts - Telt type='incident', 'crisis' én verpleegkundig category='incident' - Oranje badge in patiëntenlijst - "Met alerts" filter includeert nu ook incidenten Expandable content: - Rapportages in timeline tonen nu volledige tekst - "Lees meer" / "Minder tonen" toggle voor berichten >5 regels - Behoudt whitespace/regeleinden in tekst Opruiming: - Oude screenshots verwijderd 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/api/overdracht/patients/route.ts | 37 +++++++++- app/epd/verpleegrapportage/actions.ts | 36 +++++++++- .../components/blocks/reports-block.tsx | 68 +++++++++++++++--- .../components/patient-list-row.tsx | 12 +++- .../components/patient-list.tsx | 4 +- docs/screenshot/2_overdrachtoverzicht.png | Bin 147731 -> 0 bytes docs/screenshot/afspraak-model.png | Bin 54351 -> 0 bytes docs/screenshot/icon-ui.png | Bin 75870 -> 0 bytes docs/screenshot/icons-verpleegrapportage.png | Bin 113996 -> 0 bytes ...ptimalisatie-verpleegkundige-raportage.png | Bin 131518 -> 0 bytes docs/screenshot/overdrachtoverzicht.png | Bin 161728 -> 0 bytes docs/screenshot/verpleegrapportage-invoer.png | Bin 64300 -> 0 bytes docs/screenshot/verpleegrapportage.png | Bin 102814 -> 0 bytes docs/screenshot/zorgnotities.png | Bin 78931 -> 0 bytes lib/types/overdracht.ts | 1 + 15 files changed, 145 insertions(+), 13 deletions(-) delete mode 100644 docs/screenshot/2_overdrachtoverzicht.png delete mode 100644 docs/screenshot/afspraak-model.png delete mode 100644 docs/screenshot/icon-ui.png delete mode 100644 docs/screenshot/icons-verpleegrapportage.png delete mode 100644 docs/screenshot/optimalisatie-verpleegkundige-raportage.png delete mode 100644 docs/screenshot/overdrachtoverzicht.png delete mode 100644 docs/screenshot/verpleegrapportage-invoer.png delete mode 100644 docs/screenshot/verpleegrapportage.png delete mode 100644 docs/screenshot/zorgnotities.png diff --git a/app/api/overdracht/patients/route.ts b/app/api/overdracht/patients/route.ts index f4022ec..04a8e22 100644 --- a/app/api/overdracht/patients/route.ts +++ b/app/api/overdracht/patients/route.ts @@ -87,6 +87,7 @@ export async function GET(request: NextRequest) { { data: risksData }, { data: vitalsData }, { data: logsData }, + { data: incidentReportsData }, ] = await Promise.all([ // High risk assessments (via intakes) supabase @@ -114,6 +115,15 @@ export async function GET(request: NextRequest) { .eq('shift_date', targetDate) .eq('include_in_handover', true) .is('deleted_at', null), + + // Incident reports today (type='incident' OR verpleegkundig with category='incident') + supabase + .from('reports') + .select('id, patient_id, type, structured_data') + .in('patient_id', patientIds) + .gte('created_at', dayStart) + .lte('created_at', dayEnd) + .is('deleted_at', null), ]); // Count alerts per patient @@ -121,6 +131,7 @@ export async function GET(request: NextRequest) { high_risk_count: number; abnormal_vitals_count: number; marked_logs_count: number; + incident_count: number; }>(); // Initialize all patients with zero counts @@ -129,6 +140,7 @@ export async function GET(request: NextRequest) { high_risk_count: 0, abnormal_vitals_count: 0, marked_logs_count: 0, + incident_count: 0, }); } @@ -157,6 +169,27 @@ export async function GET(request: NextRequest) { } } + // Count incidents (type='incident' OR type='crisis' OR verpleegkundig with category='incident') + for (const report of incidentReportsData || []) { + if (!report.patient_id) continue; + const counts = alertCounts.get(report.patient_id); + if (!counts) continue; + + // Direct incident/crisis type + if (report.type === 'incident' || report.type === 'crisis') { + counts.incident_count++; + continue; + } + + // Verpleegkundig report with incident category + if (report.type === 'verpleegkundig' && report.structured_data) { + const data = report.structured_data as { category?: string }; + if (data.category === 'incident') { + counts.incident_count++; + } + } + } + // 3. Build response const patients: PatientOverzicht[] = Array.from(patientMap.values()).map( (patient) => { @@ -164,6 +197,7 @@ export async function GET(request: NextRequest) { high_risk_count: 0, abnormal_vitals_count: 0, marked_logs_count: 0, + incident_count: 0, }; return { @@ -177,7 +211,8 @@ export async function GET(request: NextRequest) { total: alerts.high_risk_count + alerts.abnormal_vitals_count + - alerts.marked_logs_count, + alerts.marked_logs_count + + alerts.incident_count, }, }; } diff --git a/app/epd/verpleegrapportage/actions.ts b/app/epd/verpleegrapportage/actions.ts index e3aea58..152c9ad 100644 --- a/app/epd/verpleegrapportage/actions.ts +++ b/app/epd/verpleegrapportage/actions.ts @@ -135,6 +135,7 @@ export async function getOverdrachtPatients(period: PeriodValue = '7d'): Promise { data: risksData }, { data: vitalsData }, { data: logsData }, + { data: incidentReportsData }, ] = await Promise.all([ // High risk assessments (via intakes) - these are not time-bound supabase @@ -163,6 +164,14 @@ export async function getOverdrachtPatients(period: PeriodValue = '7d'): Promise .lte('shift_date', targetDate) .eq('include_in_handover', true) .is('deleted_at', null), + + // Incident reports in period (type='incident' OR verpleegkundig with category='incident') + supabase + .from('reports') + .select('id, patient_id, type, structured_data') + .in('patient_id', patientIds) + .gte('created_at', periodStartISO) + .is('deleted_at', null), ]); // Count alerts per patient @@ -170,6 +179,7 @@ export async function getOverdrachtPatients(period: PeriodValue = '7d'): Promise high_risk_count: number; abnormal_vitals_count: number; marked_logs_count: number; + incident_count: number; }>(); // Initialize all patients with zero counts @@ -178,6 +188,7 @@ export async function getOverdrachtPatients(period: PeriodValue = '7d'): Promise high_risk_count: 0, abnormal_vitals_count: 0, marked_logs_count: 0, + incident_count: 0, }); } @@ -206,6 +217,27 @@ export async function getOverdrachtPatients(period: PeriodValue = '7d'): Promise } } + // Count incidents (type='incident' OR type='crisis' OR verpleegkundig with category='incident') + for (const report of incidentReportsData || []) { + if (!report.patient_id) continue; + const counts = alertCounts.get(report.patient_id); + if (!counts) continue; + + // Direct incident/crisis type + if (report.type === 'incident' || report.type === 'crisis') { + counts.incident_count++; + continue; + } + + // Verpleegkundig report with incident category + if (report.type === 'verpleegkundig' && report.structured_data) { + const data = report.structured_data as { category?: string }; + if (data.category === 'incident') { + counts.incident_count++; + } + } + } + // 3. Build response const patients: PatientOverzicht[] = Array.from(patientMap.values()).map( (patient) => { @@ -213,6 +245,7 @@ export async function getOverdrachtPatients(period: PeriodValue = '7d'): Promise high_risk_count: 0, abnormal_vitals_count: 0, marked_logs_count: 0, + incident_count: 0, }; return { @@ -226,7 +259,8 @@ export async function getOverdrachtPatients(period: PeriodValue = '7d'): Promise total: alerts.high_risk_count + alerts.abnormal_vitals_count + - alerts.marked_logs_count, + alerts.marked_logs_count + + alerts.incident_count, }, }; } diff --git a/app/epd/verpleegrapportage/components/blocks/reports-block.tsx b/app/epd/verpleegrapportage/components/blocks/reports-block.tsx index 9ef03c7..e30fa2d 100644 --- a/app/epd/verpleegrapportage/components/blocks/reports-block.tsx +++ b/app/epd/verpleegrapportage/components/blocks/reports-block.tsx @@ -4,7 +4,10 @@ * Gegroepeerd per dag met dagdeel headers */ -import { FileText, Clock, User, Pill, Utensils, AlertTriangle, CheckCircle2, Sun, Sunrise, Sunset, Moon } from 'lucide-react'; +'use client'; + +import { useState } from 'react'; +import { FileText, Clock, User, Pill, Utensils, AlertTriangle, CheckCircle2, Sun, Sunrise, Sunset, Moon, ChevronDown, ChevronUp } from 'lucide-react'; import { format, isToday, isYesterday } from 'date-fns'; import { nl } from 'date-fns/locale'; import type { Report } from '@/lib/types/overdracht'; @@ -73,9 +76,52 @@ function getCategoryIcon(category: VerpleegkundigCategory | null) { } } -function truncateContent(content: string, maxLength: number = 150): string { - if (content.length <= maxLength) return content; - return content.substring(0, maxLength).trim() + '...'; +// Check if content needs "Lees meer" (more than 5 lines or very long) +const MAX_LINES = 5; +const CHARS_PER_LINE_ESTIMATE = 80; + +function needsExpansion(content: string): boolean { + const lineCount = content.split('\n').length; + const estimatedLines = Math.ceil(content.length / CHARS_PER_LINE_ESTIMATE); + return lineCount > MAX_LINES || estimatedLines > MAX_LINES; +} + +// Expandable content component +function ExpandableContent({ content }: { content: string }) { + const [expanded, setExpanded] = useState(false); + const showToggle = needsExpansion(content); + + if (!showToggle) { + return ( +

+ {content} +

+ ); + } + + return ( +
+

+ {content} +

+ +
+ ); } // Dagdeel helpers @@ -157,7 +203,15 @@ function groupReportsByDayAndPart(reports: Report[]): GroupedReports[] { } export function ReportsBlock({ reports }: ReportsBlockProps) { - const incidentCount = reports.filter(r => r.type === 'incident' || r.type === 'crisis').length; + // Count incidents: type='incident' OR type='crisis' OR verpleegkundig with category='incident' + const incidentCount = reports.filter(r => { + if (r.type === 'incident' || r.type === 'crisis') return true; + if (r.type === 'verpleegkundig') { + const category = getVerpleegkundigCategory(r.structured_data); + return category === 'incident'; + } + return false; + }).length; const handoverCount = reports.filter(r => r.include_in_handover).length; const groupedReports = groupReportsByDayAndPart(reports); @@ -280,9 +334,7 @@ export function ReportsBlock({ reports }: ReportsBlockProps) { {/* Report content */} -

- {truncateContent(report.content)} -

+ {/* Author if available */} {report.created_by && ( diff --git a/app/epd/verpleegrapportage/components/patient-list-row.tsx b/app/epd/verpleegrapportage/components/patient-list-row.tsx index 7a2d658..792f431 100644 --- a/app/epd/verpleegrapportage/components/patient-list-row.tsx +++ b/app/epd/verpleegrapportage/components/patient-list-row.tsx @@ -40,6 +40,7 @@ export function PatientListRow({ patient, isSelected, onClick }: PatientListRowP const hasHighRisk = patient.alerts.high_risk_count > 0; const hasAbnormalVitals = patient.alerts.abnormal_vitals_count > 0; const hasMarkedLogs = patient.alerts.marked_logs_count > 0; + const hasIncidents = patient.alerts.incident_count > 0; return (