feat(overdracht): E0 + E1 - Database setup en API nursing logs
Epic 0 - Database Setup: - nursing_logs tabel met indexes en constraints - RLS policies (SELECT/INSERT/UPDATE/DELETE) - TypeScript types gegenereerd Epic 1 - API Nursing Logs: - GET/POST /api/nursing-logs (lijst + aanmaken) - PATCH/DELETE /api/nursing-logs/[id] (bewerken + verwijderen) - Zod validatie schemas - Automatische shift_date berekening Documentatie: - PRD, FO, TO en Bouwplan voor Overdracht Dashboard 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
182
app/api/nursing-logs/[id]/route.ts
Normal file
182
app/api/nursing-logs/[id]/route.ts
Normal file
@@ -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<string, unknown> = {};
|
||||||
|
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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
146
app/api/nursing-logs/route.ts
Normal file
146
app/api/nursing-logs/route.ts
Normal file
@@ -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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
314
docs/specs/overdracht/bouwplan-overdracht-v1.md
Normal file
314
docs/specs/overdracht/bouwplan-overdracht-v1.md
Normal file
@@ -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 |
|
||||||
642
docs/specs/overdracht/fo-overdracht-dashboard-v1.1.md
Normal file
642
docs/specs/overdracht/fo-overdracht-dashboard-v1.1.md
Normal file
@@ -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**
|
||||||
172
docs/specs/overdracht/outline-overdracht.md
Normal file
172
docs/specs/overdracht/outline-overdracht.md
Normal file
@@ -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)
|
||||||
197
docs/specs/overdracht/prd-overdracht-dashboard-v1.md
Normal file
197
docs/specs/overdracht/prd-overdracht-dashboard-v1.md
Normal file
@@ -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 |
|
||||||
806
docs/specs/overdracht/to-overdracht-dashboard-v1.md
Normal file
806
docs/specs/overdracht/to-overdracht-dashboard-v1.md
Normal file
@@ -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<typeof NursingLogCategory>;
|
||||||
|
|
||||||
|
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<typeof NursingLogSchema>;
|
||||||
|
|
||||||
|
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<typeof CreateNursingLogSchema>;
|
||||||
|
|
||||||
|
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<typeof UpdateNursingLogSchema>;
|
||||||
|
|
||||||
|
// Category display mapping
|
||||||
|
export const CATEGORY_CONFIG: Record<NursingLogCategory, {
|
||||||
|
label: string;
|
||||||
|
icon: string;
|
||||||
|
color: string;
|
||||||
|
}> = {
|
||||||
|
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**
|
||||||
@@ -129,6 +129,7 @@ export type Database = {
|
|||||||
based_on_examinations: string[] | null
|
based_on_examinations: string[] | null
|
||||||
based_on_intake_id: string | null
|
based_on_intake_id: string | null
|
||||||
based_on_risk_assessments: string[] | null
|
based_on_risk_assessments: string[] | null
|
||||||
|
behandelstructuur: Json | null
|
||||||
care_team_ids: string[] | null
|
care_team_ids: string[] | null
|
||||||
category_code: string | null
|
category_code: string | null
|
||||||
category_display: string | null
|
category_display: string | null
|
||||||
@@ -137,6 +138,7 @@ export type Database = {
|
|||||||
created_date: string | null
|
created_date: string | null
|
||||||
description: string | null
|
description: string | null
|
||||||
encounter_id: string | null
|
encounter_id: string | null
|
||||||
|
evaluatiemomenten: Json | null
|
||||||
goals: Json | null
|
goals: Json | null
|
||||||
id: string
|
id: string
|
||||||
identifier: string | null
|
identifier: string | null
|
||||||
@@ -145,9 +147,13 @@ export type Database = {
|
|||||||
patient_id: string
|
patient_id: string
|
||||||
period_end: string | null
|
period_end: string | null
|
||||||
period_start: string | null
|
period_start: string | null
|
||||||
|
published_at: string | null
|
||||||
|
sessie_planning: Json | null
|
||||||
status: Database["public"]["Enums"]["careplan_status"]
|
status: Database["public"]["Enums"]["careplan_status"]
|
||||||
title: string
|
title: string
|
||||||
updated_at: string | null
|
updated_at: string | null
|
||||||
|
veiligheidsplan: Json | null
|
||||||
|
version: number | null
|
||||||
}
|
}
|
||||||
Insert: {
|
Insert: {
|
||||||
activities?: Json | null
|
activities?: Json | null
|
||||||
@@ -157,6 +163,7 @@ export type Database = {
|
|||||||
based_on_examinations?: string[] | null
|
based_on_examinations?: string[] | null
|
||||||
based_on_intake_id?: string | null
|
based_on_intake_id?: string | null
|
||||||
based_on_risk_assessments?: string[] | null
|
based_on_risk_assessments?: string[] | null
|
||||||
|
behandelstructuur?: Json | null
|
||||||
care_team_ids?: string[] | null
|
care_team_ids?: string[] | null
|
||||||
category_code?: string | null
|
category_code?: string | null
|
||||||
category_display?: string | null
|
category_display?: string | null
|
||||||
@@ -165,6 +172,7 @@ export type Database = {
|
|||||||
created_date?: string | null
|
created_date?: string | null
|
||||||
description?: string | null
|
description?: string | null
|
||||||
encounter_id?: string | null
|
encounter_id?: string | null
|
||||||
|
evaluatiemomenten?: Json | null
|
||||||
goals?: Json | null
|
goals?: Json | null
|
||||||
id?: string
|
id?: string
|
||||||
identifier?: string | null
|
identifier?: string | null
|
||||||
@@ -173,9 +181,13 @@ export type Database = {
|
|||||||
patient_id: string
|
patient_id: string
|
||||||
period_end?: string | null
|
period_end?: string | null
|
||||||
period_start?: string | null
|
period_start?: string | null
|
||||||
|
published_at?: string | null
|
||||||
|
sessie_planning?: Json | null
|
||||||
status?: Database["public"]["Enums"]["careplan_status"]
|
status?: Database["public"]["Enums"]["careplan_status"]
|
||||||
title: string
|
title: string
|
||||||
updated_at?: string | null
|
updated_at?: string | null
|
||||||
|
veiligheidsplan?: Json | null
|
||||||
|
version?: number | null
|
||||||
}
|
}
|
||||||
Update: {
|
Update: {
|
||||||
activities?: Json | null
|
activities?: Json | null
|
||||||
@@ -185,6 +197,7 @@ export type Database = {
|
|||||||
based_on_examinations?: string[] | null
|
based_on_examinations?: string[] | null
|
||||||
based_on_intake_id?: string | null
|
based_on_intake_id?: string | null
|
||||||
based_on_risk_assessments?: string[] | null
|
based_on_risk_assessments?: string[] | null
|
||||||
|
behandelstructuur?: Json | null
|
||||||
care_team_ids?: string[] | null
|
care_team_ids?: string[] | null
|
||||||
category_code?: string | null
|
category_code?: string | null
|
||||||
category_display?: string | null
|
category_display?: string | null
|
||||||
@@ -193,6 +206,7 @@ export type Database = {
|
|||||||
created_date?: string | null
|
created_date?: string | null
|
||||||
description?: string | null
|
description?: string | null
|
||||||
encounter_id?: string | null
|
encounter_id?: string | null
|
||||||
|
evaluatiemomenten?: Json | null
|
||||||
goals?: Json | null
|
goals?: Json | null
|
||||||
id?: string
|
id?: string
|
||||||
identifier?: string | null
|
identifier?: string | null
|
||||||
@@ -201,9 +215,13 @@ export type Database = {
|
|||||||
patient_id?: string
|
patient_id?: string
|
||||||
period_end?: string | null
|
period_end?: string | null
|
||||||
period_start?: string | null
|
period_start?: string | null
|
||||||
|
published_at?: string | null
|
||||||
|
sessie_planning?: Json | null
|
||||||
status?: Database["public"]["Enums"]["careplan_status"]
|
status?: Database["public"]["Enums"]["careplan_status"]
|
||||||
title?: string
|
title?: string
|
||||||
updated_at?: string | null
|
updated_at?: string | null
|
||||||
|
veiligheidsplan?: Json | null
|
||||||
|
version?: number | null
|
||||||
}
|
}
|
||||||
Relationships: [
|
Relationships: [
|
||||||
{
|
{
|
||||||
@@ -765,6 +783,53 @@ export type Database = {
|
|||||||
}
|
}
|
||||||
Relationships: []
|
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: {
|
observations: {
|
||||||
Row: {
|
Row: {
|
||||||
body_site: string | null
|
body_site: string | null
|
||||||
|
|||||||
114
lib/types/nursing-log.ts
Normal file
114
lib/types/nursing-log.ts
Normal file
@@ -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<typeof CreateNursingLogSchema>;
|
||||||
|
|
||||||
|
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<typeof UpdateNursingLogSchema>;
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user