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_intake_id: string | null
|
||||
based_on_risk_assessments: string[] | null
|
||||
behandelstructuur: Json | null
|
||||
care_team_ids: string[] | null
|
||||
category_code: string | null
|
||||
category_display: string | null
|
||||
@@ -137,6 +138,7 @@ export type Database = {
|
||||
created_date: string | null
|
||||
description: string | null
|
||||
encounter_id: string | null
|
||||
evaluatiemomenten: Json | null
|
||||
goals: Json | null
|
||||
id: string
|
||||
identifier: string | null
|
||||
@@ -145,9 +147,13 @@ export type Database = {
|
||||
patient_id: string
|
||||
period_end: string | null
|
||||
period_start: string | null
|
||||
published_at: string | null
|
||||
sessie_planning: Json | null
|
||||
status: Database["public"]["Enums"]["careplan_status"]
|
||||
title: string
|
||||
updated_at: string | null
|
||||
veiligheidsplan: Json | null
|
||||
version: number | null
|
||||
}
|
||||
Insert: {
|
||||
activities?: Json | null
|
||||
@@ -157,6 +163,7 @@ export type Database = {
|
||||
based_on_examinations?: string[] | null
|
||||
based_on_intake_id?: string | null
|
||||
based_on_risk_assessments?: string[] | null
|
||||
behandelstructuur?: Json | null
|
||||
care_team_ids?: string[] | null
|
||||
category_code?: string | null
|
||||
category_display?: string | null
|
||||
@@ -165,6 +172,7 @@ export type Database = {
|
||||
created_date?: string | null
|
||||
description?: string | null
|
||||
encounter_id?: string | null
|
||||
evaluatiemomenten?: Json | null
|
||||
goals?: Json | null
|
||||
id?: string
|
||||
identifier?: string | null
|
||||
@@ -173,9 +181,13 @@ export type Database = {
|
||||
patient_id: string
|
||||
period_end?: string | null
|
||||
period_start?: string | null
|
||||
published_at?: string | null
|
||||
sessie_planning?: Json | null
|
||||
status?: Database["public"]["Enums"]["careplan_status"]
|
||||
title: string
|
||||
updated_at?: string | null
|
||||
veiligheidsplan?: Json | null
|
||||
version?: number | null
|
||||
}
|
||||
Update: {
|
||||
activities?: Json | null
|
||||
@@ -185,6 +197,7 @@ export type Database = {
|
||||
based_on_examinations?: string[] | null
|
||||
based_on_intake_id?: string | null
|
||||
based_on_risk_assessments?: string[] | null
|
||||
behandelstructuur?: Json | null
|
||||
care_team_ids?: string[] | null
|
||||
category_code?: string | null
|
||||
category_display?: string | null
|
||||
@@ -193,6 +206,7 @@ export type Database = {
|
||||
created_date?: string | null
|
||||
description?: string | null
|
||||
encounter_id?: string | null
|
||||
evaluatiemomenten?: Json | null
|
||||
goals?: Json | null
|
||||
id?: string
|
||||
identifier?: string | null
|
||||
@@ -201,9 +215,13 @@ export type Database = {
|
||||
patient_id?: string
|
||||
period_end?: string | null
|
||||
period_start?: string | null
|
||||
published_at?: string | null
|
||||
sessie_planning?: Json | null
|
||||
status?: Database["public"]["Enums"]["careplan_status"]
|
||||
title?: string
|
||||
updated_at?: string | null
|
||||
veiligheidsplan?: Json | null
|
||||
version?: number | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
@@ -765,6 +783,53 @@ export type Database = {
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
nursing_logs: {
|
||||
Row: {
|
||||
category: string
|
||||
content: string
|
||||
created_at: string
|
||||
created_by: string
|
||||
id: string
|
||||
include_in_handover: boolean
|
||||
patient_id: string
|
||||
shift_date: string
|
||||
timestamp: string
|
||||
updated_at: string
|
||||
}
|
||||
Insert: {
|
||||
category: string
|
||||
content: string
|
||||
created_at?: string
|
||||
created_by: string
|
||||
id?: string
|
||||
include_in_handover?: boolean
|
||||
patient_id: string
|
||||
shift_date: string
|
||||
timestamp?: string
|
||||
updated_at?: string
|
||||
}
|
||||
Update: {
|
||||
category?: string
|
||||
content?: string
|
||||
created_at?: string
|
||||
created_by?: string
|
||||
id?: string
|
||||
include_in_handover?: boolean
|
||||
patient_id?: string
|
||||
shift_date?: string
|
||||
timestamp?: string
|
||||
updated_at?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "nursing_logs_patient_id_fkey"
|
||||
columns: ["patient_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "patients"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
observations: {
|
||||
Row: {
|
||||
body_site: string | null
|
||||
|
||||
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