import type { CheckInStatus, Tier } from "../api/types"; import { firstName } from "./format"; type TieredFinding = { id: string; tier: string }; /** Findings that cannot wait for a check-in: Urgent review (CHK-6). */ export const urgentFindings = (findings: T[]) => findings.filter((f) => f.tier === "get_help_now"); /** The clinician's "I will speak to Mariam today", kept for the day it was given. */ export type CallConfirmation = { patientId: string; confirmedAt: string }; const localDate = (d: Date, offsetMinutes: number) => new Date(d.getTime() + offsetMinutes * 60_000).toISOString().slice(0, 10); /** A confirmation counts only on the clinic's day it was given. */ export function confirmationValid( confirmation: CallConfirmation | null, patientId: string, now: Date, clinicOffset: number, ): boolean { if (!confirmation || confirmation.patientId !== patientId) return false; return localDate(new Date(confirmation.confirmedAt), clinicOffset) === localDate(now, clinicOffset); } export type UrgentGate = | { mode: "none"; canAskYasmar: true } | { mode: "call_first"; canAskYasmar: boolean; confirmLine: string; callLine: string }; /** * YCI-1a, urgent first: with an Urgent review finding, Context leads with "Call Mariam"; * Ask Yasmar to check in opens only after "I will speak to Mariam today". */ export function urgentGate(input: { findings: TieredFinding[]; patientId: string; patientName: string; confirmation: CallConfirmation | null; now: Date; clinicOffset: number; }): UrgentGate { if (!urgentFindings(input.findings).length) return { mode: "none", canAskYasmar: true }; const first = firstName(input.patientName); return { mode: "call_first", canAskYasmar: confirmationValid(input.confirmation, input.patientId, input.now, input.clinicOffset), confirmLine: `I will speak to ${first} today`, callLine: `Call ${first}`, }; } const finished: CheckInStatus[] = ["completed", "declined", "identity_not_confirmed", "cancelled"]; /** * YCI-1a: a check-in linked to an Urgent review or Review today finding that has not * finished by the end of clinic hours raises "Mariam not reached. Call today." */ export function notReachedAlert(input: { status: CheckInStatus; linkedTiers: (Tier | string)[]; patientName: string; clinicCloseAt: Date | null; now: Date; }): string | null { const pressing = input.linkedTiers.some((t) => t === "get_help_now" || t === "today"); if (!pressing || finished.includes(input.status)) return null; const late = input.status === "not_reached" || (input.clinicCloseAt !== null && input.now >= input.clinicCloseAt); return late ? `${firstName(input.patientName)} not reached. Call today.` : null; }