feat(agenda): Epic 4 - EPD Koppeling (appointment-report integration)

Implements bidirectional linking between appointments and reports:
- E4.S1: Create report from appointment modal with encounter pre-linked
- E4.S2: Link existing reports to appointments via EncounterSelector
- E4.S3: Show linked reports in appointment modal edit view
- E4.S4: Navigation between appointments and reports with deep linking

Also includes bug fixes for patient search:
- Fix FHIR to internal format mapping for patient data
- Fix debounced search interference after patient selection
- Fix UUID handling for optional practitioner_id

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
colinislit
2025-12-02 19:34:07 +01:00
parent e661c97491
commit b7b5a0888b
18 changed files with 1612 additions and 61 deletions

View File

@@ -26,20 +26,44 @@ export async function GET(request: NextRequest) {
// Build query
let query = supabaseAdmin.from('patients').select('*');
// Search by name (family or given)
// General search (searches name, BSN, and client number)
const q = searchParams.get('q');
if (q) {
// Check if input looks like a number (BSN or client number)
const isNumeric = /^\d+$/.test(q.trim());
if (isNumeric) {
// Search BSN and client number
query = query.or(
`identifier_bsn.ilike.%${q}%,identifier_client_number.ilike.%${q}%`
);
} else {
// Search name fields
query = query.or(
`name_family.ilike.%${q}%,name_given.cs.{${q}}`
);
}
}
// Search by name (family or given) - legacy support
const name = searchParams.get('name');
if (name) {
if (name && !q) {
query = query.or(
`name_family.ilike.%${name}%,name_given.cs.{${name}}`
);
}
// Search by identifier (BSN)
// Search by identifier (BSN) - legacy support
const identifier = searchParams.get('identifier');
if (identifier) {
if (identifier && !q) {
query = query.eq('identifier_bsn', identifier);
}
// Search by client number
const clientNumber = searchParams.get('clientNumber');
if (clientNumber && !q) {
query = query.ilike('identifier_client_number', `%${clientNumber}%`);
}
// Search by birth date
const birthdate = searchParams.get('birthdate');
if (birthdate) {
@@ -55,6 +79,15 @@ export async function GET(request: NextRequest) {
// Order by updated_at descending (newest first)
query = query.order('updated_at', { ascending: false });
// Limit results (_count parameter)
const count = searchParams.get('_count');
if (count) {
const limit = parseInt(count, 10);
if (!isNaN(limit) && limit > 0) {
query = query.limit(limit);
}
}
// Execute query
const { data: patients, error } = await query;

View File

@@ -81,7 +81,7 @@ export async function POST(request: NextRequest) {
);
}
const { patient_id, type, content, ai_confidence, ai_reasoning } = result.data;
const { patient_id, type, content, ai_confidence, ai_reasoning, encounter_id, intake_id } = result.data;
const { data, error } = await supabase
.from('reports')
.insert({
@@ -90,6 +90,8 @@ export async function POST(request: NextRequest) {
content,
ai_confidence,
ai_reasoning,
encounter_id,
intake_id,
created_by: authData.user.id,
})
.select('*')