/** * Intake Status API * * GET /api/cortex/intake/status?patientId=xxx&intakeId=xxx * * Returns intake completion status: percentage, completed/missing sections. * Used by IntakeStatusBlock in Cortex Command Center. */ import { NextRequest, NextResponse } from 'next/server'; import { createClient } from '@/lib/auth/server'; import { z } from 'zod'; // Query parameter schema const QuerySchema = z.object({ patientId: z.string().uuid({ message: 'patientId moet een geldige UUID zijn' }), intakeId: z.string().uuid({ message: 'intakeId moet een geldige UUID zijn' }).optional(), }); // Section definition interface IntakeSection { id: string; label: string; required: boolean; } const INTAKE_SECTIONS: IntakeSection[] = [ { id: 'contacts', label: 'Contactmomenten', required: false }, { id: 'anamnese', label: 'Anamnese', required: true }, { id: 'risk', label: 'Risicotaxatie', required: true }, { id: 'kindcheck', label: 'Kindcheck', required: true }, { id: 'examination', label: 'Onderzoek', required: false }, { id: 'rom', label: 'Meetinstrumenten (ROM)', required: false }, { id: 'diagnosis', label: 'Diagnose', required: true }, { id: 'behandeladvies', label: 'Behandeladvies', required: true }, ]; export interface IntakeStatusResponse { intakeId: string; patientId: string; completionPercentage: number; status: 'bezig' | 'afgerond'; sections: { id: string; label: string; required: boolean; completed: boolean; count: number; }[]; completedCount: number; totalRequired: number; lastUpdated: string | null; } export async function GET(request: NextRequest) { try { // Auth check const supabase = await createClient(); const { data: { user }, error: authError, } = await supabase.auth.getUser(); if (authError || !user) { return NextResponse.json( { error: 'Niet geautoriseerd. Log opnieuw in.' }, { status: 401 } ); } // Parse and validate query parameters const searchParams = request.nextUrl.searchParams; const patientId = searchParams.get('patientId'); const intakeId = searchParams.get('intakeId'); if (!patientId) { return NextResponse.json( { error: 'Query parameter "patientId" is verplicht' }, { status: 400 } ); } const validation = QuerySchema.safeParse({ patientId, intakeId }); if (!validation.success) { const errorMessage = validation.error.issues .map((e) => e.message) .join(', '); return NextResponse.json({ error: errorMessage }, { status: 400 }); } // If no intakeId provided, get the most recent active intake for this patient let targetIntakeId = validation.data.intakeId; if (!targetIntakeId) { const { data: latestIntake, error: intakeError } = await supabase .from('intakes') .select('id') .eq('patient_id', patientId) .eq('status', 'bezig') .order('start_date', { ascending: false }) .limit(1) .maybeSingle(); if (intakeError) { console.error('Error fetching latest intake:', intakeError); return NextResponse.json( { error: 'Fout bij ophalen intake' }, { status: 500 } ); } if (!latestIntake) { return NextResponse.json( { error: 'Geen actieve intake gevonden voor deze patiƫnt' }, { status: 404 } ); } targetIntakeId = latestIntake.id; } // Fetch intake details const { data: intake, error: intakeDetailError } = await supabase .from('intakes') .select('id, patient_id, status, kindcheck_data, treatment_advice, updated_at') .eq('id', targetIntakeId) .single(); if (intakeDetailError || !intake) { return NextResponse.json( { error: 'Intake niet gevonden' }, { status: 404 } ); } // Fetch counts for each section in parallel const [ contactsResult, anamneseResult, riskResult, examinationResult, romResult, diagnosisResult, ] = await Promise.all([ // Contacts (encounters) supabase .from('encounters') .select('id', { count: 'exact', head: true }) .eq('intake_id', targetIntakeId), // Anamnese supabase .from('anamneses') .select('id', { count: 'exact', head: true }) .eq('intake_id', targetIntakeId), // Risk assessments supabase .from('risk_assessments') .select('id', { count: 'exact', head: true }) .eq('intake_id', targetIntakeId), // Examinations (non-ROM) supabase .from('examinations') .select('id', { count: 'exact', head: true }) .eq('intake_id', targetIntakeId) .neq('examination_type', 'ROM'), // ROM examinations supabase .from('examinations') .select('id', { count: 'exact', head: true }) .eq('intake_id', targetIntakeId) .eq('examination_type', 'ROM'), // Diagnoses (conditions linked to intake via encounter_id) supabase .from('conditions') .select('id', { count: 'exact', head: true }) .eq('encounter_id', targetIntakeId), ]); // Build section status const sectionCounts: Record = { contacts: contactsResult.count || 0, anamnese: anamneseResult.count || 0, risk: riskResult.count || 0, kindcheck: intake.kindcheck_data && Object.keys(intake.kindcheck_data as object).length > 0 ? 1 : 0, examination: examinationResult.count || 0, rom: romResult.count || 0, diagnosis: diagnosisResult.count || 0, behandeladvies: intake.treatment_advice && Object.keys(intake.treatment_advice as object).length > 0 ? 1 : 0, }; const sections = INTAKE_SECTIONS.map((section) => ({ id: section.id, label: section.label, required: section.required, completed: sectionCounts[section.id] > 0, count: sectionCounts[section.id], })); // Calculate completion percentage (based on required sections) const requiredSections = sections.filter((s) => s.required); const completedRequired = requiredSections.filter((s) => s.completed).length; const completionPercentage = Math.round( (completedRequired / requiredSections.length) * 100 ); const response: IntakeStatusResponse = { intakeId: targetIntakeId, patientId: intake.patient_id, completionPercentage, status: intake.status as 'bezig' | 'afgerond', sections, completedCount: completedRequired, totalRequired: requiredSections.length, lastUpdated: intake.updated_at, }; return NextResponse.json(response, { status: 200 }); } catch (error) { console.error('Error in intake status API:', error); return NextResponse.json( { error: 'Er ging iets mis bij het ophalen van de intake status.' }, { status: 500 } ); } }