Files
triqura-ecd/docs/architecture/overzicht-po-pm.html
2026-07-09 23:16:28 +02:00

288 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mini-EPD — Architectuur & Datacomponenten (PO/PM)</title>
<style>
:root{
--bg:#0f172a; --panel:#ffffff; --ink:#1e293b; --muted:#64748b;
--line:#e2e8f0; --brand:#4f46e5; --brand-soft:#eef2ff;
--teal:#0d9488; --teal-soft:#ecfdf5; --amber:#d97706; --amber-soft:#fffbeb;
--rose:#e11d48; --rose-soft:#fff1f2; --sky:#0284c7; --sky-soft:#f0f9ff;
}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
background:linear-gradient(160deg,#0f172a 0%,#1e1b4b 100%);color:var(--ink);line-height:1.5;padding:32px 16px;}
.wrap{max-width:1080px;margin:0 auto;}
header{text-align:center;color:#fff;margin-bottom:32px;}
header h1{font-size:30px;margin:0 0 8px;letter-spacing:-.5px;}
header p{color:#c7d2fe;margin:0;font-size:15px;}
.badge{display:inline-block;background:rgba(255,255,255,.12);color:#e0e7ff;
padding:4px 12px;border-radius:999px;font-size:12px;margin-top:12px;}
.card{background:var(--panel);border-radius:18px;padding:26px 28px;margin-bottom:22px;
box-shadow:0 10px 40px rgba(0,0,0,.18);}
.card h2{font-size:13px;text-transform:uppercase;letter-spacing:1.5px;color:var(--brand);
margin:0 0 4px;font-weight:700;}
.card h2 .num{background:var(--brand);color:#fff;border-radius:8px;padding:2px 9px;margin-right:8px;font-size:13px;}
.card .lead{color:var(--muted);font-size:14px;margin:0 0 20px;}
/* layers stack */
.stack{display:flex;flex-direction:column;gap:10px;}
.layer{border-radius:14px;padding:16px 20px;border:1px solid var(--line);}
.layer .tag{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;opacity:.7;}
.layer .title{font-size:16px;font-weight:700;margin:2px 0 6px;}
.layer .desc{font-size:13px;color:var(--muted);}
.l1{background:var(--sky-soft);border-color:#bae6fd;}
.l2{background:var(--brand-soft);border-color:#c7d2fe;}
.l3{background:var(--teal-soft);border-color:#a7f3d0;}
.chev{text-align:center;color:#94a3b8;font-size:18px;line-height:1;}
/* modules grid */
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;}
.mod{border:1px solid var(--line);border-radius:14px;padding:16px;}
.mod .ic{font-size:22px;}
.mod .nm{font-weight:700;margin:8px 0 4px;font-size:15px;}
.mod .tx{font-size:13px;color:var(--muted);}
/* cortex flow */
.flow{display:flex;flex-direction:column;gap:8px;}
.step{display:flex;align-items:center;gap:14px;border:1px solid var(--line);border-radius:12px;padding:12px 16px;}
.step .badgeN{flex:0 0 34px;height:34px;border-radius:50%;display:flex;align-items:center;justify-content:center;
font-weight:800;color:#fff;font-size:14px;}
.step .body{flex:1;}
.step .t{font-weight:700;font-size:14px;}
.step .d{font-size:12.5px;color:var(--muted);}
.n1{background:var(--sky);} .n2{background:var(--brand);} .n3{background:var(--teal);} .nA{background:#475569;}
.intents{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px;}
.intent{background:var(--brand-soft);color:var(--brand);border-radius:999px;padding:5px 13px;font-size:12.5px;font-weight:600;}
/* services */
.svc{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px;}
.svcbox{border:1px solid var(--line);border-radius:14px;padding:16px;}
.svcbox .h{font-weight:700;font-size:15px;display:flex;align-items:center;gap:8px;}
.svcbox .role{font-size:12px;color:var(--muted);margin:6px 0;}
.svcbox .why{font-size:12.5px;background:#f8fafc;border-radius:8px;padding:8px 10px;color:var(--ink);}
/* data model diagram */
.dm{display:flex;flex-direction:column;align-items:center;gap:0;}
.entity{border-radius:12px;padding:12px 18px;text-align:center;border:2px solid;min-width:150px;}
.entity .e-nm{font-weight:800;font-size:14px;}
.entity .e-tx{font-size:11.5px;color:var(--muted);}
.epatient{background:var(--brand-soft);border-color:var(--brand);}
.erow{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:12px;}
.echild{background:#f8fafc;border-color:var(--line);}
.echild .e-nm{font-size:13px;}
.pipe{width:2px;height:18px;background:#cbd5e1;}
/* journey */
.journey{display:flex;flex-wrap:wrap;align-items:stretch;gap:0;}
.jstep{flex:1;min-width:120px;background:#f8fafc;border:1px solid var(--line);padding:12px;text-align:center;position:relative;}
.jstep:first-child{border-radius:12px 0 0 12px;}
.jstep:last-child{border-radius:0 12px 12px 0;}
.jstep .jt{font-weight:700;font-size:13px;}
.jstep .jd{font-size:11px;color:var(--muted);margin-top:3px;}
.jstep .arrow{position:absolute;right:-9px;top:50%;transform:translateY(-50%);color:#cbd5e1;font-size:18px;z-index:2;}
/* tables */
table{width:100%;border-collapse:collapse;font-size:13px;}
th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line);vertical-align:top;}
th{font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);}
td:first-child{font-weight:600;white-space:nowrap;}
/* rules / warnings */
.rules{display:grid;gap:10px;}
.rule{display:flex;gap:12px;border:1px solid var(--line);border-radius:12px;padding:12px 14px;}
.rule .ic{font-size:18px;}
.rule .rt{font-weight:700;font-size:13.5px;}
.rule .rd{font-size:12.5px;color:var(--muted);}
.warns{display:grid;gap:10px;}
.warn{display:flex;gap:12px;background:var(--amber-soft);border:1px solid #fde68a;border-radius:12px;padding:12px 14px;}
.warn .rt{font-weight:700;font-size:13.5px;color:#92400e;}
.warn .rd{font-size:12.5px;color:#78350f;}
footer{text-align:center;color:#94a3b8;font-size:12px;margin-top:8px;}
.tags{margin-top:14px;}
.pill{display:inline-block;font-size:11px;padding:3px 9px;border-radius:6px;margin:2px;font-weight:600;}
.pill-type{background:var(--sky-soft);color:var(--sky);}
.pill-cat{background:var(--teal-soft);color:var(--teal);}
</style>
</head>
<body>
<div class="wrap">
<header>
<h1>Mini-EPD — Architectuur &amp; Datacomponenten</h1>
<p>Eén begrijpelijk overzicht voor Product Owners &amp; Project Managers</p>
<span class="badge">GGZ Elektronisch Patiëntdossier · Prototype</span>
</header>
<!-- IN ÉÉN ALINEA -->
<div class="card">
<h2><span class="num">1</span>In het kort</h2>
<p style="font-size:15px;margin:0;">Het Mini-EPD is een <strong>elektronisch patiëntdossier voor de GGZ</strong>. Een zorgverlener meldt patiënten aan, doet intake, maakt behandelplannen, schrijft dagrapportages en draagt diensten over. Het hart is de <strong>Cortex AI Command Center</strong>: een natuurlijke-taal-interface (typen of inspreken) waarmee je acties uitvoert in gewone zinnen — <em>"maak een notitie voor Jan: medicatie ingenomen"</em> of <em>"plan een afspraak morgen om 14:00"</em>.</p>
</div>
<!-- DRIE LAGEN -->
<div class="card">
<h2><span class="num">2</span>De drie lagen van het systeem</h2>
<p class="lead">Van wat de gebruiker ziet tot waar de gegevens staan.</p>
<div class="stack">
<div class="layer l1">
<div class="tag">Laag 1 · Schermen</div>
<div class="title">Wat de gebruiker ziet</div>
<div class="desc">Dashboard · Patiëntdossier · Intake · Behandelplan · Verpleegrapportage · Agenda · Cortex AI Command Center</div>
</div>
<div class="chev"></div>
<div class="layer l2">
<div class="tag">Laag 2 · Logica</div>
<div class="title">De verwerking (API's)</div>
<div class="desc">Rapportage · Overdracht · Behandelplan · Intake · Cortex (intentherkenning + chat) · Spraak-naar-tekst</div>
</div>
<div class="chev"></div>
<div class="layer l3">
<div class="tag">Laag 3 · Gegevens</div>
<div class="title">Database &amp; externe diensten</div>
<div class="desc">Supabase database · Claude AI (Anthropic) · Deepgram (spraak)</div>
</div>
</div>
</div>
<!-- MODULES -->
<div class="card">
<h2><span class="num">3</span>De modules (wat de gebruiker ziet)</h2>
<p class="lead">De schermen waarmee de zorgverlener werkt.</p>
<div class="grid">
<div class="mod"><div class="ic">🏠</div><div class="nm">Dashboard</div><div class="tx">Startscherm met het Cortex AI Command Center. Hier begint elke actie.</div></div>
<div class="mod"><div class="ic">📋</div><div class="nm">Patiëntdossier</div><div class="tx">Basisgegevens (incl. BSN), diagnoses, observaties en intakes per patiënt.</div></div>
<div class="mod"><div class="ic">📝</div><div class="nm">Intake</div><div class="tx">Stapsgewijs: contactpersonen, risicotaxatie, anamnese, onderzoek, diagnose, advies.</div></div>
<div class="mod"><div class="ic">🎯</div><div class="nm">Behandelplan</div><div class="tx">Doelen en interventies per leefgebied vastleggen en beheren.</div></div>
<div class="mod"><div class="ic">🔄</div><div class="nm">Verpleegrapportage</div><div class="tx">Dagnotities in tijdlijn + overdracht met AI-samenvatting voor de volgende dienst.</div></div>
<div class="mod"><div class="ic">📅</div><div class="nm">Agenda</div><div class="tx">Kalender met afspraken; inplannen, verzetten of annuleren.</div></div>
</div>
</div>
<!-- CORTEX -->
<div class="card">
<h2><span class="num">4</span>Het hart: Cortex AI Command Center</h2>
<p class="lead">De gebruiker typt of spreekt een opdracht. Het systeem begrijpt de bedoeling en handelt — in drie slimme lagen die snelheid én begrip combineren.</p>
<div class="flow">
<div class="step"><div class="badgeN nA">🗣</div><div class="body"><div class="t">Gebruiker zegt of typt iets</div><div class="d">"Maak een notitie voor Jan: medicatie ingenomen"</div></div></div>
<div class="chev"></div>
<div class="step"><div class="badgeN n1">1</div><div class="body"><div class="t">Reflex (lokaal) — snel &amp; gratis</div><div class="d">Herkent veelvoorkomende opdrachten direct via patronen (&lt;20ms). Twijfel of complex? → door naar laag 2.</div></div></div>
<div class="chev"></div>
<div class="step"><div class="badgeN n2">2</div><div class="body"><div class="t">Orchestrator (AI) — voor lastige gevallen</div><div class="d">Claude lost op: meerdere opdrachten tegelijk, "hem/haar", "morgen", onduidelijke zinnen.</div></div></div>
<div class="chev"></div>
<div class="step"><div class="badgeN nA"></div><div class="body"><div class="t">Actie uitgevoerd</div><div class="d">Notitie opgeslagen · afspraak gemaakt · patiënt gevonden …</div></div></div>
<div class="chev"></div>
<div class="step"><div class="badgeN n3">3</div><div class="body"><div class="t">Nudge — proactieve suggestie achteraf</div><div class="d">"Wil je hier ook een observatie bij maken?" — verhoogt de zorgkwaliteit op het juiste moment.</div></div></div>
</div>
<p class="lead" style="margin:18px 0 6px;"><strong>Waarom drie lagen?</strong> De goedkope lokale laag vangt het gros af zonder AI-kosten of vertraging. Alleen echt lastige zinnen gaan naar de betaalde AI.</p>
<p class="lead" style="margin:14px 0 4px;"><strong>Wat Cortex begrijpt (de "intents"):</strong></p>
<div class="intents">
<span class="intent">Dagnotitie maken</span>
<span class="intent">Patiënt zoeken</span>
<span class="intent">Overdracht bekijken</span>
<span class="intent">Agenda opvragen</span>
<span class="intent">Afspraak inplannen</span>
<span class="intent">Afspraak annuleren</span>
<span class="intent">Afspraak verzetten</span>
</div>
</div>
<!-- EXTERNE DIENSTEN -->
<div class="card">
<h2><span class="num">5</span>De externe diensten (afhankelijkheden)</h2>
<p class="lead">Waar het product op leunt buiten de eigen code.</p>
<div class="svc">
<div class="svcbox">
<div class="h">🗄️ Supabase</div>
<div class="role">Database + inloggen (authenticatie)</div>
<div class="why">De plek waar alle patiëntgegevens veilig staan.</div>
</div>
<div class="svcbox">
<div class="h">🤖 Claude (Anthropic)</div>
<div class="role">Intentherkenning · chat · overdrachtssamenvatting · behandelplan</div>
<div class="why">De "intelligentie" achter Cortex en de samenvattingen. <strong>Betaalde API → gebruik = kosten.</strong></div>
</div>
<div class="svcbox">
<div class="h">🎙️ Deepgram</div>
<div class="role">Spraak omzetten naar tekst</div>
<div class="why">Maakt het inspreken van notities mogelijk. <strong>Betaalde API → gebruik = kosten.</strong></div>
</div>
</div>
</div>
<!-- DATAMODEL -->
<div class="card">
<h2><span class="num">6</span>De gegevens (datacomponenten)</h2>
<p class="lead">Het datamodel is <strong>FHIR-geïnspireerd</strong> — de internationale standaard voor zorggegevens, pragmatisch vereenvoudigd. Alles draait om de patiënt.</p>
<div class="dm">
<div class="entity epatient">
<div class="e-nm">PATIËNT</div>
<div class="e-tx">naam · BSN · geboortedatum · status</div>
</div>
<div class="pipe"></div>
<div class="erow">
<div class="entity echild"><div class="e-nm">Screening</div><div class="e-tx">aanmelding &amp; besluit</div></div>
<div class="entity echild"><div class="e-nm">Intake</div><div class="e-tx">+ risico, anamnese, onderzoek</div></div>
<div class="entity echild"><div class="e-nm">Diagnose</div><div class="e-tx">DSM-5 / ICD-10</div></div>
<div class="entity echild"><div class="e-nm">Observatie</div><div class="e-tx">metingen &amp; scores</div></div>
<div class="entity echild"><div class="e-nm">Behandelplan</div><div class="e-tx">doelen &amp; interventies</div></div>
</div>
<div class="pipe"></div>
<div class="entity echild" style="min-width:200px;"><div class="e-nm">Contactmoment → Rapportage</div><div class="e-tx">sessies &amp; dagnotities</div></div>
</div>
<div class="tags">
<p class="lead" style="margin:18px 0 6px;"><strong>Soorten rapportage (type):</strong></p>
<span class="pill pill-type">voortgang</span><span class="pill pill-type">observatie</span><span class="pill pill-type">incident</span><span class="pill pill-type">medicatie</span><span class="pill pill-type">contact</span><span class="pill pill-type">crisis</span><span class="pill pill-type">verpleegkundig</span><span class="pill pill-type">behandeladvies</span>
<p class="lead" style="margin:14px 0 6px;"><strong>Categorieën (verpleegkundig):</strong></p>
<span class="pill pill-cat">medicatie</span><span class="pill pill-cat">ADL</span><span class="pill pill-cat">gedrag</span><span class="pill pill-cat">incident</span><span class="pill pill-cat">observatie</span>
</div>
</div>
<!-- PATIENTREIS -->
<div class="card">
<h2><span class="num">7</span>De patiëntreis door de data</h2>
<p class="lead">Hoe een patiënt door het systeem stroomt.</p>
<div class="journey">
<div class="jstep"><div class="jt">Aangemeld</div><div class="jd">status: planned</div><span class="arrow"></span></div>
<div class="jstep"><div class="jt">Screening</div><div class="jd">geschikt?</div><span class="arrow"></span></div>
<div class="jstep"><div class="jt">Intake</div><div class="jd">anamnese, risico</div><span class="arrow"></span></div>
<div class="jstep"><div class="jt">In zorg</div><div class="jd">plan + rapportages</div><span class="arrow"></span></div>
<div class="jstep"><div class="jt">Overdracht</div><div class="jd">samenvatting collega</div></div>
</div>
</div>
<!-- REGELS -->
<div class="card">
<h2><span class="num">8</span>Belangrijke regels in de data</h2>
<p class="lead">Goed om te weten als PO/PM.</p>
<div class="rules">
<div class="rule"><div class="ic">♻️</div><div><div class="rt">Soft delete</div><div class="rd">Verwijderen markeert alleen; data blijft staan. Niets gaat echt verloren — belangrijk voor zorgdossiers en herstel.</div></div></div>
<div class="rule"><div class="ic">🌙</div><div><div class="rt">Shift-logica</div><div class="rd">Notities vóór 07:00 horen bij de dienst van de vórige dag. Zorgt dat nachtdiensten correct in de overdracht vallen.</div></div></div>
<div class="rule"><div class="ic"></div><div><div class="rt">Overdrachtsfilter</div><div class="rd">Alleen notities met "meenemen in overdracht" verschijnen in de samenvatting. De zorgverlener bepaalt wat relevant is.</div></div></div>
<div class="rule"><div class="ic">🔒</div><div><div class="rt">Row Level Security (RLS)</div><div class="rd">De database dwingt af wie welke gegevens mag zien — fundament voor privacy/AVG. In deze fase nog ruim ingesteld.</div></div></div>
<div class="rule"><div class="ic">🧠</div><div><div class="rt">AI-herkomst</div><div class="rd">Bij AI-gegenereerde inhoud worden zekerheid en redenering bewaard — transparant en controleerbaar.</div></div></div>
</div>
</div>
<!-- AANDACHTSPUNTEN -->
<div class="card">
<h2><span class="num">9</span>Aandachtspunten &amp; status (eerlijk beeld)</h2>
<div class="warns">
<div class="warn"><div class="ic">⚠️</div><div><div class="rt">Prototype-status</div><div class="rd">Geen productiesysteem. Beveiligingsregels (RLS) zijn nog ruim; verfijning per team/organisatie is nog niet gebouwd.</div></div></div>
<div class="warn"><div class="ic">⚠️</div><div><div class="rt">Twee-sporen datamodel</div><div class="rd">Naast het nieuwe FHIR-model bestaat nog een ouder model. Het nieuwe is leidend; het oude is nog niet opgeruimd — technische schuld om te bewaken.</div></div></div>
<div class="warn"><div class="ic">⚠️</div><div><div class="rt">Externe AI-kosten</div><div class="rd">Schaalbaarheid van Cortex hangt samen met de gebruikskosten van Claude en Deepgram.</div></div></div>
<div class="warn"><div class="ic">⚠️</div><div><div class="rt">Nog geen echte interoperabiliteit</div><div class="rd">Het model is FHIR-geïnspireerd; volledige FHIR-uitwisseling met externe systemen is nog niet geïmplementeerd.</div></div></div>
</div>
</div>
<footer>Bron: codebase mini-epd-prototype · modules in <code>app/</code> · logica in <code>app/api/</code> · Cortex in <code>lib/cortex/</code> · datamodel in <code>supabase/migrations/</code></footer>
</div>
</body>
</html>