marmar-context / web / context / src / logic / eligibility.ts
eligibility.ts
Raw
import type { EligibilityCode, PatientDetail } from "../api/types";
import { firstName, pronouns } from "./format";
import { limitState } from "./windows";

export type Eligibility =
  | { allowed: true; notes: string[] }
  | { allowed: false; code: EligibilityCode; message: string; action: "invite_app" | null };

type Patient = Pick<
  PatientDetail,
  | "kind"
  | "name"
  | "age"
  | "sharing"
  | "hasOwnAccount"
  | "identityOnFile"
  | "pronoun"
  | "activeCheckInId"
  | "checkInLimits"
>;

const no = (code: EligibilityCode, message: string, action: "invite_app" | null = null): Eligibility => ({
  allowed: false,
  code,
  message,
  action,
});

/**
 * YCI-1: who Yasmar may check in with, and in plain words what is missing.
 * Only adults with their own MARMAR Health account, answering for themselves,
 * who allowed this clinician to ask for calls (section 7). The server checks again.
 */
export function checkInEligibility(patient: Patient, now = new Date()): Eligibility {
  const first = firstName(patient.name);
  const they = pronouns(patient.pronoun);
  if (patient.kind === "clinic_added")
    return no("clinic_added", `Invite ${first} to MARMAR Health to use check-ins.`, "invite_app");
  const sharing = patient.sharing;
  if (!sharing || sharing.status === "stopped")
    return no("sharing_stopped", `${first} stopped sharing, so check-ins are not available.`);
  if (sharing.status === "paused")
    return no("sharing_paused", `${first} paused sharing. Check-ins are available again when ${they.subject} ${they.subject === "they" ? "resume" : "resumes"}.`);
  if (!patient.hasOwnAccount)
    return no(
      "no_own_account",
      `Check-ins are only for patients answering on their own MARMAR Health account. ${first}'s record is managed by someone else.`,
    );
  if (!sharing.allowsCheckInCalls)
    return no("calls_not_allowed", `${first} has not allowed Yasmar check-ins. ${first} can turn this on in MARMAR Health.`);
  if (patient.identityOnFile === "none")
    return no(
      "no_date_of_birth",
      `${first}'s date of birth is not in MARMAR Health, so Yasmar cannot check who is answering.`,
    );
  if (patient.age === null || patient.age < 18)
    return no(
      "under_18",
      patient.age === null
        ? `${first}'s age is not known. Check-ins are for adults answering for themselves.`
        : `Check-ins are for adults answering for themselves. ${first} is under 18.`,
    );
  if (patient.activeCheckInId) return no("active_check_in", `A check-in with ${first} is already open.`);
  const limits = limitState(patient.checkInLimits, first, now);
  if (limits.blocked) return no("limit_reached", limits.message);
  const notes: string[] = [];
  if (patient.identityOnFile === "birth_year")
    notes.push(
      `Only ${first}'s birth year is on file. Yasmar checks the year, and ${they.possessive} unlocked phone is the second check.`,
    );
  return { allowed: true, notes };
}