feat(cortex): no-show casus — intent flow, brief-rescript en document artifact

Cortex handelt een no-show af vanuit één chatcommando: afspraak
annuleren (declarabiliteits-nudge), en de openstaande concept-
huisartsbrief wordt via LLM herschreven en ter review aangeboden
in een document artifact (human-in-the-loop, PATCH dispatch zet
status op verzendklaar).

- API-routes: context, rescript, cancel, dispatch
- NoShowDocumentBlock: review/edit UI met origineel-vergelijk
- Mock-data voor concept huisartsbrief
- PRD, FO, bouwplan en epics in docs/intent/noshow-case/

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
colinislit
2026-07-09 23:14:55 +02:00
parent d08db12765
commit 924988dd15
14 changed files with 3022 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { createClient } from '@/lib/auth/server';
import { isMockAppointment } from '@/lib/cortex/mock-data/noshow';
const CancelNoShowSchema = z.object({
appointmentId: z.string().min(1, 'appointmentId is verplicht'),
patientId: z.string().min(1, 'patientId is verplicht'),
});
export async function POST(request: NextRequest) {
const supabase = await createClient();
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) {
return NextResponse.json({ error: 'Niet geautoriseerd' }, { status: 401 });
}
let body: z.infer<typeof CancelNoShowSchema>;
try {
body = CancelNoShowSchema.parse(await request.json());
} catch {
return NextResponse.json({ error: 'Ongeldige invoer' }, { status: 400 });
}
const { appointmentId, patientId } = body;
// Mock path — demo flow
if (isMockAppointment(appointmentId)) {
return NextResponse.json({
success: true,
appointmentId,
newStatus: 'cancelled_no_show',
message: 'Afspraak geregistreerd als no show (demo)',
});
}
// Productie path — 'cancelled' is de dichtstbijzijnde geldige DB status
// In productie zou je een aparte no_show kolom of notitieveld gebruiken
const { error } = await supabase
.from('encounters')
.update({ status: 'cancelled' as 'cancelled' })
.eq('id', appointmentId)
.eq('patient_id', patientId);
if (error) {
console.error('[noshow/cancel] DB error:', error);
return NextResponse.json({ error: 'Annuleren mislukt' }, { status: 500 });
}
return NextResponse.json({ success: true, appointmentId, newStatus: 'cancelled_no_show' });
}