diff --git a/app/api/nursing-logs/[id]/route.ts b/app/api/nursing-logs/[id]/route.ts new file mode 100644 index 0000000..d864de4 --- /dev/null +++ b/app/api/nursing-logs/[id]/route.ts @@ -0,0 +1,182 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; +import { createClient } from '@/lib/auth/server'; +import { + UpdateNursingLogSchema, + calculateShiftDate, +} from '@/lib/types/nursing-log'; + +interface RouteParams { + params: Promise<{ id: string }>; +} + +export async function PATCH(request: NextRequest, { params }: RouteParams) { + try { + const { id } = await params; + + if (!z.string().uuid().safeParse(id).success) { + return NextResponse.json( + { error: 'id moet een geldige UUID zijn' }, + { status: 400 } + ); + } + + const body = await request.json(); + const result = UpdateNursingLogSchema.safeParse(body); + + if (!result.success) { + return NextResponse.json( + { + error: 'Validatiefout', + details: result.error.issues.map((issue) => ({ + field: issue.path.join('.'), + message: issue.message, + })), + }, + { status: 400 } + ); + } + + const supabase = await createClient(); + const { data: authData } = await supabase.auth.getUser(); + + if (!authData?.user) { + return NextResponse.json({ error: 'Niet geautoriseerd' }, { status: 401 }); + } + + // Check if log exists and belongs to current user + const { data: existingLog, error: fetchError } = await supabase + .from('nursing_logs') + .select('id, created_by') + .eq('id', id) + .single(); + + if (fetchError || !existingLog) { + return NextResponse.json( + { error: 'Dagnotitie niet gevonden' }, + { status: 404 } + ); + } + + if (existingLog.created_by !== authData.user.id) { + return NextResponse.json( + { error: 'Je kunt alleen je eigen notities bewerken' }, + { status: 403 } + ); + } + + // Build update object + const updateData: Record = {}; + const { category, content, timestamp, include_in_handover } = result.data; + + if (category !== undefined) updateData.category = category; + if (content !== undefined) updateData.content = content; + if (include_in_handover !== undefined) + updateData.include_in_handover = include_in_handover; + + // If timestamp changes, recalculate shift_date + if (timestamp !== undefined) { + updateData.timestamp = timestamp; + updateData.shift_date = calculateShiftDate(timestamp); + } + + if (Object.keys(updateData).length === 0) { + return NextResponse.json( + { error: 'Geen velden om te updaten' }, + { status: 400 } + ); + } + + const { data, error } = await supabase + .from('nursing_logs') + .update(updateData) + .eq('id', id) + .select('*') + .single(); + + if (error) { + console.error('Error updating nursing log:', error); + return NextResponse.json( + { error: 'Bijwerken mislukt', details: error.message }, + { status: 500 } + ); + } + + return NextResponse.json(data); + } catch (error) { + console.error('Unexpected error in PATCH /api/nursing-logs/[id]:', error); + if (error instanceof SyntaxError) { + return NextResponse.json( + { error: 'Ongeldige JSON in request body' }, + { status: 400 } + ); + } + return NextResponse.json( + { error: 'Onverwachte serverfout' }, + { status: 500 } + ); + } +} + +export async function DELETE(request: NextRequest, { params }: RouteParams) { + try { + const { id } = await params; + + if (!z.string().uuid().safeParse(id).success) { + return NextResponse.json( + { error: 'id moet een geldige UUID zijn' }, + { status: 400 } + ); + } + + const supabase = await createClient(); + const { data: authData } = await supabase.auth.getUser(); + + if (!authData?.user) { + return NextResponse.json({ error: 'Niet geautoriseerd' }, { status: 401 }); + } + + // Check if log exists and belongs to current user + const { data: existingLog, error: fetchError } = await supabase + .from('nursing_logs') + .select('id, created_by') + .eq('id', id) + .single(); + + if (fetchError || !existingLog) { + return NextResponse.json( + { error: 'Dagnotitie niet gevonden' }, + { status: 404 } + ); + } + + if (existingLog.created_by !== authData.user.id) { + return NextResponse.json( + { error: 'Je kunt alleen je eigen notities verwijderen' }, + { status: 403 } + ); + } + + // Hard delete (RLS policy already ensures user can only delete own logs) + const { error } = await supabase + .from('nursing_logs') + .delete() + .eq('id', id); + + if (error) { + console.error('Error deleting nursing log:', error); + return NextResponse.json( + { error: 'Verwijderen mislukt', details: error.message }, + { status: 500 } + ); + } + + return new NextResponse(null, { status: 204 }); + } catch (error) { + console.error('Unexpected error in DELETE /api/nursing-logs/[id]:', error); + return NextResponse.json( + { error: 'Onverwachte serverfout' }, + { status: 500 } + ); + } +} diff --git a/app/api/nursing-logs/route.ts b/app/api/nursing-logs/route.ts new file mode 100644 index 0000000..3694868 --- /dev/null +++ b/app/api/nursing-logs/route.ts @@ -0,0 +1,146 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; +import { createClient } from '@/lib/auth/server'; +import { + CreateNursingLogSchema, + calculateShiftDate, + type NursingLogListResponse, +} from '@/lib/types/nursing-log'; + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url); + const patientId = searchParams.get('patientId'); + const date = searchParams.get('date'); // Optional: YYYY-MM-DD format + + if (!patientId) { + return NextResponse.json( + { error: 'patientId query parameter is verplicht' }, + { status: 400 } + ); + } + + if (!z.string().uuid().safeParse(patientId).success) { + return NextResponse.json( + { error: 'patientId moet een geldige UUID zijn' }, + { status: 400 } + ); + } + + // Validate date format if provided + if (date && !/^\d{4}-\d{2}-\d{2}$/.test(date)) { + return NextResponse.json( + { error: 'date moet in YYYY-MM-DD formaat zijn' }, + { status: 400 } + ); + } + + const supabase = await createClient(); + + let query = supabase + .from('nursing_logs') + .select('*') + .eq('patient_id', patientId) + .order('timestamp', { ascending: false }); + + // Filter by shift_date if date is provided + if (date) { + query = query.eq('shift_date', date); + } + + const { data, error } = await query; + + if (error) { + console.error('Error fetching nursing logs:', error); + return NextResponse.json( + { error: 'Fout bij ophalen dagnotities', details: error.message }, + { status: 500 } + ); + } + + const response: NursingLogListResponse = { + logs: data ?? [], + total: data?.length ?? 0, + }; + + return NextResponse.json(response); + } catch (error) { + console.error('Unexpected error in GET /api/nursing-logs:', error); + return NextResponse.json( + { error: 'Onverwachte serverfout' }, + { status: 500 } + ); + } +} + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const result = CreateNursingLogSchema.safeParse(body); + + if (!result.success) { + return NextResponse.json( + { + error: 'Validatiefout', + details: result.error.issues.map((issue) => ({ + field: issue.path.join('.'), + message: issue.message, + })), + }, + { status: 400 } + ); + } + + const supabase = await createClient(); + const { data: authData } = await supabase.auth.getUser(); + + if (!authData?.user) { + return NextResponse.json({ error: 'Niet geautoriseerd' }, { status: 401 }); + } + + const { patient_id, category, content, timestamp, include_in_handover } = + result.data; + + // Use provided timestamp or current time + const logTimestamp = timestamp || new Date().toISOString(); + + // Calculate shift_date from timestamp + const shiftDate = calculateShiftDate(logTimestamp); + + const { data, error } = await supabase + .from('nursing_logs') + .insert({ + patient_id, + category, + content, + timestamp: logTimestamp, + shift_date: shiftDate, + include_in_handover: include_in_handover ?? false, + created_by: authData.user.id, + }) + .select('*') + .single(); + + if (error) { + console.error('Error creating nursing log:', error); + return NextResponse.json( + { error: 'Opslaan mislukt', details: error.message }, + { status: 500 } + ); + } + + return NextResponse.json(data, { status: 201 }); + } catch (error) { + console.error('Unexpected error in POST /api/nursing-logs:', error); + if (error instanceof SyntaxError) { + return NextResponse.json( + { error: 'Ongeldige JSON in request body' }, + { status: 400 } + ); + } + return NextResponse.json( + { error: 'Onverwachte serverfout' }, + { status: 500 } + ); + } +} diff --git a/docs/specs/overdracht/bouwplan-overdracht-v1.md b/docs/specs/overdracht/bouwplan-overdracht-v1.md new file mode 100644 index 0000000..fced193 --- /dev/null +++ b/docs/specs/overdracht/bouwplan-overdracht-v1.md @@ -0,0 +1,314 @@ +# Mission Control - Bouwplan Overdracht Dashboard + +**Projectnaam:** Verpleegkundige Overdracht Dashboard +**Versie:** v1.0 +**Datum:** 05-12-2024 +**Auteur:** Colin Lit + +--- + +## 1. Doel en context + +**Doel:** Een werkend MVP bouwen van het Overdracht Dashboard met Dagregistratie Module. Verpleegkundigen kunnen snel patiëntinformatie overzien en met AI-hulp een beknopte overdracht genereren in 30 seconden. + +**Context:** +- Verpleegkundigen doen gemiddeld 6 overdrachten per dag +- Huidige workflow is tijdrovend en foutgevoelig +- Dit dashboard bundelt vitals, rapportages, dagnotities en risico's +- AI genereert gestructureerde samenvattingen met bronverwijzingen + +**Relatie met andere documenten:** +- PRD: `prd-overdracht-dashboard-v1.md` - scope en requirements +- FO: `fo-overdracht-dashboard-v1.1.md` - functionele specificatie +- TO: `to-overdracht-dashboard-v1.md` - technische architectuur + +--- + +## 2. Uitgangspunten + +### 2.1 Technische Stack + +| Component | Technologie | Status | +|-----------|-------------|--------| +| Frontend | Next.js 15 (App Router) | Bestaand | +| Backend | Next.js API Routes | Bestaand | +| Database | Supabase (PostgreSQL) | Bestaand | +| AI | Claude claude-sonnet-4-20250514 (Anthropic) | Bestaand | +| Styling | TailwindCSS + shadcn/ui | Bestaand | +| Validation | Zod | Bestaand | +| Auth | Supabase Auth + RLS | Bestaand | + +### 2.2 Projectkaders + +- **Nieuwe dependencies:** Geen (alles aanwezig) +- **Database:** 1 nieuwe tabel (`nursing_logs`) +- **Routes:** 2 nieuwe secties (`/epd/overdracht/`, `/epd/dagregistratie/`) +- **API endpoints:** 5 nieuwe endpoints +- **Data:** Demo data beschikbaar (patients, encounters, reports) + +### 2.3 Programmeer Uitgangspunten + +**Code Quality Principles:** +- **DRY:** Hergebruik bestaande componenten (Card, Badge, AIButton) +- **KISS:** Eenvoudige Server Components waar mogelijk +- **SOC:** API logic in `/api/`, UI in `/app/epd/`, types in `/lib/types/` +- **YAGNI:** Alleen MVP features, geen "nice to have" + +**Bestaande Patterns:** +- AI integratie: `app/api/behandelplan/generate/route.ts` +- CRUD API: `app/api/reports/route.ts` +- Form components: `app/epd/patients/[id]/screening/` +- Card layouts: `components/ui/card.tsx` + +--- + +## 3. Epics & Stories Overzicht + +| Epic ID | Titel | Doel | Status | Stories | Complexiteit | +|---------|-------|------|--------|---------|--------------| +| E0 | Database Setup | nursing_logs tabel + RLS | ⏳ To Do | 2 | Laag | +| E1 | API Nursing Logs | CRUD endpoints voor dagnotities | ⏳ To Do | 2 | Laag | +| E2 | API Overdracht | Endpoints voor overdracht data + AI | ⏳ To Do | 3 | Middel | +| E3 | Dagregistratie UI | Quick entry module | ⏳ To Do | 3 | Middel | +| E4 | Overdracht Overzicht | Patiënten grid | ⏳ To Do | 2 | Middel | +| E5 | Overdracht Detail | Info blokken + AI samenvatting | ⏳ To Do | 4 | Middel | +| E6 | Integratie & Polish | Sidebar, navigatie, testing | ⏳ To Do | 3 | Laag | + +**Totaal:** 19 stories + +--- + +## 4. Epics & Stories (Uitwerking) + +### Epic 0 - Database Setup +**Epic Doel:** nursing_logs tabel aanmaken met RLS policies en indexes. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP | +|----------|--------------|---------------------|--------|------|----| +| E0.S1 | nursing_logs tabel aanmaken | Tabel bestaat met alle kolommen uit TO, indexes aanwezig | ⏳ | - | 2 | +| E0.S2 | RLS policies implementeren | SELECT/INSERT/UPDATE/DELETE policies actief, alleen eigen logs muteerbaar | ⏳ | E0.S1 | 2 | + +**Technical Notes:** +- Migratie via `npx supabase migration new create_nursing_logs` +- Kolommen: id, patient_id, shift_date, timestamp, category, content, include_in_handover, created_by +- Categories: medicatie, adl, gedrag, incident, observatie + +--- + +### Epic 1 - API Nursing Logs +**Epic Doel:** CRUD endpoints voor dagnotities. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP | +|----------|--------------|---------------------|--------|------|----| +| E1.S1 | GET/POST /api/nursing-logs | Ophalen per patient+date, aanmaken met Zod validatie | ⏳ | E0.S2 | 3 | +| E1.S2 | PATCH/DELETE /api/nursing-logs/[id] | Update eigen logs, soft delete | ⏳ | E1.S1 | 2 | + +**Technical Notes:** +- Pattern volgen van `app/api/reports/route.ts` +- Zod schema: CreateNursingLogSchema, UpdateNursingLogSchema +- shift_date automatisch bepalen op basis van timestamp + +--- + +### Epic 2 - API Overdracht +**Epic Doel:** Endpoints voor overdracht overzicht, detail en AI generatie. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP | +|----------|--------------|---------------------|--------|------|----| +| E2.S1 | GET /api/overdracht/patients | Retourneert patiënten met encounters vandaag + alert counts | ⏳ | E0.S2 | 3 | +| E2.S2 | GET /api/overdracht/[patientId] | Retourneert patient + vitals + reports + logs + risks + conditions | ⏳ | E1.S1 | 5 | +| E2.S3 | POST /api/overdracht/generate | AI samenvatting met bronverwijzingen, logging naar ai_events | ⏳ | E2.S2 | 5 | + +**Technical Notes:** +- E2.S2: Parallel queries via Promise.all() +- E2.S3: Pattern van `behandelplan/generate`, nieuwe prompt in `lib/ai/overdracht-prompt.ts` +- AI output: { samenvatting, aandachtspunten[], actiepunten[] } + +--- + +### Epic 3 - Dagregistratie UI +**Epic Doel:** Quick entry module voor verpleegkundige notities. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP | +|----------|--------------|---------------------|--------|------|----| +| E3.S1 | Dagregistratie page | Route `/epd/dagregistratie/[patientId]`, lijst van notities vandaag | ⏳ | E1.S2 | 3 | +| E3.S2 | Quick entry form | Categorie dropdown, tijd, tekst (max 500), overdracht checkbox | ⏳ | E3.S1 | 5 | +| E3.S3 | Edit/Delete functionality | Inline edit, confirm delete dialog | ⏳ | E3.S2 | 3 | + +**Technical Notes:** +- Icons per categorie: Pill (medicatie), Utensils (adl), User (gedrag), AlertTriangle (incident), FileText (observatie) +- Kleuren: red (incident), blue (medicatie), green (adl), purple (gedrag), gray (observatie) +- Optimistic UI updates + +--- + +### Epic 4 - Overdracht Overzicht +**Epic Doel:** Grid van patiënten met alerts voor overdracht. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP | +|----------|--------------|---------------------|--------|------|----| +| E4.S1 | Overdracht overzicht page | Route `/epd/overdracht/`, grid van PatientCards, filter tabs | ⏳ | E2.S1 | 5 | +| E4.S2 | PatientCard component | Naam, leeftijd, alert badge (rood=hoog risico), doorklik | ⏳ | E4.S1 | 3 | + +**Technical Notes:** +- Filter tabs: "Alle patiënten", "Met alerts" +- Alert count = high_risk_count + abnormal_vitals_count + marked_logs_count +- Responsive grid: 1 col mobile, 2 col tablet, 3-4 col desktop + +--- + +### Epic 5 - Overdracht Detail +**Epic Doel:** Patiënt detail pagina met informatieblokken en AI samenvatting. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP | +|----------|--------------|---------------------|--------|------|----| +| E5.S1 | Overdracht detail page | Route `/epd/overdracht/[patientId]`, patient header, 2-kolom layout | ⏳ | E2.S2 | 3 | +| E5.S2 | Info blokken: Vitals + Reports | VitalsBlock (metingen vandaag), ReportsBlock (24u) | ⏳ | E5.S1 | 5 | +| E5.S3 | Info blokken: Logs + Risks | NursingLogsBlock (gemarkeerd), RisksBlock (actief) | ⏳ | E5.S2 | 5 | +| E5.S4 | AI Samenvatting blok | AIButton "Genereer samenvatting", loading state, output met bronnen | ⏳ | E2.S3, E5.S3 | 5 | + +**Technical Notes:** +- Linker kolom: Vitals, Reports, Logs, Risks (scrollable) +- Rechter kolom: AI Samenvatting (sticky) +- Bronverwijzingen klikbaar naar originele record +- Empty states per blok + +--- + +### Epic 6 - Integratie & Polish +**Epic Doel:** Sidebar link, navigatie en testing. + +| Story ID | Beschrijving | Acceptatiecriteria | Status | Afh. | SP | +|----------|--------------|---------------------|--------|------|----| +| E6.S1 | Sidebar uitbreiden | "Overdracht" link in EPD sidebar met alert badge | ⏳ | E4.S1 | 1 | +| E6.S2 | Navigatie links | Link van dagregistratie naar overdracht en vice versa | ⏳ | E5.S3 | 2 | +| E6.S3 | Smoke testing | Alle flows werken, geen console errors, performance OK | ⏳ | E6.S2 | 3 | + +**Technical Notes:** +- Sidebar icon: ClipboardList (lucide) +- Badge toont totaal aantal alerts +- Test met bestaande demo patients + +--- + +## 5. Kwaliteit & Testplan + +### Test Types + +| Test Type | Scope | Verantwoordelijke | +|-----------|-------|-------------------| +| Manual Testing | Alle flows | Developer | +| TypeScript | Type checking | Build process | +| RLS Testing | Database policies | Developer | + +### Manual Test Checklist + +**Dagregistratie:** +- [ ] Nieuwe notitie aanmaken werkt +- [ ] Categorie selectie werkt +- [ ] "Opnemen in overdracht" checkbox werkt +- [ ] Edit notitie werkt +- [ ] Delete notitie werkt (met confirm) +- [ ] Lijst refresht na mutatie + +**Overdracht Overzicht:** +- [ ] Grid toont patiënten met encounters vandaag +- [ ] Alert badges tonen correct aantal +- [ ] Filter "Met alerts" werkt +- [ ] Doorklik naar detail werkt + +**Overdracht Detail:** +- [ ] Patient header toont naam, leeftijd, diagnose +- [ ] Vitals blok toont metingen vandaag (of empty state) +- [ ] Reports blok toont laatste 24u (of empty state) +- [ ] Nursing logs blok toont gemarkeerde notities +- [ ] Risks blok toont actieve risico's +- [ ] AI samenvatting genereert binnen 5 sec +- [ ] Bronverwijzingen in AI output zijn correct + +**Performance:** +- [ ] Overzicht laadt < 2 sec +- [ ] Detail laadt < 1.5 sec +- [ ] AI response < 5 sec + +--- + +## 6. Succescriteria (uit PRD) + +- [ ] Overzicht laadt binnen 2 seconden +- [ ] Patiënt detail toont alle 6 informatieblokken correct (incl. dagnotities) +- [ ] Dagregistratie form submit < 1 seconde +- [ ] AI samenvatting genereert binnen 5 seconden +- [ ] AI output is begrijpelijk en medisch relevant +- [ ] AI integreert dagnotities correct in samenvatting +- [ ] Navigatie tussen overzicht, detail en dagregistratie werkt vlot +- [ ] Alerts (hoog risico, afwijkende vitals, gemarkeerde notities) zijn direct zichtbaar +- [ ] Empty states bij ontbrekende data zijn informatief + +--- + +## 7. Risico's & Mitigatie + +| Risico | Kans | Impact | Mitigatie | +|--------|------|--------|-----------| +| AI samenvatting te lang/vaag | Middel | Hoog | Strikte prompt, max tokens, testen | +| Geen vitale functies in systeem | Hoog | Laag | Graceful empty state | +| Performance bij veel data | Laag | Middel | Indexes, parallel queries | +| AI hallucinaties | Laag | Hoog | Bronverwijzingen verplicht | +| VPK vergeet notities markeren | Middel | Middel | UI hint, standaard checkbox | + +--- + +## 8. Niet in Scope (MVP) + +| Feature | Reden | +|---------|-------| +| Medicatie-invoer | Alleen weergave, aparte module | +| Historische trends | Geen grafieken | +| PDF export | Komt later | +| Notificaties | Geen realtime alerts | +| Multi-afdeling | Te complex | +| Rechten per rol | Beperkt onderscheid VPK/arts | + +--- + +## 9. Referenties + +### Mission Control Documents + +| Document | Status | +|----------|--------| +| PRD Overdracht Dashboard v1.0 | Gereed | +| FO Overdracht Dashboard v1.1 | Gereed | +| TO Overdracht Dashboard v1.0 | Gereed | +| Bouwplan v1.0 | Gereed (dit document) | + +### Code Locaties + +| Wat | Locatie | +|-----|---------| +| AI prompt pattern | `lib/ai/behandelplan-prompt.ts` | +| API route pattern | `app/api/reports/route.ts` | +| shadcn components | `components/ui/` | +| EPD layout | `app/epd/layout.tsx` | +| Sidebar | `app/epd/components/epd-sidebar.tsx` | + +--- + +## 10. Glossary + +| Term | Betekenis | +|------|-----------| +| Overdracht | Mondelinge/schriftelijke informatieoverdracht tussen diensten | +| VPK | Verpleegkundige | +| Dagnotitie | Korte registratie tijdens dienst (nursing_log) | +| Alert | Signaal voor aandacht (hoog risico, afwijking, incident) | +| Handover | Engelse term voor overdracht | + +--- + +## Changelog + +| Versie | Datum | Auteur | Wijziging | +|--------|-------|--------|-----------| +| v1.0 | 05-12-2024 | Colin | Initieel bouwplan gebaseerd op PRD, FO en TO | diff --git a/docs/specs/overdracht/fo-overdracht-dashboard-v1.1.md b/docs/specs/overdracht/fo-overdracht-dashboard-v1.1.md new file mode 100644 index 0000000..7e2fa27 --- /dev/null +++ b/docs/specs/overdracht/fo-overdracht-dashboard-v1.1.md @@ -0,0 +1,642 @@ +# 🧩 Functioneel Ontwerp (FO) — Verpleegkundige Overdracht Dashboard + +**Projectnaam:** Verpleegkundige Overdracht Dashboard +**Versie:** v1.1 (met Dagregistratie Module) +**Datum:** 05-12-2024 +**Auteur:** Colin Lit + +--- + +## 1. Doel en relatie met het PRD + +🎯 **Doel van dit document:** +Dit Functioneel Ontwerp beschrijft **hoe** het Overdracht Dashboard uit het PRD functioneel zal werken — wat de verpleegkundige ziet, doet en ervaart. Waar het PRD uitlegt *wat en waarom*, laat dit FO zien *hoe dit in de praktijk werkt*. + +📘 **Relatie met PRD:** +- PRD-referentie: `prd-overdracht-dashboard-v1.md` +- Dit FO is de functionele uitwerking van PRD secties 3 (Kernfunctionaliteiten) en 4 (Gebruikersflows) + +**Kernprincipe:** +> Elke getoonde informatie moet **traceerbaar** zijn naar de bron. De gebruiker moet kunnen zien waar data vandaan komt en kunnen doorklikken naar het originele record. + +**Nieuw in v1.1:** +> **Dagregistratie Module** - Verpleegkundigen kunnen tijdens hun dienst snelle notities maken (medicatie, ADL, gedrag, incidenten) en markeren welke relevant zijn voor overdracht. Dit scheidt operationele dagregistratie van behandelrapportages die in het decursus komen. + +--- + +## 2. Overzicht van de belangrijkste onderdelen + +🎯 **Doel:** Kort overzicht van schermen en componenten binnen het Overdracht Dashboard. + +| # | Onderdeel | Beschrijving | Route | +|---|-----------|--------------|-------| +| 1 | **Overdracht Overzicht** | Grid van alle patiënten voor vandaag | `/epd/overdracht/` | +| 2 | **Patiënt Detail** | Informatieblokken + AI samenvatting | `/epd/overdracht/[patientId]` | +| 3 | **Dagregistratie Module** | Snelle verpleegkundige notities met overdracht-markering | `/epd/dagregistratie/[patientId]` | +| 4 | **Vitale Functies Blok** | Metingen vandaag met trend indicators | Detail pagina | +| 5 | **Rapportages Blok** | Behandelrapportages (24u) met bronlinks | Detail pagina | +| 6 | **Dagnotities Blok** | Verpleegkundige registraties vandaag | Detail pagina | +| 7 | **Medicatie Blok** | Huidige medicatie *(placeholder)* | Detail pagina | +| 8 | **Risico's Blok** | Actieve risicotaxaties | Detail pagina | +| 9 | **AI Samenvatting Blok** | Gegenereerde overdracht met bronverwijzingen | Detail pagina | + +--- + +## 3. User Stories + +🎯 **Doel:** Beschrijven wat gebruikers moeten kunnen doen, vanuit hun perspectief. + +### MVP User Stories (origineel) + +| ID | Rol | Doel / Actie | Verwachte waarde | Prio | +|----|-----|--------------|------------------|------| +| **OD-US01** | Verpleegkundige | Overzicht zien van alle patiënten voor vandaag | Weet wie ik moet overdragen | 🔴 Hoog | +| **OD-US02** | Verpleegkundige | Filteren op patiënten met alerts | Focus op urgente cases | 🔴 Hoog | +| **OD-US03** | Verpleegkundige | Doorklikken naar patiënt detail | Zie alle relevante info | 🔴 Hoog | +| **OD-US04** | Verpleegkundige | Vitale functies zien met afwijkingen gemarkeerd | Direct zien wat er aan de hand is | 🔴 Hoog | +| **OD-US05** | Verpleegkundige | Recente rapportages lezen | Context voor overdracht | 🔴 Hoog | +| **OD-US06** | Verpleegkundige | Doorklikken naar originele rapportage | Bronverificatie | 🔴 Hoog | +| **OD-US07** | Verpleegkundige | Risico's zien met ernst-niveau | Weet wat aandacht nodig heeft | 🟡 Middel | +| **OD-US08** | Verpleegkundige | AI-samenvatting genereren | Snelle overdracht in 30 sec | 🔴 Hoog | +| **OD-US09** | Verpleegkundige | In AI-samenvatting bronnen zien | Weet waar info vandaan komt | 🔴 Hoog | +| **OD-US10** | Verpleegkundige | Vanuit AI-samenvatting doorklikken naar bron | Kan details checken | 🟡 Middel | + +### Nieuwe User Stories (Dagregistratie) + +| ID | Rol | Doel / Actie | Verwachte waarde | Prio | +|----|-----|--------------|------------------|------| +| **OD-US11** | Verpleegkundige | Tijdens dienst snelle notitie toevoegen | Registreer gebeurtenis direct | 🔴 Hoog | +| **OD-US12** | Verpleegkundige | Notitie categoriseren (medicatie/ADL/gedrag/incident) | Heldere structuur | 🔴 Hoog | +| **OD-US13** | Verpleegkundige | Markeren welke notities relevant zijn voor overdracht | Controle over wat gedeeld wordt | 🔴 Hoog | +| **OD-US14** | Verpleegkundige | Overzicht van dagnotities zien | Snel terugkijken wat er gebeurd is | 🔴 Hoog | +| **OD-US15** | Verpleegkundige | Dagnotitie bewerken/verwijderen | Correctie mogelijk | 🟡 Middel | +| **OD-US16** | Psychiater | Alleen relevante notities in overdracht zien | Geen informatie-overload | 🔴 Hoog | +| **OD-US17** | Psychiater | AI-samenvatting inclusief dagnotities | Compleet beeld van dienst | 🔴 Hoog | + +--- + +## 4. Functionele werking per onderdeel + +🎯 **Doel:** Per hoofdonderdeel beschrijven wat de gebruiker kan doen en wat het systeem doet. + +### 4.1 Dagregistratie Module (NIEUW) + +**Route:** `/epd/dagregistratie/[patientId]` + +**Doel:** Snelle registratie tijdens dienst van operationele gebeurtenissen die relevant kunnen zijn voor overdracht, maar niet in het behandelverloop (decursus) horen. + +**Functionaliteit:** + +| Actie | Beschrijving | Systeem reactie | +|-------|--------------|-----------------| +| **Nieuwe notitie** | Klik "+ Registratie" | Toon quick-entry form | +| **Categorie selecteren** | Dropdown: Medicatie, ADL, Gedrag, Incident, Observatie | Icoon + kleurcodering | +| **Tijd instellen** | Standaard: nu, Aanpasbaar | Timestamp registratie | +| **Tekst invoeren** | Kort tekstveld (max 500 chars) | Autosave draft | +| **Overdracht markeren** | Checkbox "Opnemen in overdracht" | Badge in lijst | +| **Opslaan** | Submit form | Insert nursing_log, refresh lijst | +| **Bewerken** | Klik op notitie | Inline edit mode | +| **Verwijderen** | Trash icon | Confirm dialog → delete | + +**UI Componenten:** + +``` +┌─────────────────────────────────────────┐ +│ Dagregistratie - Jan de Vries │ +│ Donderdag 5 december 2024 │ +├─────────────────────────────────────────┤ +│ [+ Nieuwe registratie] Filter: [Alle]│ +├─────────────────────────────────────────┤ +│ │ +│ 14:30 🔴 Incident [Overdracht] │ +│ Verbale escalatie bij groepsactiviteit │ +│ Collega heeft de-escalatie gedaan │ +│ [Bewerken] [Verwijderen] │ +│ │ +│ 12:00 💊 Medicatie [Overdracht] │ +│ Lithium geweigerd - "Voel me goed" │ +│ [Bewerken] [Verwijderen] │ +│ │ +│ 08:00 💊 Medicatie │ +│ Olanzapine 10mg toegediend conform │ +│ [Bewerken] [Verwijderen] │ +│ │ +│ 07:30 🍽️ ADL │ +│ Ontbijt volledig genuttigd │ +│ [Bewerken] [Verwijderen] │ +│ │ +└─────────────────────────────────────────┘ +``` + +**Quick Entry Form:** + +``` +┌─────────────────────────────────────┐ +│ Nieuwe registratie │ +├─────────────────────────────────────┤ +│ Categorie: [Incident ▼] │ +│ 💊 Medicatie │ +│ 🍽️ ADL/verzorging │ +│ 👤 Gedragsobservatie │ +│ 🔴 Incident │ +│ 📝 Algemene observatie │ +│ │ +│ Tijd: [14:30] [Nu] │ +│ │ +│ Omschrijving: │ +│ ┌─────────────────────────────────┐ │ +│ │ Patiënt werd geprikkeld tijdens │ │ +│ │ groepsactiviteit, verbale │ │ +│ │ escalatie. Collega heeft... │ │ +│ └─────────────────────────────────┘ │ +│ 234 / 500 karakters │ +│ │ +│ ☑️ Opnemen in overdracht │ +│ │ +│ [Annuleren] [Opslaan] │ +└─────────────────────────────────────┘ +``` + +**Data Flow:** + +``` +User input + ↓ +nursing_logs table + ↓ +Overdracht Detail (indien marked) + ↓ +AI Samenvatting (contextueel) +``` + +--- + +### 4.2 Overdracht Overzicht (`/epd/overdracht/`) + +**Context:** Level 1 - Alle patiënten voor vandaag + +[Rest blijft hetzelfde als origineel FO] + +**Layout:** + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ EPD Sidebar │ Overdracht │ +│ │ │ +│ ┌─────────┐ │ ┌──────────────────────────────────────────────┐ │ +│ │Dashboard│ │ │ Overdracht 🔴 3 alerts │ │ +│ │─────────│ │ │ Donderdag 5 december 2024 · 8 patiënten │ │ +│ │Cliënten │ │ ├──────────────────────────────────────────────┤ │ +│ │─────────│ │ │ [Alle patiënten (8)] [Met alerts (3)] │ │ +│ │Agenda │ │ ├──────────────────────────────────────────────┤ │ +│ │─────────│ │ │ │ │ +│ │►Overdracht│ │ ┌───────── ┌───────── ┌───────── │ │ +│ │─────────│ │ │ │ Jan V. │ │ Marie K.│ │ Piet B. │ │ │ +│ │Rapportage│ │ │ │ 🔴 2 │ │ OK │ │ 🟡 1 │ │ │ +│ └─────────┘ │ │ │ 67 jaar │ │ 45 jaar │ │ 52 jaar │ │ │ +│ │ │ │ [→] │ │ [→] │ │ [→] │ │ │ +│ │ │ └───────── └───────── └───────── │ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +**Patiënt Card Inhoud:** + +| Element | Toelichting | +|---------|-------------| +| Naam | Voornaam + initiaal achternaam | +| Alert badge | 🔴 Aantal hoog-risico items | +| Leeftijd | Berekend uit geboortedatum | +| Klik | → Navigeer naar detail pagina | + +**Filter Functionaliteit:** + +```typescript +// Pseudo-code +if (filter === 'met-alerts') { + patients = patients.filter(p => + p.high_risk_count > 0 || + p.abnormal_vitals_count > 0 || + p.marked_nursing_logs > 0 // NIEUW + ) +} +``` + +--- + +### 4.3 Patiënt Detail (`/epd/overdracht/[patientId]`) + +**Context:** Level 2 - specifieke patiënt overdracht informatie + +**Layout (uitgebreid met Dagnotities blok):** + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ ← Terug naar overzicht │ +├─────────────────────────────────────────────────────────────────┤ +│ Jan de Vries │ +│ ♂ 67 jaar · Depressieve stoornis, recidiverend │ +├────────────────────────────────┬────────────────────────────────┤ +│ │ │ +│ ┌─ Vitale Functies ────────┐ │ ┌─ AI Samenvatting ────────┐ │ +│ │ ... │ │ │ │ │ +│ └───────────────────────────┘ │ │ [Genereer samenvatting] │ │ +│ │ │ │ │ +│ ┌─ Rapportages ─────────────┐ │ │ Samenvatting... │ │ +│ │ Behandelverslagen (24u) │ │ │ │ │ +│ │ [3 rapportages] │ │ │ Aandachtspunten: │ │ +│ └───────────────────────────┘ │ │ • Medicatie geweigerd │ │ +│ │ │ • Incident vanmiddag │ │ +│ ┌─ Dagnotities ─────────────┐ │ │ │ │ ← NIEUW +│ │ VPK registraties vandaag │ │ │ Actiepunten: │ │ +│ │ [5 notities, 2 relevant] │ │ │ • Bloeddruk checken │ │ +│ │ → [Dagregistratie] │ │ │ │ │ +│ └───────────────────────────┘ │ └───────────────────────────┘ │ +│ │ │ +│ ┌─ Medicatie ───────────────┐ │ │ +│ │ [Placeholder] │ │ │ +│ └───────────────────────────┘ │ │ +│ │ │ +│ ┌─ Risico's ────────────────┐ │ │ +│ │ ... │ │ │ +│ └───────────────────────────┘ │ │ +│ │ │ +└────────────────────────────────┴────────────────────────────────┘ +``` + +--- + +### 4.4 Dagnotities Blok (NIEUW) + +**Doel:** Toon relevante verpleegkundige registraties in overdracht context + +**Inhoud:** + +``` +┌─ Dagnotities ─────────────────────────────────────┐ +│ Verpleegkundige registraties vandaag │ +│ │ +│ 14:30 🔴 Incident [Overdracht] │ +│ Verbale escalatie bij groepsactiviteit │ +│ Collega heeft de-escalatie gedaan │ +│ Bron: nursing_logs/789 │ +│ │ +│ 12:00 💊 Medicatie [Overdracht] │ +│ Lithium geweigerd - "Voel me goed" │ +│ Bron: nursing_logs/788 │ +│ │ +│ [Toon alle registraties (5)] → /dagregistratie/ │ +└────────────────────────────────────────────────────┘ +``` + +**Business Rules:** + +| Regel | Implementatie | +|-------|---------------| +| Toon alleen gemarkeerde notities | `WHERE include_in_handover = true` | +| Sorteer chronologisch (nieuw → oud) | `ORDER BY timestamp DESC` | +| Max 5 items inline | Rest via link naar dagregistratie | +| Urgentie kleuren | 🔴 Incident > 💊 Medicatie > 👤 Gedrag > 🍽️ ADL | + +--- + +## 5. Interacties met AI (functionele beschrijving) + +🎯 **Doel:** Uitleggen waar AI in de flow voorkomt en wat de gebruiker ziet. + +### 5.1 AI Overdracht Generator (uitgebreid) + +| Aspect | Beschrijving | +|--------|--------------| +| **Locatie** | AI Samenvatting blok op patiënt detail pagina | +| **Trigger** | Klik op "Genereer samenvatting" button | +| **Input context** | Vitals (vandaag) + Reports (24u) + **Nursing Logs (marked)** + Risks (actief) + Conditions (actief) | +| **Processing** | ~3-5 seconden, progress indicator | +| **Output** | Samenvatting + aandachtspunten (met bronnen) + actiepunten | + +### 5.2 AI Prompt Strategie (aangepast) + +**System Prompt Kernpunten:** +- Rol: Ervaren verpleegkundige die overdrachten maakt +- Taal: Nederlands, zakelijk, beknopt +- Focus: Veranderingen, zorgen, actiepunten +- **Bronvermelding:** Bij elk aandachtspunt de databron vermelden +- **Nieuwe data:** Verwerk nursing_logs als operationele context + +**Context Structuur voor AI (uitgebreid):** + +``` +PATIENT: [naam], [leeftijd] jaar + +DIAGNOSES: +- [diagnose 1] (source: conditions/[id]) +- [diagnose 2] (source: conditions/[id]) + +VITALE FUNCTIES (vandaag): +- Bloeddruk: 145/92 mmHg [VERHOOGD] (source: observations/[id], 14:30) +- Hartslag: 78 bpm [NORMAAL] (source: observations/[id], 14:30) + +RAPPORTAGES (laatste 24u): +- [14:15] Voortgangsnotitie: "..." (source: reports/[id]) +- [09:30] Contactmoment: "..." (source: reports/[id]) + +DAGREGISTRATIES (dienst vandaag, relevant voor overdracht): ← NIEUW +- [14:30] [INCIDENT] Verbale escalatie bij groepsactiviteit... (source: nursing_logs/789) +- [12:00] [MEDICATIE] Lithium geweigerd - "Voel me goed" (source: nursing_logs/788) + +RISICO'S: +- [HOOG] Suïcidaliteit: "..." (source: risk_assessments/[id]) +- [MIDDEL] Zelfverwaarlozing: "..." (source: risk_assessments/[id]) +``` + +**Expected Output (voorbeeld):** + +```json +{ + "samenvatting": "67-jarige man met recidiverende depressie. Medicatie-therapietrouw problematisch vandaag (Lithium geweigerd), incident vanmiddag met verbale escalatie.", + "aandachtspunten": [ + { + "tekst": "Lithium medicatie geweigerd om 12:00 - patiënt geeft aan zich goed te voelen", + "urgent": false, + "bron": { + "type": "dagnotitie", + "id": "nursing-788", + "datum": "05-12-2024 12:00", + "label": "Medicatie registratie" + } + }, + { + "tekst": "Incident 14:30 - verbale escalatie tijdens groepsactiviteit, de-escalatie door collega", + "urgent": true, + "bron": { + "type": "dagnotitie", + "id": "nursing-789", + "datum": "05-12-2024 14:30", + "label": "Incident registratie" + } + }, + { + "tekst": "Bloeddruk verhoogd (145/92) - monitoring nodig", + "urgent": false, + "bron": { + "type": "observatie", + "id": "obs-456", + "datum": "05-12-2024 14:30", + "label": "Vitale functies" + } + } + ], + "actiepunten": [ + "Overleg arts over medicatie-weigering en compliance", + "Bloeddruk controleren over 2 uur", + "Evalueer triggers voor incident in behandelplan" + ] +} +``` + +--- + +## 6. Database Schema (NIEUW) + +🎯 **Doel:** Duidelijk maken welke nieuwe data-structuren nodig zijn. + +### 6.1 Nieuwe Tabel: nursing_logs + +```sql +CREATE TABLE nursing_logs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + patient_id UUID NOT NULL REFERENCES patients(id) ON DELETE CASCADE, + + -- Timing + shift_date DATE NOT NULL, -- Voor filtering per dienst + timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + -- Content + category TEXT NOT NULL CHECK (category IN ('medicatie', 'adl', 'gedrag', 'incident', 'observatie')), + content TEXT NOT NULL, -- Max 500 chars in UI + + -- Overdracht + include_in_handover BOOLEAN NOT NULL DEFAULT false, + + -- Metadata + created_by UUID NOT NULL REFERENCES auth.users(id), + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- Indexes +CREATE INDEX idx_nursing_logs_patient ON nursing_logs(patient_id); +CREATE INDEX idx_nursing_logs_shift ON nursing_logs(shift_date); +CREATE INDEX idx_nursing_logs_handover ON nursing_logs(patient_id, include_in_handover); +``` + +**Verschil met reports tabel:** + +| Aspect | reports | nursing_logs | +|--------|---------|--------------| +| **Doel** | Behandelverloof (decursus) | Operationele dagregistratie | +| **Lengte** | Lang (rich text) | Kort (max 500 chars) | +| **Levensduur** | Jaren | Dagen/weken | +| **In overdracht** | Soms | Frequent | +| **Gebruiker** | Behandelaar + VPK | Voornamelijk VPK | + +--- + +## 7. Gebruikersflows (uitgebreid) + +🎯 **Doel:** Laten zien hoe de gebruiker stap-voor-stap door het systeem gaat. + +### Flow 1: Dagelijkse Overdracht (aangepast) + +``` +1. Verpleegkundige opent Overdracht pagina +2. Ziet grid van alle patiënten voor vandaag +3. Filtert eventueel op "Met alerts" +4. Klikt op patiënt voor detail view +5. Bekijkt informatieblokken: + - Vitals + - Behandelrapportages + - Dagnotities (NIEUW) + - Risico's +6. Klikt "Genereer samenvatting" +7. AI maakt beknopte overdracht (incl. dagnotities) +8. Verpleegkundige gebruikt samenvatting voor overdracht aan psychiater +``` + +### Flow 2: Registratie Tijdens Dienst (NIEUW) + +``` +1. Verpleegkundige tijdens dienst: gebeurtenis met patiënt +2. Opent Dagregistratie module voor die patiënt +3. Klikt "+ Nieuwe registratie" +4. Vult in: + - Categorie (bijv. Incident) + - Tijd (standaard: nu) + - Omschrijving (kort, 2-3 zinnen) + - ☑️ "Opnemen in overdracht" +5. Slaat op +6. Notitie verschijnt in lijst met [Overdracht] badge +7. Aan eind dienst: notitie automatisch in overdracht-view +``` + +### Flow 3: Psychiater Bekijkt Overdracht (NIEUW) + +``` +1. Psychiater opent Overdracht dashboard +2. Ziet lijst met patiënten waarvan VPK dienst had +3. Opent patiënt detail +4. Bekijkt Dagnotities blok: + - Ziet alleen gemarkeerde items + - Leest 2 incidenten + 1 medicatie-weigering +5. Klikt "Genereer samenvatting" +6. AI vat samen: "Vandaag 2 incidenten, medicatie geweigerd, verhoogde bloeddruk" +7. Psychiater bespreekt met VPK of patiënt +``` + +--- + +## 8. Gebruikersrollen en rechten + +🎯 **Doel:** Beschrijven welke rollen toegang hebben. + +### MVP: Uniform toegangsmodel + +| Rol | Dagregistratie | Overdracht Dashboard | Beperkingen | +|-----|----------------|---------------------|-------------| +| Verpleegkundige | Volledige CRUD | Volledige functionaliteit | Alleen eigen patiënten | +| Psychiater | Read-only | Volledige functionaliteit | Alleen eigen patiënten | + +### Data Scoping (uitgebreid) + +- **Patiënten:** Gefilterd op actieve encounters vandaag +- **Reports:** Gefilterd op `created_by` of team-toegang +- **Nursing Logs:** Gefilterd op shift_date (vandaag) en `created_by` +- **Risico's:** Via intake → patient relatie + +--- + +## 9. Niet in Scope (aangepast) + +🎯 **Doel:** Duidelijk maken wat (nog) niet wordt gebouwd. + +| Feature | Reden exclusie | +|---------|----------------| +| Medicatie-invoer | Alleen weergave, CRUD is aparte module | +| Templating dagnotities | Vrije tekst is sneller voor MVP | +| Historische dagnotities | Alleen vandaag, archivering later | +| Multi-afdeling view | Te complex voor MVP, alleen eigen patiënten | +| Historische trends | Geen grafieken of lange termijn overzichten | +| Notificaties/push | Geen realtime alerts | +| Print/export | Geen PDF of print functionaliteit | +| Rechten per rol | Beperkt onderscheid VPK/arts (komt later) | +| Metingen invoer | Aparte functionaliteit, hier alleen weergave | +| Dicteer-functie | Typen is snel genoeg voor korte notities | + +--- + +## 10. Succescriteria (uitgebreid) + +🎯 **Doel:** Objectieve meetlat voor een geslaagde oplevering. + +- [ ] Overzicht laadt binnen 2 seconden +- [ ] Patiënt detail toont alle 6 informatieblokken correct (incl. dagnotities) +- [ ] Dagregistratie form submit < 1 seconde +- [ ] AI samenvatting genereert binnen 5 seconden +- [ ] AI output is begrijpelijk en medisch relevant +- [ ] AI integreert dagnotities correct in samenvatting +- [ ] Navigatie tussen overzicht, detail en dagregistratie werkt vlot +- [ ] Alerts (hoog risico, afwijkende vitals, markeerde notities) zijn direct zichtbaar +- [ ] Empty states bij ontbrekende data zijn informatief +- [ ] Dagnotities met [Overdracht] badge zijn duidelijk herkenbaar + +--- + +## 11. Risico's & Mitigatie (uitgebreid) + +🎯 **Doel:** Risico's vroeg signaleren en plannen hoe ermee om te gaan. + +| Risico | Impact | Mitigatie | +|--------|--------|-----------| +| AI samenvatting te lang/vaag | Hoog | Strikte prompt met max lengte, testen met echte data | +| Geen vitale functies in systeem | Middel | Graceful empty state, instructie om metingen toe te voegen | +| Medicatie tabel bestaat niet | Middel | Placeholder blok met "Binnenkort beschikbaar" | +| Performance bij veel patiënten | Middel | Parallel queries, pagination indien nodig | +| Risico's gekoppeld aan intake ipv patient | Laag | Query via intake tabel | +| VPK vergeet notities markeren | Hoog | AI pre-selectie als backup | +| Dubbele registratie (reports + nursing_logs) | Middel | Duidelijke UI scheiding + training | +| Nursing_logs niet archiveren | Laag | Auto-delete na 30 dagen (fase 2) | + +--- + +## 12. Roadmap / Vervolg (Post-MVP) + +🎯 **Doel:** Richting geven aan toekomstige uitbreidingen. + +| Fase | Feature | Beschrijving | +|------|---------|--------------| +| 2 | Medicatie module | Volledige CRUD voor medicatie, koppeling met nursing_logs | +| 3 | Templates dagnotities | Snelle keuzes: "Medicatie conform", "Eetpatroon normaal" | +| 4 | Archivering | Auto-delete nursing_logs > 30 dagen, export naar archief | +| 5 | Trend grafieken | Vitale functies over tijd | +| 6 | PDF export | Formele overdracht documenten | +| 7 | Agenda integratie | Koppeling met encounters/afspraken | +| 8 | Notificaties | Alerts bij kritieke waarden | +| 9 | Multi-afdeling | Overzicht meerdere afdelingen | +| 10 | Spraak-naar-tekst | Dicteer dagnotities (Deepgram integratie) | + +--- + +## 13. Bijlagen & Referenties + +🎯 **Doel:** Linken naar gerelateerde documenten. + +### Interne Documenten + +| Document | Status | +|----------|--------| +| PRD Overdracht Dashboard v1.0 | ✅ Gereed | +| FO Overdracht Dashboard v1.1 | ✅ Gereed (dit document) | +| TO Overdracht Dashboard | 📋 Nog te schrijven | +| UX Stylesheet | ✅ Beschikbaar | + +### Database Tabellen (uitgebreid) + +| Tabel | Velden gebruikt | Nieuw? | +|-------|-----------------|--------| +| `nursing_logs` | id, patient_id, shift_date, timestamp, category, content, include_in_handover, created_by | ✅ Ja | +| `observations` | id, patient_id, code_display, value_quantity_value, interpretation_code, effective_datetime | Bestaand | +| `reports` | id, patient_id, type, content, created_at, created_by | Bestaand | +| `risk_assessments` | id, intake_id, risk_type, risk_level, rationale, measures, assessment_date | Bestaand | +| `conditions` | id, patient_id, code_display, clinical_status | Bestaand | +| `patients` | id, name_given, name_family, birth_date, gender | Bestaand | +| `encounters` | id, patient_id, period_start, type_display | Bestaand | + +### Externe Referenties + +| Bron | Gebruik | +|------|---------| +| shadcn/ui | Card, Button, Badge, Form components | +| Lucide React | Icons (Pill, Utensils, User, AlertTriangle) | +| Anthropic Claude API | AI samenvatting generatie | +| Supabase RLS | Row-level security voor nursing_logs | + +### UI Component Hergebruik + +| Component | Locatie | Hergebruik voor | +|-----------|---------|-----------------| +| Quick Entry Form | Rapportage module | Nursing logs form | +| Badge component | Risico's | Overdracht marker | +| Card layout | Behandelplan | Informatie blokken | +| AI Button | Behandelplan | Genereer samenvatting | + +--- + +## Changelog + +| Versie | Datum | Auteur | Wijziging | +|--------|-------|--------|-----------| +| v1.1 | 05-12-2024 | Colin | Dagregistratie module toegevoegd, nursing_logs tabel, uitgebreide flows | +| v1.0 | 05-12-2024 | Colin | Initieel FO met focus op traceerbaarheid en bronvermelding | + +--- + +**Einde Functioneel Ontwerp - Overdracht Dashboard v1.1** diff --git a/docs/specs/overdracht/outline-overdracht.md b/docs/specs/overdracht/outline-overdracht.md new file mode 100644 index 0000000..0b9e140 --- /dev/null +++ b/docs/specs/overdracht/outline-overdracht.md @@ -0,0 +1,172 @@ +# Verpleegkundige Overdracht Dashboard + +## Implementatieplan + +### Stap 1: PRD aanmaken +Maak folder en PRD bestand: +- `docs/specs/overdracht/prd-overdracht-dashboard-v1.md` + +### Stap 2: Implementatie (na PRD) +Zie technisch plan onderaan. + +--- + +## PRD Content (te schrijven naar docs/specs/overdracht/prd-overdracht-dashboard-v1.md) + +```markdown +# 📄 Product Requirements Document (PRD) – Verpleegkundige Overdracht Dashboard + +**Projectnaam:** Verpleegkundige Overdracht Dashboard +**Versie:** v1.0 +**Datum:** 05-12-2024 +**Auteur:** Colin Lit + +--- + +## 1. Doelstelling + +Een dashboard voor verpleegkundigen die dagelijks meerdere overdrachten doen aan artsen. Het dashboard bundelt alle relevante patiëntinformatie (metingen, rapportages, medicatie, risico's) in overzichtelijke blokken en biedt een AI-functie om automatisch een beknopte overdracht-samenvatting te genereren. + +**Focus:** Snelheid en efficiëntie - verpleegkundigen hebben weinig tijd en moeten snel de juiste informatie kunnen vinden en overdragen. + +**Type:** MVP/Prototype met AI-integratie + +--- + +## 2. Doelgroep + +**Primaire gebruikers:** +- **Verpleegkundigen (GGZ):** Doen ~6 overdrachten per dag aan artsen/collega's. Hebben behoefte aan snel overzicht van patiëntstatus, wijzigingen en aandachtspunten. + +**Secundaire gebruikers:** +- **Artsen:** Ontvangen de overdracht, willen beknopte maar complete informatie. +- **Teamleiders:** Overzicht van alle patiënten en eventuele alerts. + +**Kernbehoeften:** +- Snel overzicht van alle patiënten die overgedragen moeten worden +- Per patiënt: vitale functies, recente notities, medicatie, risico's +- AI-hulp om overdracht samen te vatten in 30 seconden + +--- + +## 3. Kernfunctionaliteiten (MVP-scope) + +### 3.1 Niveau 1: Behandelaar Overzicht +1. **Patiëntenlijst:** Grid van alle actieve patiënten met afspraken/encounters vandaag +2. **Quick Stats per patiënt:** Naam, leeftijd, aantal alerts, recente activiteit +3. **Filter op alerts:** Toon alleen patiënten met hoog risico of afwijkende metingen +4. **Doorklik naar detail:** Navigatie naar patiënt-specifiek overdracht scherm + +### 3.2 Niveau 2: Patiënt Detail +5. **Informatieblokken:** + - **Vitale functies:** Metingen van vandaag (bloeddruk, hartslag, temperatuur, O2, ademhaling) + - **Rapportages:** Recente notities en observaties (laatste 24 uur) + - **Medicatie:** Huidige medicatie en recente wijzigingen *(placeholder voor MVP)* + - **Risico's:** Actieve risicotaxaties met ernst-niveau + +6. **AI Samenvatting Blok:** + - Eén compact blok met "Genereer samenvatting" knop + - AI genereert beknopte overdracht op basis van alle informatieblokken + - Output: samenvatting (max 3 zinnen) + aandachtspunten + actiepunten + - Kan opnieuw gegenereerd worden bij nieuwe data + +### 3.3 AI Integratie +7. **Overdracht Generator:** + - Input: vitals + reports + risks + diagnoses + - Output: gestructureerde JSON met samenvatting, aandachtspunten, actiepunten + - Taal: Nederlands, zakelijk, beknopt + - Markering van urgente zaken met [URGENT] + +--- + +## 4. Gebruikersflows + +### Flow 1: Dagelijkse Overdracht +1. Verpleegkundige opent Overdracht pagina +2. Ziet grid van alle patiënten voor vandaag +3. Filtert eventueel op "Met alerts" +4. Klikt op patiënt voor detail view +5. Bekijkt informatieblokken (vitals, reports, risico's) +6. Klikt "Genereer samenvatting" +7. AI maakt beknopte overdracht +8. Verpleegkundige gebruikt samenvatting voor mondelinge/schriftelijke overdracht + +### Flow 2: Snelle Check bij Alert +1. Verpleegkundige ziet rode badge op patiënt-card (hoog risico) +2. Klikt direct door naar detail +3. Ziet welke vitale functies afwijkend zijn +4. Checkt bijbehorende rapportages +5. Neemt direct actie of escaleert + +--- + +## 5. Niet in Scope + +- **Medicatie-invoer:** Alleen weergave, geen CRUD (aparte module) +- **Multi-afdeling view:** Alleen eigen patiënten +- **Historische trends:** Geen grafieken of lange termijn overzichten +- **Notificaties/push:** Geen realtime alerts +- **Print/export:** Geen PDF of print functionaliteit +- **Rechten per rol:** Geen onderscheid verpleegkundige/arts (komt later) + +--- + +## 6. Succescriteria + +- [ ] Overzicht laadt binnen 2 seconden +- [ ] Patiënt detail toont alle 4 informatieblokken correct +- [ ] AI samenvatting genereert binnen 5 seconden +- [ ] AI output is begrijpelijk en medisch relevant +- [ ] Navigatie tussen overzicht en detail werkt vlot +- [ ] Alerts (hoog risico, afwijkende vitals) zijn direct zichtbaar + +--- + +## 7. Risico's & Mitigatie + +| Risico | Impact | Mitigatie | +|--------|--------|-----------| +| AI samenvatting te lang/vaag | Hoog | Strikte prompt met max lengte, testen met echte data | +| Geen vitale functies in systeem | Middel | Graceful empty state, instructie om metingen toe te voegen | +| Medicatie tabel bestaat niet | Middel | Placeholder blok met "Binnenkort beschikbaar" | +| Performance bij veel patiënten | Middel | Parallel queries, pagination indien nodig | + +--- + +## 8. Roadmap / Vervolg (Post-MVP) + +- **Fase 2:** Medicatie module met volledige CRUD +- **Fase 3:** Trend grafieken voor vitale functies over tijd +- **Fase 4:** Export naar PDF voor formele overdracht +- **Fase 5:** Integratie met agenda (koppeling encounters) +- **Fase 6:** Notificaties bij kritieke waarden + +--- + +## 9. Bijlagen & Referenties + +- **Bestaande AI integratie:** `/app/api/behandelplan/generate/route.ts` (prompt pattern) +- **Database schema:** `observations`, `reports`, `risk_assessments` tabellen +- **UI componenten:** `/components/ui/ai-button.tsx`, Card components +- **Gerelateerde docs:** `docs/specs/ai-integratie/`, `docs/design/datamodel-documentatie.md` +``` + +--- + +## Technisch Plan (na PRD goedkeuring) + +### Route Structuur +``` +/epd/overdracht/ → Level 1: Overzicht +/epd/overdracht/[patientId] → Level 2: Detail +``` + +### Te maken bestanden +1. `docs/specs/overdracht/prd-overdracht-dashboard-v1.md` (PRD) +2. `/lib/types/handover.ts` (types) +3. `/app/epd/overdracht/actions.ts` (server actions) +4. `/app/epd/overdracht/page.tsx` (overzicht) +5. `/app/epd/overdracht/[patientId]/page.tsx` (detail) +6. `/app/api/overdracht/generate/route.ts` (AI endpoint) +7. `/components/overdracht/*.tsx` (UI componenten) +8. Update `/app/epd/components/epd-sidebar.tsx` (navigatie) diff --git a/docs/specs/overdracht/prd-overdracht-dashboard-v1.md b/docs/specs/overdracht/prd-overdracht-dashboard-v1.md new file mode 100644 index 0000000..16f9fb1 --- /dev/null +++ b/docs/specs/overdracht/prd-overdracht-dashboard-v1.md @@ -0,0 +1,197 @@ +# 📄 Product Requirements Document (PRD) – Verpleegkundige Overdracht Dashboard + +**Projectnaam:** Verpleegkundige Overdracht Dashboard +**Versie:** v1.0 +**Datum:** 05-12-2024 +**Auteur:** Colin Lit + +--- + +## 1. Doelstelling + +🎯 **Doel:** Een dashboard voor verpleegkundigen die dagelijks meerdere overdrachten doen aan artsen. Het dashboard bundelt alle relevante patiëntinformatie (metingen, rapportages, medicatie, risico's) in overzichtelijke blokken en biedt een AI-functie om automatisch een beknopte overdracht-samenvatting te genereren. + +**Focus:** Snelheid en efficiëntie - verpleegkundigen hebben weinig tijd en moeten snel de juiste informatie kunnen vinden en overdragen. + +**Type:** MVP/Prototype met AI-integratie + +> Een verpleegkundige doet gemiddeld 6 overdrachten per dag aan artsen. Met dit dashboard kan zij in 30 seconden een complete overdracht genereren, inclusief AI-samenvatting met aandachtspunten en actiepunten. + +--- + +## 2. Doelgroep + +🎯 **Doel:** Schets wie de eindgebruikers, stakeholders en testers zijn. + +**Primaire gebruikers:** +- **Verpleegkundigen (GGZ):** Doen ~6 overdrachten per dag aan artsen/collega's. Hebben behoefte aan snel overzicht van patiëntstatus, wijzigingen en aandachtspunten. + +**Secundaire gebruikers:** +- **Artsen:** Ontvangen de overdracht, willen beknopte maar complete informatie. +- **Teamleiders:** Overzicht van alle patiënten en eventuele alerts. + +**Kernbehoeften:** +- Snel overzicht van alle patiënten die overgedragen moeten worden +- Per patiënt: vitale functies, recente notities, medicatie, risico's +- AI-hulp om overdracht samen te vatten in 30 seconden + +--- + +## 3. Kernfunctionaliteiten (MVP-scope) + +🎯 **Doel:** Afbakenen van de minimale werkende functies. + +### 3.1 Niveau 1: Behandelaar Overzicht (`/epd/overdracht/`) + +| # | Functie | Beschrijving | +|---|---------|--------------| +| 1 | **Patiëntenlijst** | Grid van alle actieve patiënten met afspraken/encounters vandaag | +| 2 | **Quick Stats per patiënt** | Naam, leeftijd, aantal alerts, recente activiteit | +| 3 | **Filter op alerts** | Toon alleen patiënten met hoog risico of afwijkende metingen | +| 4 | **Doorklik naar detail** | Navigatie naar patiënt-specifiek overdracht scherm | + +### 3.2 Niveau 2: Patiënt Detail (`/epd/overdracht/[patientId]`) + +| # | Functie | Beschrijving | +|---|---------|--------------| +| 5 | **Vitale functies blok** | Metingen van vandaag (bloeddruk, hartslag, temperatuur, O2, ademhaling) | +| 6 | **Rapportages blok** | Recente notities en observaties (laatste 24 uur) | +| 7 | **Medicatie blok** | Huidige medicatie en recente wijzigingen *(placeholder voor MVP)* | +| 8 | **Risico's blok** | Actieve risicotaxaties met ernst-niveau | +| 9 | **AI Samenvatting blok** | Compact blok met "Genereer samenvatting" knop | + +### 3.3 AI Integratie + +| # | Functie | Beschrijving | +|---|---------|--------------| +| 10 | **Overdracht Generator** | AI genereert beknopte overdracht op basis van alle informatieblokken | +| 11 | **Gestructureerde output** | Samenvatting (max 3 zinnen) + aandachtspunten + actiepunten | +| 12 | **Urgentie markering** | Urgente zaken gemarkeerd met [URGENT] | + +--- + +## 4. Gebruikersflows (Demo- of MVP-flows) + +🎯 **Doel:** Laten zien hoe de gebruiker stap-voor-stap door het systeem gaat. + +### Flow 1: Dagelijkse Overdracht + +``` +1. Verpleegkundige opent Overdracht pagina +2. Ziet grid van alle patiënten voor vandaag +3. Filtert eventueel op "Met alerts" +4. Klikt op patiënt voor detail view +5. Bekijkt informatieblokken (vitals, reports, risico's) +6. Klikt "Genereer samenvatting" +7. AI maakt beknopte overdracht +8. Verpleegkundige gebruikt samenvatting voor mondelinge/schriftelijke overdracht +``` + +### Flow 2: Snelle Check bij Alert + +``` +1. Verpleegkundige ziet rode badge op patiënt-card (hoog risico) +2. Klikt direct door naar detail +3. Ziet welke vitale functies afwijkend zijn +4. Checkt bijbehorende rapportages +5. Neemt direct actie of escaleert +``` + +--- + +## 5. Niet in Scope + +🎯 **Doel:** Duidelijk maken wat (nog) niet wordt gebouwd. + +| Feature | Reden exclusie | +|---------|----------------| +| Medicatie-invoer | Alleen weergave, CRUD is aparte module | +| Multi-afdeling view | Te complex voor MVP, alleen eigen patiënten | +| Historische trends | Geen grafieken of lange termijn overzichten | +| Notificaties/push | Geen realtime alerts | +| Print/export | Geen PDF of print functionaliteit | +| Rechten per rol | Geen onderscheid verpleegkundige/arts (komt later) | +| Metingen invoer | Aparte functionaliteit, hier alleen weergave | + +--- + +## 6. Succescriteria + +🎯 **Doel:** Objectieve meetlat voor een geslaagde oplevering. + +- [ ] Overzicht laadt binnen 2 seconden +- [ ] Patiënt detail toont alle 4 informatieblokken correct +- [ ] AI samenvatting genereert binnen 5 seconden +- [ ] AI output is begrijpelijk en medisch relevant +- [ ] Navigatie tussen overzicht en detail werkt vlot +- [ ] Alerts (hoog risico, afwijkende vitals) zijn direct zichtbaar +- [ ] Empty states bij ontbrekende data zijn informatief + +--- + +## 7. Risico's & Mitigatie + +🎯 **Doel:** Risico's vroeg signaleren en plannen hoe ermee om te gaan. + +| Risico | Impact | Mitigatie | +|--------|--------|-----------| +| AI samenvatting te lang/vaag | Hoog | Strikte prompt met max lengte, testen met echte data | +| Geen vitale functies in systeem | Middel | Graceful empty state, instructie om metingen toe te voegen | +| Medicatie tabel bestaat niet | Middel | Placeholder blok met "Binnenkort beschikbaar" | +| Performance bij veel patiënten | Middel | Parallel queries, pagination indien nodig | +| Risico's gekoppeld aan intake ipv patient | Laag | Query via intake tabel | + +--- + +## 8. Roadmap / Vervolg (Post-MVP) + +🎯 **Doel:** Richting geven aan toekomstige uitbreidingen. + +| Fase | Feature | Beschrijving | +|------|---------|--------------| +| 2 | Medicatie module | Volledige CRUD voor medicatie | +| 3 | Trend grafieken | Vitale functies over tijd | +| 4 | PDF export | Formele overdracht documenten | +| 5 | Agenda integratie | Koppeling met encounters/afspraken | +| 6 | Notificaties | Alerts bij kritieke waarden | +| 7 | Multi-afdeling | Overzicht meerdere afdelingen | + +--- + +## 9. Bijlagen & Referenties + +🎯 **Doel:** Bronnen koppelen voor context en consistentie. + +### Bestaande Code Patterns + +| Pattern | Locatie | Hergebruik voor | +|---------|---------|-----------------| +| AI integratie | `/app/api/behandelplan/generate/route.ts` | Overdracht generator | +| Server actions | `/app/epd/patients/[id]/behandelplan/actions.ts` | Data fetching | +| Card components | `/components/ui/card.tsx` | Informatieblokken | +| AI Button | `/components/ui/ai-button.tsx` | Genereer knop | + +### Database Tabellen + +| Tabel | Gebruik | +|-------|---------| +| `observations` | Vitale functies (FHIR-compliant) | +| `reports` | Rapportages/notities | +| `risk_assessments` | Risico's (via intakes) | +| `conditions` | Diagnoses | +| `patients`, `encounters` | Basis data | + +### Gerelateerde Documentatie + +- FO (Functioneel Ontwerp) – *nog te schrijven* +- TO (Technisch Ontwerp) – *nog te schrijven* +- `docs/specs/ai-integratie/` – AI prompt patterns +- `docs/design/datamodel-documentatie.md` – Database schema + +--- + +## Changelog + +| Versie | Datum | Auteur | Wijziging | +|--------|-------|--------|-----------| +| v1.0 | 05-12-2024 | Colin | Initieel PRD | diff --git a/docs/specs/overdracht/to-overdracht-dashboard-v1.md b/docs/specs/overdracht/to-overdracht-dashboard-v1.md new file mode 100644 index 0000000..cfa6443 --- /dev/null +++ b/docs/specs/overdracht/to-overdracht-dashboard-v1.md @@ -0,0 +1,806 @@ +# Technisch Ontwerp (TO) - Verpleegkundige Overdracht Dashboard + +**Projectnaam:** Verpleegkundige Overdracht Dashboard +**Versie:** v1.0 +**Datum:** 05-12-2024 +**Auteur:** Claude (AI-assisted) + +--- + +## 1. Doel en relatie met PRD en FO + +**Doel van dit document:** +Dit Technisch Ontwerp beschrijft **hoe** het Overdracht Dashboard technisch wordt gebouwd. Het vertaalt de functionele specificaties uit FO v1.1 naar concrete technische implementatie. + +**Relatie met andere documenten:** +- PRD: `prd-overdracht-dashboard-v1.md` - wat en waarom +- FO: `fo-overdracht-dashboard-v1.1.md` - hoe functioneel +- TO: dit document - hoe technisch + +**Technische haalbaarheidsanalyse:** +| Aspect | Status | Toelichting | +|--------|--------|-------------| +| Database tabellen | Deels aanwezig | patients, encounters, observations, reports, risk_assessments, conditions aanwezig. nursing_logs moet worden toegevoegd | +| Frontend framework | Aanwezig | Next.js 15 App Router met EPD layout | +| UI componenten | Aanwezig | shadcn/ui (Card, Button, Badge, Dialog, etc.) | +| AI integratie | Aanwezig | Claude API via behandelplan/generate pattern | +| Authentication | Aanwezig | Supabase Auth met RLS | + +--- + +## 2. Technische Architectuur Overzicht + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ Frontend (Next.js 15) │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ /epd/overdracht/ /epd/dagregistratie/[patientId] │ +│ ├── page.tsx ├── page.tsx │ +│ │ (Overzicht Grid) │ (Dagregistratie Module) │ +│ │ │ │ +│ └── [patientId]/ └── components/ │ +│ └── page.tsx ├── log-list.tsx │ +│ (Patiënt Detail) ├── log-form.tsx │ +│ └── log-card.tsx │ +│ │ +├─────────────────────────────────────────────────────────────────────┤ +│ API Routes (Next.js) │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ /api/overdracht/ │ +│ ├── patients/route.ts GET - Patiënten voor vandaag │ +│ ├── [patientId]/route.ts GET - Detail data voor patiënt │ +│ └── generate/route.ts POST - AI samenvatting genereren │ +│ │ +│ /api/nursing-logs/ │ +│ ├── route.ts GET/POST - CRUD nursing logs │ +│ └── [logId]/route.ts PATCH/DELETE - Update/delete log │ +│ │ +├─────────────────────────────────────────────────────────────────────┤ +│ Database (Supabase) │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ Bestaand: Nieuw: │ +│ ├── patients ├── nursing_logs │ +│ ├── encounters │ │ +│ ├── observations │ │ +│ ├── reports │ │ +│ ├── risk_assessments │ │ +│ ├── conditions │ │ +│ ├── intakes │ │ +│ └── ai_events │ │ +│ │ +├─────────────────────────────────────────────────────────────────────┤ +│ External Services │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ Claude API (Anthropic) AI samenvatting generatie │ +│ Supabase Auth Session-based authentication │ +│ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 3. Techstack Selectie + +| Component | Technologie | Argumentatie | Bestaand | +|-----------|-------------|--------------|----------| +| Frontend | Next.js 15 (App Router) | Bestaande codebase, SSR, TypeScript | Ja | +| Backend | Next.js API Routes | Co-located met frontend | Ja | +| Database | Supabase (PostgreSQL) | Realtime, auth included, RLS | Ja | +| AI | Claude claude-sonnet-4-20250514 | Bestaande integratie, Nederlands | Ja | +| Styling | TailwindCSS + shadcn/ui | Bestaande componenten | Ja | +| Validation | Zod | Type-safe validatie, bestaand pattern | Ja | +| State | React Server Components + Client | Minimale client state | Ja | + +--- + +## 4. Datamodel + +### 4.1 Nieuwe Tabel: nursing_logs + +```sql +-- Migratie: create_nursing_logs_table +CREATE TABLE public.nursing_logs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + patient_id UUID NOT NULL REFERENCES patients(id) ON DELETE CASCADE, + + -- Timing + shift_date DATE NOT NULL, + timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + -- Content + category TEXT NOT NULL CHECK (category IN ( + 'medicatie', 'adl', 'gedrag', 'incident', 'observatie' + )), + content TEXT NOT NULL CHECK (char_length(content) <= 500), + + -- Overdracht markering + include_in_handover BOOLEAN NOT NULL DEFAULT false, + + -- Metadata + created_by UUID NOT NULL REFERENCES auth.users(id), + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- Indexes voor performance +CREATE INDEX idx_nursing_logs_patient ON nursing_logs(patient_id); +CREATE INDEX idx_nursing_logs_shift ON nursing_logs(shift_date); +CREATE INDEX idx_nursing_logs_handover ON nursing_logs(patient_id, include_in_handover) + WHERE include_in_handover = true; +CREATE INDEX idx_nursing_logs_timestamp ON nursing_logs(patient_id, timestamp DESC); + +-- Updated_at trigger +CREATE OR REPLACE FUNCTION update_nursing_logs_updated_at() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER nursing_logs_updated_at + BEFORE UPDATE ON nursing_logs + FOR EACH ROW + EXECUTE FUNCTION update_nursing_logs_updated_at(); + +-- Comments +COMMENT ON TABLE nursing_logs IS + 'Operationele verpleegkundige dagregistraties - kort, snel, met overdracht-markering'; +COMMENT ON COLUMN nursing_logs.shift_date IS + 'Datum van de dienst (voor filtering per dag)'; +COMMENT ON COLUMN nursing_logs.include_in_handover IS + 'True als deze notitie relevant is voor overdracht'; +``` + +### 4.2 RLS Policies voor nursing_logs + +```sql +-- Enable RLS +ALTER TABLE nursing_logs ENABLE ROW LEVEL SECURITY; + +-- Policy: Authenticated users kunnen lezen +CREATE POLICY "nursing_logs_select_authenticated" ON nursing_logs + FOR SELECT + TO authenticated + USING (true); + +-- Policy: Authenticated users kunnen inserten +CREATE POLICY "nursing_logs_insert_authenticated" ON nursing_logs + FOR INSERT + TO authenticated + WITH CHECK (created_by = auth.uid()); + +-- Policy: Eigen logs kunnen updaten +CREATE POLICY "nursing_logs_update_own" ON nursing_logs + FOR UPDATE + TO authenticated + USING (created_by = auth.uid()) + WITH CHECK (created_by = auth.uid()); + +-- Policy: Eigen logs kunnen deleten +CREATE POLICY "nursing_logs_delete_own" ON nursing_logs + FOR DELETE + TO authenticated + USING (created_by = auth.uid()); +``` + +### 4.3 TypeScript Types + +```typescript +// lib/types/nursing-log.ts + +import { z } from 'zod'; + +export const NursingLogCategory = z.enum([ + 'medicatie', + 'adl', + 'gedrag', + 'incident', + 'observatie' +]); + +export type NursingLogCategory = z.infer; + +export const NursingLogSchema = z.object({ + id: z.string().uuid(), + patient_id: z.string().uuid(), + shift_date: z.string(), // ISO date + timestamp: z.string(), // ISO datetime + category: NursingLogCategory, + content: z.string().min(1).max(500), + include_in_handover: z.boolean(), + created_by: z.string().uuid(), + created_at: z.string(), + updated_at: z.string(), +}); + +export type NursingLog = z.infer; + +export const CreateNursingLogSchema = z.object({ + patient_id: z.string().uuid(), + category: NursingLogCategory, + content: z.string().min(1).max(500), + timestamp: z.string().optional(), // Default: now + include_in_handover: z.boolean().default(false), +}); + +export type CreateNursingLog = z.infer; + +export const UpdateNursingLogSchema = z.object({ + category: NursingLogCategory.optional(), + content: z.string().min(1).max(500).optional(), + timestamp: z.string().optional(), + include_in_handover: z.boolean().optional(), +}); + +export type UpdateNursingLog = z.infer; + +// Category display mapping +export const CATEGORY_CONFIG: Record = { + medicatie: { label: 'Medicatie', icon: 'Pill', color: 'blue' }, + adl: { label: 'ADL/verzorging', icon: 'Utensils', color: 'green' }, + gedrag: { label: 'Gedragsobservatie', icon: 'User', color: 'purple' }, + incident: { label: 'Incident', icon: 'AlertTriangle', color: 'red' }, + observatie: { label: 'Algemene observatie', icon: 'FileText', color: 'gray' }, +}; +``` + +### 4.4 Bestaande Tabellen (queries) + +```typescript +// Overdracht context queries + +// 1. Patiënten voor vandaag (via actieve encounters) +const patientsToday = supabase + .from('encounters') + .select(` + patient_id, + patients!inner ( + id, name_given, name_family, birth_date, gender + ) + `) + .gte('period_start', todayStart) + .lte('period_start', todayEnd) + .in('status', ['planned', 'in-progress']); + +// 2. Vitale functies (observations) vandaag +const vitalsToday = supabase + .from('observations') + .select('*') + .eq('patient_id', patientId) + .eq('category', 'vital-signs') + .gte('effective_datetime', todayStart) + .order('effective_datetime', { ascending: false }); + +// 3. Rapportages laatste 24 uur +const reportsLast24h = supabase + .from('reports') + .select('*') + .eq('patient_id', patientId) + .gte('created_at', last24h) + .is('deleted_at', null) + .order('created_at', { ascending: false }); + +// 4. Actieve risico's (via intake) +const activeRisks = supabase + .from('risk_assessments') + .select(` + *, + intakes!inner (patient_id) + `) + .eq('intakes.patient_id', patientId) + .in('risk_level', ['hoog', 'zeer_hoog', 'gemiddeld']); + +// 5. Actieve diagnoses +const activeConditions = supabase + .from('conditions') + .select('*') + .eq('patient_id', patientId) + .eq('clinical_status', 'active'); +``` + +--- + +## 5. API Ontwerp + +### 5.1 Overdracht Endpoints + +| Endpoint | Method | Input | Output | Auth | +|----------|--------|-------|--------|------| +| `/api/overdracht/patients` | GET | `?date=YYYY-MM-DD` | `PatientOverzicht[]` | Required | +| `/api/overdracht/[patientId]` | GET | - | `PatientDetail` | Required | +| `/api/overdracht/generate` | POST | `{ patientId }` | `AISamenvatting` | Required | + +### 5.2 Nursing Logs Endpoints + +| Endpoint | Method | Input | Output | Auth | +|----------|--------|-------|--------|------| +| `/api/nursing-logs` | GET | `?patientId&date` | `NursingLog[]` | Required | +| `/api/nursing-logs` | POST | `CreateNursingLog` | `NursingLog` | Required | +| `/api/nursing-logs/[logId]` | PATCH | `UpdateNursingLog` | `NursingLog` | Required | +| `/api/nursing-logs/[logId]` | DELETE | - | `204` | Required | + +### 5.3 Response Types + +```typescript +// lib/types/overdracht.ts + +// Patiënt overzicht (lijst view) +export interface PatientOverzicht { + id: string; + name_given: string[]; + name_family: string; + birth_date: string; + gender: string; + alerts: { + high_risk_count: number; + abnormal_vitals_count: number; + marked_logs_count: number; + }; +} + +// Patiënt detail (detail view) +export interface PatientDetail { + patient: { + id: string; + name_given: string[]; + name_family: string; + name_prefix?: string; + birth_date: string; + gender: string; + }; + vitals: VitalSign[]; + reports: Report[]; + nursingLogs: NursingLog[]; + risks: RiskAssessment[]; + conditions: Condition[]; +} + +// Vitale functie met interpretatie +export interface VitalSign { + id: string; + code_display: string; + value_quantity_value: number; + value_quantity_unit: string; + interpretation_code?: string; // 'H' | 'L' | 'N' + effective_datetime: string; + source_id: string; +} + +// AI Samenvatting output +export interface AISamenvatting { + samenvatting: string; + aandachtspunten: Aandachtspunt[]; + actiepunten: string[]; + generatedAt: string; + durationMs: number; +} + +export interface Aandachtspunt { + tekst: string; + urgent: boolean; + bron: { + type: 'observatie' | 'rapportage' | 'dagnotitie' | 'risico'; + id: string; + datum: string; + label: string; + }; +} +``` + +--- + +## 6. AI Integratie + +### 6.1 AI Overdracht Generator + +**Locatie:** `/api/overdracht/generate/route.ts` + +**Pattern:** Gebaseerd op bestaande `behandelplan/generate` implementatie. + +```typescript +// lib/ai/overdracht-prompt.ts + +export const OVERDRACHT_SYSTEM_PROMPT = `Je bent een ervaren verpleegkundige die overdrachten maakt in een GGZ-instelling. + +Je taak: Maak een beknopte, relevante overdracht voor de opvolgende dienst. + +## Outputformaat (JSON) +{ + "samenvatting": "1-2 zinnen over de patiënt en wat er speelt", + "aandachtspunten": [ + { + "tekst": "Beschrijving van het aandachtspunt", + "urgent": true/false, + "bron": { + "type": "observatie|rapportage|dagnotitie|risico", + "id": "source-id", + "datum": "DD-MM-YYYY HH:mm", + "label": "Korte beschrijving bron" + } + } + ], + "actiepunten": [ + "Concrete actie voor opvolgende dienst" + ] +} + +## Regels +1. Taal: Nederlands, zakelijk, beknopt +2. Focus op: veranderingen, afwijkingen, aandachtspunten +3. Elke aandachtspunt MOET een bronverwijzing hebben +4. Maximum 5 aandachtspunten, 3 actiepunten +5. Markeer urgent=true voor: + - Medicatie-weigering + - Incidenten + - Sterk afwijkende vitals + - Hoog-risico situaties +`; + +export function buildOverdrachtUserPrompt(context: OverdrachtContext): string { + const lines: string[] = [ + `PATIENT: ${context.patientName}, ${context.age} jaar, ${context.gender}`, + '', + 'DIAGNOSES:', + ...context.conditions.map(c => + `- ${c.code_display} (source: conditions/${c.id})` + ), + '', + 'VITALE FUNCTIES (vandaag):', + ...context.vitals.map(v => + `- ${v.code_display}: ${v.value_quantity_value} ${v.value_quantity_unit} ` + + `[${interpretationLabel(v.interpretation_code)}] ` + + `(source: observations/${v.id}, ${formatTime(v.effective_datetime)})` + ), + '', + 'RAPPORTAGES (laatste 24u):', + ...context.reports.map(r => + `- [${formatTime(r.created_at)}] ${r.type}: "${truncate(r.content, 200)}" ` + + `(source: reports/${r.id})` + ), + '', + 'DAGREGISTRATIES (relevant voor overdracht):', + ...context.nursingLogs.map(l => + `- [${formatTime(l.timestamp)}] [${l.category.toUpperCase()}] ${l.content} ` + + `(source: nursing_logs/${l.id})` + ), + '', + 'RISICOS:', + ...context.risks.map(r => + `- [${r.risk_level.toUpperCase()}] ${r.risk_type}: "${truncate(r.rationale, 150)}" ` + + `(source: risk_assessments/${r.id})` + ), + ]; + + return lines.join('\n'); +} +``` + +### 6.2 AI Event Logging + +```typescript +// Uitbreiding ai_events.kind enum +// Voeg toe: 'overdracht_generate' + +await supabase.from('ai_events').insert({ + kind: 'overdracht_generate', + patient_id: patientId, + request: { + vitalCount: context.vitals.length, + reportCount: context.reports.length, + logCount: context.nursingLogs.length, + riskCount: context.risks.length, + }, + response: { + aandachtspuntenCount: result.aandachtspunten.length, + actiepuntenCount: result.actiepunten.length, + urgentCount: result.aandachtspunten.filter(a => a.urgent).length, + }, + duration_ms: durationMs, +}); +``` + +--- + +## 7. Frontend Componenten + +### 7.1 Nieuwe Routes + +``` +app/epd/ +├── overdracht/ +│ ├── page.tsx # Overzicht grid +│ ├── [patientId]/ +│ │ └── page.tsx # Patiënt detail +│ └── components/ +│ ├── patient-card.tsx # Card in overzicht grid +│ ├── vitals-block.tsx # Vitale functies blok +│ ├── reports-block.tsx # Rapportages blok +│ ├── nursing-logs-block.tsx # Dagnotities blok +│ ├── risks-block.tsx # Risico's blok +│ └── ai-summary-block.tsx # AI samenvatting blok +│ +└── dagregistratie/ + └── [patientId]/ + ├── page.tsx # Dagregistratie module + └── components/ + ├── log-list.tsx # Lijst van notities + ├── log-form.tsx # Quick entry form + └── log-card.tsx # Individuele notitie card +``` + +### 7.2 Component Hergebruik + +| Nieuw Component | Hergebruik van | Locatie | +|-----------------|----------------|---------| +| patient-card.tsx | Card (shadcn), Badge | components/ui/ | +| ai-summary-block.tsx | AIButton pattern | behandelplan/ | +| log-form.tsx | Form patterns | screening/activity-log.tsx | +| risks-block.tsx | Card, Badge | intakes/risk/ | + +### 7.3 Sidebar Uitbreiding + +```typescript +// app/epd/components/epd-sidebar.tsx +// Voeg toe aan navigatie items: + +{ + title: 'Overdracht', + href: '/epd/overdracht', + icon: ClipboardList, + badge: alertCount > 0 ? alertCount : undefined, +}, +``` + +--- + +## 8. Security & Compliance + +### 8.1 Authentication Flow + +``` +User Request + │ + ▼ +┌─────────────────┐ +│ Supabase Auth │ ← Session cookie +│ (JWT validation)│ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ RLS Policies │ ← auth.uid() check +│ (Row filtering) │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ API Response │ +└─────────────────┘ +``` + +### 8.2 Security Checklist + +- [x] **Authentication:** Supabase Auth (bestaand) +- [x] **Authorization:** RLS policies op nursing_logs +- [x] **Data Encryption:** PostgreSQL at rest, HTTPS in transit +- [x] **Input Validation:** Zod schemas op alle endpoints +- [x] **CORS:** Via Next.js middleware (bestaand) +- [x] **Audit Trail:** ai_events tabel voor AI calls + +### 8.3 AVG/GDPR Compliance + +- **Data minimalisatie:** nursing_logs max 500 chars +- **Retention:** nursing_logs cleanup na 30 dagen (fase 2) +- **Audit:** created_by tracking op alle records +- **Access logging:** Via Supabase logs + +--- + +## 9. Performance & Scalability + +### 9.1 Performance Targets + +| Metric | Target | Implementatie | +|--------|--------|---------------| +| Overzicht load | < 2s | Parallel queries, index op encounters | +| Detail load | < 1.5s | Batch queries via Promise.all() | +| Log submit | < 500ms | Direct insert, optimistic UI | +| AI response | < 5s | Claude claude-sonnet-4-20250514, max_tokens=2048 | + +### 9.2 Database Indexes + +```sql +-- Bestaande indexes (verificatie) +CREATE INDEX IF NOT EXISTS idx_encounters_period ON encounters(period_start); +CREATE INDEX IF NOT EXISTS idx_observations_patient_datetime ON observations(patient_id, effective_datetime DESC); +CREATE INDEX IF NOT EXISTS idx_reports_patient_created ON reports(patient_id, created_at DESC); + +-- Nieuwe indexes voor overdracht +CREATE INDEX idx_nursing_logs_patient ON nursing_logs(patient_id); +CREATE INDEX idx_nursing_logs_shift ON nursing_logs(shift_date); +CREATE INDEX idx_nursing_logs_handover ON nursing_logs(patient_id, include_in_handover) + WHERE include_in_handover = true; +``` + +### 9.3 Query Optimalisatie + +```typescript +// Parallelle queries voor detail pagina +export async function getPatientDetail(patientId: string) { + const supabase = await createClient(); + const today = new Date().toISOString().split('T')[0]; + const last24h = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(); + + const [ + { data: patient }, + { data: vitals }, + { data: reports }, + { data: nursingLogs }, + { data: risks }, + { data: conditions }, + ] = await Promise.all([ + supabase.from('patients').select('*').eq('id', patientId).single(), + supabase.from('observations') + .select('*') + .eq('patient_id', patientId) + .eq('category', 'vital-signs') + .gte('effective_datetime', today) + .order('effective_datetime', { ascending: false }), + supabase.from('reports') + .select('*') + .eq('patient_id', patientId) + .gte('created_at', last24h) + .is('deleted_at', null) + .order('created_at', { ascending: false }), + supabase.from('nursing_logs') + .select('*') + .eq('patient_id', patientId) + .eq('shift_date', today) + .order('timestamp', { ascending: false }), + // Risks via intakes join + supabase.from('risk_assessments') + .select('*, intakes!inner(patient_id)') + .eq('intakes.patient_id', patientId), + supabase.from('conditions') + .select('*') + .eq('patient_id', patientId) + .eq('clinical_status', 'active'), + ]); + + return { patient, vitals, reports, nursingLogs, risks, conditions }; +} +``` + +--- + +## 10. Deployment & Implementatie + +### 10.1 Implementatie Volgorde + +``` +Fase 1: Database (1 migratie) +├── nursing_logs tabel +├── RLS policies +└── Indexes + +Fase 2: API Routes +├── /api/nursing-logs (CRUD) +├── /api/overdracht/patients +├── /api/overdracht/[patientId] +└── /api/overdracht/generate + +Fase 3: Frontend - Dagregistratie +├── /epd/dagregistratie/[patientId]/page.tsx +├── Log form component +└── Log list component + +Fase 4: Frontend - Overdracht +├── /epd/overdracht/page.tsx +├── /epd/overdracht/[patientId]/page.tsx +├── Info blokken (vitals, reports, logs, risks) +└── AI samenvatting blok + +Fase 5: Integratie +├── Sidebar link toevoegen +├── AI prompt refinement +└── Testing & polish +``` + +### 10.2 Migratie Commands + +```bash +# Database migratie +npx supabase migration new create_nursing_logs_table + +# TypeScript types regenereren +npx supabase gen types typescript --linked > lib/supabase/database.types.ts +``` + +--- + +## 11. Monitoring & Logging + +### 11.1 Key Metrics + +| Metric | Doel | Bron | +|--------|------|------| +| AI success rate | > 95% | ai_events.kind = 'overdracht_generate' | +| Avg AI duration | < 5s | ai_events.duration_ms | +| Log creations/day | Tracking | nursing_logs.created_at | +| Handover marks % | Tracking | nursing_logs.include_in_handover | + +### 11.2 Error Tracking + +```typescript +// Sentry error context (bestaand pattern) +Sentry.setContext('overdracht', { + patientId, + vitalCount: vitals.length, + reportCount: reports.length, + logCount: nursingLogs.length, +}); +``` + +--- + +## 12. Risico's & Technische Mitigatie + +| Risico | Impact | Waarschijnlijkheid | Mitigatie | +|--------|--------|-------------------|-----------| +| Claude API down | Hoog | Laag | Graceful error, toon data zonder AI | +| Veel patiënten (>50) | Middel | Middel | Pagination in overzicht, lazy loading | +| Lege data (geen vitals) | Laag | Hoog | Empty states per blok, instructieve tekst | +| AI hallucinaties | Hoog | Laag | Bronverwijzingen verplicht, validatie | +| Performance nursing_logs | Middel | Laag | Indexes, 30-dagen cleanup (fase 2) | + +--- + +## 13. Bijlagen & Referenties + +### 13.1 Projectdocumenten + +| Document | Status | +|----------|--------| +| PRD Overdracht Dashboard v1.0 | Gereed | +| FO Overdracht Dashboard v1.1 | Gereed | +| TO Overdracht Dashboard v1.0 | Gereed (dit document) | + +### 13.2 Code Locaties + +| Wat | Locatie | +|-----|---------| +| AI prompt pattern | `lib/ai/behandelplan-prompt.ts` | +| API route pattern | `app/api/reports/route.ts` | +| shadcn components | `components/ui/` | +| Supabase client | `lib/auth/server.ts` | +| Database types | `lib/supabase/database.types.ts` | + +### 13.3 Externe Documentatie + +| Bron | URL | +|------|-----| +| Next.js App Router | https://nextjs.org/docs/app | +| Supabase RLS | https://supabase.com/docs/guides/auth/row-level-security | +| Claude API | https://docs.anthropic.com/en/api | +| shadcn/ui | https://ui.shadcn.com | + +--- + +## Changelog + +| Versie | Datum | Auteur | Wijziging | +|--------|-------|--------|-----------| +| v1.0 | 05-12-2024 | Claude | Initieel TO gebaseerd op FO v1.1 | + +--- + +**Einde Technisch Ontwerp - Overdracht Dashboard v1.0** diff --git a/lib/supabase/database.types.ts b/lib/supabase/database.types.ts index dc84375..cf5fc51 100644 --- a/lib/supabase/database.types.ts +++ b/lib/supabase/database.types.ts @@ -129,6 +129,7 @@ export type Database = { based_on_examinations: string[] | null based_on_intake_id: string | null based_on_risk_assessments: string[] | null + behandelstructuur: Json | null care_team_ids: string[] | null category_code: string | null category_display: string | null @@ -137,6 +138,7 @@ export type Database = { created_date: string | null description: string | null encounter_id: string | null + evaluatiemomenten: Json | null goals: Json | null id: string identifier: string | null @@ -145,9 +147,13 @@ export type Database = { patient_id: string period_end: string | null period_start: string | null + published_at: string | null + sessie_planning: Json | null status: Database["public"]["Enums"]["careplan_status"] title: string updated_at: string | null + veiligheidsplan: Json | null + version: number | null } Insert: { activities?: Json | null @@ -157,6 +163,7 @@ export type Database = { based_on_examinations?: string[] | null based_on_intake_id?: string | null based_on_risk_assessments?: string[] | null + behandelstructuur?: Json | null care_team_ids?: string[] | null category_code?: string | null category_display?: string | null @@ -165,6 +172,7 @@ export type Database = { created_date?: string | null description?: string | null encounter_id?: string | null + evaluatiemomenten?: Json | null goals?: Json | null id?: string identifier?: string | null @@ -173,9 +181,13 @@ export type Database = { patient_id: string period_end?: string | null period_start?: string | null + published_at?: string | null + sessie_planning?: Json | null status?: Database["public"]["Enums"]["careplan_status"] title: string updated_at?: string | null + veiligheidsplan?: Json | null + version?: number | null } Update: { activities?: Json | null @@ -185,6 +197,7 @@ export type Database = { based_on_examinations?: string[] | null based_on_intake_id?: string | null based_on_risk_assessments?: string[] | null + behandelstructuur?: Json | null care_team_ids?: string[] | null category_code?: string | null category_display?: string | null @@ -193,6 +206,7 @@ export type Database = { created_date?: string | null description?: string | null encounter_id?: string | null + evaluatiemomenten?: Json | null goals?: Json | null id?: string identifier?: string | null @@ -201,9 +215,13 @@ export type Database = { patient_id?: string period_end?: string | null period_start?: string | null + published_at?: string | null + sessie_planning?: Json | null status?: Database["public"]["Enums"]["careplan_status"] title?: string updated_at?: string | null + veiligheidsplan?: Json | null + version?: number | null } Relationships: [ { @@ -765,6 +783,53 @@ export type Database = { } Relationships: [] } + nursing_logs: { + Row: { + category: string + content: string + created_at: string + created_by: string + id: string + include_in_handover: boolean + patient_id: string + shift_date: string + timestamp: string + updated_at: string + } + Insert: { + category: string + content: string + created_at?: string + created_by: string + id?: string + include_in_handover?: boolean + patient_id: string + shift_date: string + timestamp?: string + updated_at?: string + } + Update: { + category?: string + content?: string + created_at?: string + created_by?: string + id?: string + include_in_handover?: boolean + patient_id?: string + shift_date?: string + timestamp?: string + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "nursing_logs_patient_id_fkey" + columns: ["patient_id"] + isOneToOne: false + referencedRelation: "patients" + referencedColumns: ["id"] + }, + ] + } observations: { Row: { body_site: string | null diff --git a/lib/types/nursing-log.ts b/lib/types/nursing-log.ts new file mode 100644 index 0000000..3fabb5e --- /dev/null +++ b/lib/types/nursing-log.ts @@ -0,0 +1,114 @@ +import { z } from 'zod'; +import type { Database } from '@/lib/supabase/database.types'; + +// Database types +export type NursingLog = Database['public']['Tables']['nursing_logs']['Row']; +export type NursingLogInsert = Database['public']['Tables']['nursing_logs']['Insert']; +export type NursingLogUpdate = Database['public']['Tables']['nursing_logs']['Update']; + +// Category enum +export const NURSING_LOG_CATEGORIES = [ + 'medicatie', + 'adl', + 'gedrag', + 'incident', + 'observatie', +] as const; + +export type NursingLogCategory = (typeof NURSING_LOG_CATEGORIES)[number]; + +// Zod schemas for validation +export const NursingLogCategorySchema = z.enum(NURSING_LOG_CATEGORIES); + +export const CreateNursingLogSchema = z.object({ + patient_id: z.string().uuid('Patient ID moet een geldige UUID zijn'), + category: NursingLogCategorySchema, + content: z + .string() + .min(1, 'Notitie mag niet leeg zijn') + .max(500, 'Notitie mag maximaal 500 karakters bevatten'), + timestamp: z.string().datetime().optional(), + include_in_handover: z.boolean().default(false), +}); + +export type CreateNursingLogInput = z.infer; + +export const UpdateNursingLogSchema = z.object({ + category: NursingLogCategorySchema.optional(), + content: z + .string() + .min(1, 'Notitie mag niet leeg zijn') + .max(500, 'Notitie mag maximaal 500 karakters bevatten') + .optional(), + timestamp: z.string().datetime().optional(), + include_in_handover: z.boolean().optional(), +}); + +export type UpdateNursingLogInput = z.infer; + +// Response types +export interface NursingLogListResponse { + logs: NursingLog[]; + total: number; +} + +// Category display configuration +export const CATEGORY_CONFIG: Record< + NursingLogCategory, + { + label: string; + icon: string; + color: string; + bgColor: string; + textColor: string; + } +> = { + medicatie: { + label: 'Medicatie', + icon: 'Pill', + color: 'blue', + bgColor: 'bg-blue-100', + textColor: 'text-blue-700', + }, + adl: { + label: 'ADL/verzorging', + icon: 'Utensils', + color: 'green', + bgColor: 'bg-green-100', + textColor: 'text-green-700', + }, + gedrag: { + label: 'Gedragsobservatie', + icon: 'User', + color: 'purple', + bgColor: 'bg-purple-100', + textColor: 'text-purple-700', + }, + incident: { + label: 'Incident', + icon: 'AlertTriangle', + color: 'red', + bgColor: 'bg-red-100', + textColor: 'text-red-700', + }, + observatie: { + label: 'Algemene observatie', + icon: 'FileText', + color: 'gray', + bgColor: 'bg-gray-100', + textColor: 'text-gray-700', + }, +}; + +// Helper function to calculate shift_date from timestamp +export function calculateShiftDate(timestamp: Date | string): string { + const date = typeof timestamp === 'string' ? new Date(timestamp) : timestamp; + const hours = date.getHours(); + + // Night shift (before 7:00) belongs to previous day + if (hours < 7) { + date.setDate(date.getDate() - 1); + } + + return date.toISOString().split('T')[0]; +}