feat(swift): implementeer linked evidence UI voor overdracht (E5.S2)
E5.S2 compleet: Bronnotitie links met hover preview in OverdrachtBlock. Voortgang: 66 SP / 82 SP (80%) - Epic 5 40% compleet! Implementatie: - Bronverwijzingen nu klikbaar met hover popover voor volledige content - Source data enrichment in API response - Type-specific content display (observaties, rapportages, risico's) - Visual feedback met icons en kleuren per bron type Components & Types: - LinkedEvidence component (160 regels) - Reusable popover voor alle bron types - Aandachtspunt type extended met optionele sourceData field - enrichWithSourceData() functie in API route (48 regels) LinkedEvidence Features: - Hover popover met volledige bron content - Type-specific icons: Activity (observatie), FileText (rapportage), AlertTriangle (risico) - Click/hover trigger met underline-dotted styling - ExternalLink icon voor visual affordance - Popover positioning: top-start voor beste UX Source Data per Type: - Observaties: value + unit + interpretation (HH/H/N/L/LL) met kleuren - Rapportages/Verpleegkundig: content + createdBy - Risico's: riskLevel + rationale met severity kleuren API Enrichment Logic: - enrichWithSourceData() lookup in context (vitals, reports, risks) - Source data toegevoegd aan aandachtspunten vóór response - Type-safe matching op bron.id en bron.type - Backwards compatible: werkt met/zonder sourceData UI/UX Improvements: - Color-coded interpretations: Red (HH/LL kritiek), Amber (H/L), Teal (N) - Color-coded risk levels: Red (zeer hoog/hoog), Amber (gemiddeld), Teal (laag) - Formatted text display met bg-slate-50 border - Responsive popover met max-width 96 (384px) - Fallback: geen sourceData = plain text zonder popover Integration in OverdrachtBlock: - AandachtspuntItem gebruikt LinkedEvidence component - Replaced: "bron.label • bron.datum" plain text - Now: LinkedEvidence met hover preview Files Changed: - lib/types/overdracht.ts: +12 lines (sourceData interface) - app/api/overdracht/generate/route.ts: +58 lines (enrichWithSourceData) - components/swift/blocks/overdracht-block.tsx: +1/-3 lines (LinkedEvidence import/usage) - components/swift/shared/linked-evidence.tsx: +160 lines (NEW) Technical Details: - Popover from shadcn/ui (@/components/ui/popover) - Icons from lucide-react (Activity, FileText, AlertTriangle, ExternalLink) - Type guards voor safe property access (sourceData?.value) - Underline decoration-dotted cursor-help voor accessibility Testing: - Build succesvol (pnpm build) - /epd/swift: 143 kB (+24 kB door LinkedEvidence component) - Geen nieuwe type errors - Popover trigger werkt met keyboard (accessible) Voortgang: 66 SP / 82 SP (80%) - E5.S2 compleet (3 SP) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -219,6 +219,59 @@ async function callClaudeAPI(
|
||||
return validated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enrich aandachtspunten with full source data
|
||||
*/
|
||||
function enrichWithSourceData(
|
||||
aandachtspunten: Aandachtspunt[],
|
||||
context: OverdrachtContext
|
||||
): Aandachtspunt[] {
|
||||
return aandachtspunten.map((punt) => {
|
||||
const { bron } = punt;
|
||||
let sourceData: Aandachtspunt['sourceData'];
|
||||
|
||||
switch (bron.type) {
|
||||
case 'observatie': {
|
||||
const vital = context.vitals.find((v) => v.id === bron.id);
|
||||
if (vital) {
|
||||
sourceData = {
|
||||
value: vital.value_quantity_value?.toString() || undefined,
|
||||
unit: vital.value_quantity_unit || undefined,
|
||||
interpretation: vital.interpretation_code || undefined,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'rapportage':
|
||||
case 'verpleegkundig': {
|
||||
const report = context.reports.find((r) => r.id === bron.id);
|
||||
if (report) {
|
||||
sourceData = {
|
||||
content: report.content,
|
||||
createdBy: report.created_by || undefined,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'risico': {
|
||||
const risk = context.risks.find((r) => r.id === bron.id);
|
||||
if (risk) {
|
||||
sourceData = {
|
||||
riskLevel: risk.risk_level,
|
||||
rationale: risk.rationale || undefined,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...punt,
|
||||
sourceData,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log AI event to database
|
||||
*/
|
||||
@@ -298,13 +351,16 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const durationMs = Date.now() - startTime;
|
||||
|
||||
// Enrich aandachtspunten with full source data for linked evidence
|
||||
const enrichedAandachtspunten = enrichWithSourceData(aiResult.aandachtspunten, context);
|
||||
|
||||
// Log AI event
|
||||
await logAIEvent(supabase, patientId, context, aiResult, durationMs);
|
||||
|
||||
// Build response
|
||||
const response: AISamenvatting = {
|
||||
samenvatting: aiResult.samenvatting,
|
||||
aandachtspunten: aiResult.aandachtspunten,
|
||||
aandachtspunten: enrichedAandachtspunten,
|
||||
actiepunten: aiResult.actiepunten,
|
||||
generatedAt: new Date().toISOString(),
|
||||
durationMs,
|
||||
|
||||
Reference in New Issue
Block a user