Files
triqura-ecd/app/api/overdracht/[patientId]/route.ts
colinislit 75c7e284b9 feat(verpleegrapportage): Nieuwe module + opruiming codebase
Verpleegrapportage module:
- Nieuwe /epd/verpleegrapportage met patiëntenoverzicht
- Rapportage invoer workspace met timeline view
- Overdracht overzicht met AI-samenvatting
- API endpoints voor verpleegrapportage data

Opruiming:
- Oude /epd/overdracht en /epd/dagregistratie verwijderd (vervangen)
- Oude /api/nursing-logs verwijderd (geconsolideerd naar reports)
- Verouderde design docs en reports verwijderd
- Fonts verplaatst van docs/ naar public/fonts/

Bugfixes:
- Risk-manager: fix constraint violation (db values vs display labels)
- Overdracht API: filter op rapportages i.p.v. encounters

Database:
- Migratie voor consolidatie nursing_logs naar reports tabel

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 11:19:48 +01:00

170 lines
5.2 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { createClient } from '@/lib/auth/server';
import type {
PatientDetail,
VitalSign,
Report,
RiskAssessment,
Condition,
} from '@/lib/types/overdracht';
import { VERPLEEG_REPORT_TYPES } from '@/lib/types/report';
interface RouteParams {
params: Promise<{ patientId: string }>;
}
export async function GET(request: NextRequest, { params }: RouteParams) {
try {
const { patientId } = await params;
if (!z.string().uuid().safeParse(patientId).success) {
return NextResponse.json(
{ error: 'patientId moet een geldige UUID zijn' },
{ status: 400 }
);
}
const supabase = await createClient();
// Date calculations
const today = new Date().toISOString().split('T')[0];
const todayStart = `${today}T00:00:00.000Z`;
const last24h = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
// Parallel queries for all data
const [
patientResult,
vitalsResult,
reportsResult,
risksResult,
conditionsResult,
] = await Promise.all([
// 1. Patient info
supabase
.from('patients')
.select('id, name_given, name_family, name_prefix, birth_date, gender')
.eq('id', patientId)
.single(),
// 2. Vitals today
supabase
.from('observations')
.select('id, code_display, value_quantity_value, value_quantity_unit, interpretation_code, effective_datetime')
.eq('patient_id', patientId)
.eq('category', 'vital-signs')
.gte('effective_datetime', todayStart)
.order('effective_datetime', { ascending: false }),
// 3. Reports last 24h - includes verpleegkundig (was nursing_logs) plus observatie, incident, etc
supabase
.from('reports')
.select('id, type, content, created_at, created_by, structured_data, include_in_handover, shift_date')
.eq('patient_id', patientId)
.in('type', [...VERPLEEG_REPORT_TYPES])
.gte('created_at', last24h)
.is('deleted_at', null)
.order('created_at', { ascending: false }),
// 4. Risks via intakes
supabase
.from('risk_assessments')
.select('id, risk_type, risk_level, rationale, created_at, intakes!inner(patient_id)')
.eq('intakes.patient_id', patientId)
.in('risk_level', ['laag', 'gemiddeld', 'hoog', 'zeer_hoog']),
// 5. Active conditions
supabase
.from('conditions')
.select('id, code_display, clinical_status, onset_datetime')
.eq('patient_id', patientId)
.eq('clinical_status', 'active'),
]);
// Check if patient exists
if (patientResult.error || !patientResult.data) {
return NextResponse.json(
{ error: 'Patiënt niet gevonden' },
{ status: 404 }
);
}
// Handle errors for other queries gracefully (return empty arrays)
if (vitalsResult.error) {
console.error('Error fetching vitals:', vitalsResult.error);
}
if (reportsResult.error) {
console.error('Error fetching reports:', reportsResult.error);
}
if (risksResult.error) {
console.error('Error fetching risks:', risksResult.error);
}
if (conditionsResult.error) {
console.error('Error fetching conditions:', conditionsResult.error);
}
// Map vitals
const vitals: VitalSign[] = (vitalsResult.data || []).map((v) => ({
id: v.id,
code_display: v.code_display,
value_quantity_value: v.value_quantity_value,
value_quantity_unit: v.value_quantity_unit,
interpretation_code: v.interpretation_code,
effective_datetime: v.effective_datetime,
}));
// Map reports (now includes verpleegkundig type)
const reports: Report[] = (reportsResult.data || []).map((r) => ({
id: r.id,
type: r.type,
content: r.content,
created_at: r.created_at,
created_by: r.created_by,
structured_data: r.structured_data,
include_in_handover: r.include_in_handover,
shift_date: r.shift_date,
}));
// Map risks (remove the intakes join data)
const risks: RiskAssessment[] = (risksResult.data || []).map((r) => ({
id: r.id,
risk_type: r.risk_type,
risk_level: r.risk_level,
rationale: r.rationale,
created_at: r.created_at,
}));
// Map conditions
const conditions: Condition[] = (conditionsResult.data || []).map((c) => ({
id: c.id,
code_display: c.code_display,
clinical_status: c.clinical_status,
onset_datetime: c.onset_datetime || undefined,
}));
// Build response
const response: PatientDetail = {
patient: {
id: patientResult.data.id,
name_given: patientResult.data.name_given,
name_family: patientResult.data.name_family,
name_prefix: patientResult.data.name_prefix || undefined,
birth_date: patientResult.data.birth_date,
gender: patientResult.data.gender,
},
vitals,
reports,
risks,
conditions,
};
return NextResponse.json(response);
} catch (error) {
console.error('Unexpected error in GET /api/overdracht/[patientId]:', error);
return NextResponse.json(
{ error: 'Onverwachte serverfout' },
{ status: 500 }
);
}
}