// Local browser QA only. Every person, clinic, number and record here is fictional. import type * as T from "../../src/api/types"; import type { Source } from "../../src/types"; import { ADAEZE_RECORDS, MARIAM_APP_RECORD, MARIAM_RECORDS, TUNDE_RECORDS } from "./records"; export const FIXTURE_EMAIL = "emeka.okafor@example.test"; /** The fixture's email code and second-factor code. Fixture-only values. */ export const FIXTURE_EMAIL_CODE = "246810"; export const FIXTURE_TOTP_CODE = "135790"; export const FIXTURE_TOTP_SECRET = "JBSWY3DPEHPK3PXP"; /** clarifies: the original line a clarification record is linked to (REV-5). */ export type StoredRecord = T.ContextRecord & { text: string | null; clarifies?: Source["clarification"] }; export type Shared = { medicines: Omit; doses: Omit; daily: T.DailyCheckIn[]; records: Omit; }; export type StoredPatient = { base: Omit< T.PatientDetail, "medicines" | "doses" | "attention" | "checkInLimits" | "activeCheckInId" | "lastCheckIn" >; shared: Shared | null; lastDaily: string | null; lastLooked: Record; newSinceLastCheck: boolean; usedThisWeekBefore: number; variant: "film" | "urgent"; activity: T.ActivityEvent[]; }; const DAY = 86_400_000; export const iso = (ms: number) => new Date(ms).toISOString(); let counter = 0; export const nextId = (prefix: string) => `${prefix}-${Date.now().toString(36)}${(counter++).toString(36)}`; const weekdays = (open: string, close: string, saturday?: [string, string]) => [ ...[1, 2, 3, 4, 5].map((day) => ({ day, open, close })), ...(saturday ? [{ day: 6, open: saturday[0], close: saturday[1] }] : []), ]; const allDay = [0, 1, 2, 3, 4, 5, 6].map((day) => ({ day, open: "00:00", close: "24:00" })); export function clinicianSeed(): T.ClinicianProfile { return { id: "clin-okafor", email: FIXTURE_EMAIL, title: "Dr", fullName: "Dr Emeka Okafor", firstName: "Emeka", lastName: "Okafor", spokenName: "Doctor Okafor", pronoun: "he", profession: "doctor", licensingBody: "MDCN", licenceNumber: "MDCN/R/104233", licenceExpiresAt: "2027-03-31", verifiedAt: "2026-10-01T09:00:00Z", photoUrl: null, mobilePhone: "+234 803 555 0101", idleLockMinutes: 15, notifications: { browser: false, email: true }, termsAcceptedAt: null, }; } export function clinicSeed(alwaysOpen: boolean): T.ClinicProfile { return { id: "clinic-surulere", role: "owner", version: 1, name: "Surulere Family Clinic", address: "12 Example Close, Surulere, Lagos", phone: "+234 1 555 0142", country: "NG", timeZone: "Africa/Lagos", openingHours: alwaysOpen ? allDay : weekdays("08:00", "18:00", ["09:00", "14:00"]), alertWatchHours: alwaysOpen ? allDay : weekdays("08:00", "18:00", ["09:00", "14:00"]), backupContact: { name: "Nurse Bisi Ade", phone: "+2348035550199", email: "bisi.ade@surulere-clinic.example.test" }, emergencyNumbers: [ { number: "767", label: "Lagos State emergency", testedAt: "2026-10-01T10:00:00Z", source: "clinic" }, { number: "112", label: "National emergency", testedAt: "2026-10-01T10:05:00Z", source: "clinic" }, ], }; } function base( id: string, name: string, kind: T.PatientKind, dob: string | null, now: number, change: Partial = {}, ): StoredPatient["base"] { const age = dob ? Math.floor((now - Date.parse(dob)) / (365.25 * DAY)) : null; return { id, kind, name, firstName: name.split(" ")[0], age, sharing: null, lastActivityAt: iso(now - 3 * DAY), dateOfBirth: dob, birthYear: dob ? Number(dob.slice(0, 4)) : null, sex: null, phone: null, pronoun: null, preferredTitle: null, language: "en", hasOwnAccount: kind === "connected", identityOnFile: dob ? "date_of_birth" : "none", timeZone: "Africa/Lagos", quietHours: { start: "22:00", end: "07:00" }, blockedTimes: [], lastCheckId: null, newInformation: null, ...change, }; } const sharing = ( sharedBy: string[], scopes: T.ShareScope[], calls: boolean, changedAt: string, status: T.SharingStatus = "connected", ): T.Sharing => ({ status, sharedBy, scopes, allowsCheckInCalls: calls, withheldSome: false, changedAt }); const counts = (onTime: number, late: number, skipped: number, missed: number, expected: number): T.DoseCounts => ({ onTime, late, skipped, missed, expected, }); /** Mariam's shared data once she is connected (SHR-2 to SHR-5). */ export function mariamShared(now: number): Shared { const by = (who: string, daysAgo: number) => ({ addedBy: who, addedAt: iso(now - daysAgo * DAY), changedAt: iso(now - daysAgo * DAY) }); return { medicines: { withheldSome: false, medicines: [ { id: "m-metformin", name: "Metformin", strength: "500 mg", schedule: "1 tablet twice a day", startDate: "2024-03-10", purpose: "Diabetes", origin: "prescribed", status: "current", stoppedAt: null, prescriber: "Dr Emeka Okafor", ...by("Funke", 28) }, { id: "m-amlodipine", name: "Amlodipine", strength: "5 mg", schedule: "1 tablet once a day", startDate: "2023-11-01", purpose: "Blood pressure", origin: "prescribed", status: "current", stoppedAt: null, prescriber: null, ...by("Mariam", 60) }, { id: "m-amoxicillin", name: "Amoxicillin", strength: "500 mg", schedule: "1 capsule three times a day", startDate: "2026-10-23", purpose: "Chest infection", origin: "prescribed", status: "current", stoppedAt: null, prescriber: "Dr Emeka Okafor", ...by("Funke", 7) }, ], allergies: [ { id: "a-penicillin", substance: "Penicillin", reaction: "Hives", onset: "Within a day", severity: null, year: 2019, treatment: null, recordedWhere: "Ikeja hospital", ...by("Funke", 28) }, ], conditions: [ { id: "c-diabetes", name: "Type 2 diabetes", ...by("Funke", 28) }, { id: "c-bp", name: "Hypertension", ...by("Funke", 28) }, ], profile: { dateOfBirth: "1959-03-04", age: 67, sex: "female", weightKg: null, weightRecordedAt: null, pregnancy: null }, }, doses: { week: { taken: 12, expected: 14, onTime: 12 }, perMedicine: [ { medicineId: "m-metformin", name: "Metformin", schedule: "Morning and evening", week: counts(12, 0, 2, 0, 14), last30: counts(51, 3, 6, 0, 60), changedAt: iso(now - 0.4 * DAY) }, ], patterns: [{ text: "Evening Metformin skipped 2 times this week", medicineId: "m-metformin", changedAt: iso(now - 0.4 * DAY) }], }, daily: [ { id: "d1", date: iso(now - DAY).slice(0, 10), mood: "Okay", energy: "Low", sleep: "Fair", sideEffects: ["Loose stools"], note: "Stomach running since Sunday.", changedAt: iso(now - DAY) }, { id: "d2", date: iso(now - 3 * DAY).slice(0, 10), mood: "Good", energy: "Fair", sleep: "Good", sideEffects: [], note: null, changedAt: iso(now - 3 * DAY) }, ], records: { labs: [ { id: "l-egfr", name: "eGFR", value: "38", unit: "mL/min/1.73m2", range: "Above 60", flag: "low", date: "2026-10-12", ...by("Funke", 17) }, { id: "l-egfr-aug", name: "eGFR", value: "47", unit: "mL/min/1.73m2", range: "Above 60", flag: "low", date: "2026-08-14", ...by("Funke", 78) }, { id: "l-egfr-apr", name: "eGFR", value: "58", unit: "mL/min/1.73m2", range: "Above 60", flag: "low", date: "2026-04-03", ...by("Funke", 211) }, { id: "l-hba1c", name: "HbA1c", value: "7.9", unit: "%", range: "Below 7.0", flag: "high", date: "2026-09-15", ...by("Funke", 40) }, { id: "l-hba1c-jun", name: "HbA1c", value: "7.4", unit: "%", range: "Below 7.0", flag: "high", date: "2026-06-10", ...by("Funke", 143) }, ], documents: [ { id: "doc-yaba", title: "Yaba lab kidney report", kind: "Lab report", date: "2026-10-12", ...by("Funke", 17) }, { id: "doc-ikeja", title: "Ikeja hospital discharge letter", kind: "Discharge letter", date: "2026-09-02", ...by("Funke", 40) }, ], }, }; } function simpleShared(now: number, meds: [string, string, string][], taken: number, expected: number): Shared { const at = iso(now - 2 * DAY); return { medicines: { withheldSome: false, medicines: meds.map(([name, strength, schedule], i) => ({ id: `m-${name.toLowerCase()}`, name, strength, schedule, startDate: "2025-01-10", purpose: null, origin: "prescribed" as const, status: "current" as const, stoppedAt: null, prescriber: null, addedBy: "Patient", addedAt: iso(now - (20 + i) * DAY), changedAt: iso(now - (20 + i) * DAY), })), allergies: [], conditions: [], profile: { dateOfBirth: null, age: null, sex: null, weightKg: null, weightRecordedAt: null, pregnancy: null }, }, doses: { week: { taken, expected, onTime: taken }, perMedicine: meds.map(([name], i) => ({ medicineId: `m-${name.toLowerCase()}`, name, schedule: null, week: counts(i ? 7 : taken - 7 * (meds.length - 1), 0, 0, i ? 0 : expected - taken, i ? 7 : expected - 7 * (meds.length - 1)), last30: counts(28, 1, 1, 0, 30), changedAt: at })), patterns: [], }, daily: [{ id: "d1", date: iso(now - DAY).slice(0, 10), mood: "Good", energy: "Good", sleep: "Good", sideEffects: [], note: null, changedAt: iso(now - DAY) }], records: { labs: [], documents: [] }, }; } export type Seed = { patients: StoredPatient[]; records: StoredRecord[]; invitations: (T.Invitation & { token: string; connect: () => StoredPatient })[]; }; const record = (patientId: string, r: { key: string; title: string; type: T.RecordType; place: string | null; recordDate: string | null; text: string }, addedAt: number, origin: T.RecordOrigin = "upload", addedBy = "Dr Emeka Okafor"): StoredRecord => ({ id: `rec-${patientId.replace(/^p-/, "")}-${r.key}`, patientId, title: r.title, type: r.type, place: r.place, recordDate: r.recordDate, addedBy, addedAt: iso(addedAt), origin, fileName: origin === "upload" ? `${r.title.toLowerCase().replace(/\s+/g, "-")}.txt` : null, contentType: origin === "upload" ? "text/plain" : null, sizeBytes: r.text.length, pages: null, reading: "read", readingError: null, readingErrorCode: null, lineCount: r.text.split("\n").length, heldBack: null, clarification: null, text: r.text, }); const event = (now: number, minutesAgo: number, actor: string, actorKind: T.ActivityEvent["actorKind"], action: string): T.ActivityEvent => ({ id: nextId("act"), at: iso(now - minutesAgo * 60_000), actor, actorKind, action, }); export function seed(now: number): Seed { const mariam: StoredPatient = { base: base("p-mariam", "Mariam Adeyemi", "clinic_added", "1959-03-04", now, { sex: "female", lastActivityAt: iso(now - 2 * 3_600_000), hasOwnAccount: false }), shared: null, lastDaily: null, lastLooked: {}, newSinceLastCheck: false, usedThisWeekBefore: 0, variant: "film", activity: [event(now, 180, "Dr Emeka Okafor", "clinician", "Dr Okafor added Mariam Adeyemi as a clinic patient and confirmed a care relationship.")], }; const adaeze: StoredPatient = { base: base("p-adaeze", "Adaeze Nwosu", "connected", "1968-05-11", now, { sex: "female", pronoun: null, phone: "+234 803 555 0161", lastActivityAt: iso(now - 50 * 60_000), sharing: sharing(["Adaeze"], ["medicines", "doses", "check_ins", "records"], true, iso(now - 50 * 60_000)), newInformation: { since: iso(now - 50 * 60_000), items: ["A new potassium result from MARMAR Health"] }, }), shared: simpleShared(now, [["Lisinopril", "10 mg", "1 tablet once a day"], ["Spironolactone", "25 mg", "1 tablet once a day"]], 13, 14), lastDaily: iso(now - DAY), lastLooked: { medicines: iso(now - 2 * DAY), doses: iso(now - 2 * DAY), check_ins: iso(now - 2 * DAY), records: iso(now - 2 * DAY) }, newSinceLastCheck: true, usedThisWeekBefore: 0, variant: "urgent", activity: [event(now, 50, "Adaeze", "patient", "Adaeze shared a new potassium result.")], }; adaeze.shared!.records.labs.push({ id: "l-k", name: "Potassium", value: "6.8", unit: "mmol/L", range: "3.5 to 5.0", flag: "high", date: "2026-10-23", addedBy: "Adaeze", addedAt: iso(now - 50 * 60_000), changedAt: iso(now - 50 * 60_000) }); const ngozi: StoredPatient = { base: base("p-ngozi", "Ngozi Eze", "connected", "1981-02-19", now, { sex: "female", pronoun: null, phone: "+234 803 555 0174", lastActivityAt: iso(now - 95 * 60_000), sharing: sharing(["Ngozi"], ["medicines", "doses", "check_ins", "records"], true, iso(now - 95 * 60_000)), }), shared: simpleShared(now, [["Amlodipine", "10 mg", "1 tablet once a day"]], 7, 7), lastDaily: iso(now - 4 * 3_600_000), lastLooked: {}, newSinceLastCheck: false, usedThisWeekBefore: 0, variant: "film", activity: [], }; const femi: StoredPatient = { base: base("p-femi", "Femi Balogun", "connected", "1977-08-02", now, { pronoun: null, phone: "+234 803 555 0187", lastActivityAt: iso(now - 26 * 3_600_000), sharing: sharing(["Femi"], ["medicines", "doses", "check_ins"], true, iso(now - 26 * 3_600_000)), }), shared: simpleShared(now, [["Metformin", "500 mg", "1 tablet twice a day"]], 11, 14), lastDaily: iso(now - 2 * DAY), lastLooked: {}, newSinceLastCheck: false, usedThisWeekBefore: 1, variant: "film", activity: [], }; const chidi: StoredPatient = { base: base("p-chidi", "Chidi Okeke", "connected", "1987-01-30", now, { sex: "male", pronoun: null, phone: "+234 803 555 0112", lastActivityAt: iso(now - 4 * DAY), sharing: sharing(["Chidi"], ["medicines", "doses"], false, iso(now - 4 * DAY)), }), shared: simpleShared(now, [["Losartan", "50 mg", "1 tablet once a day"]], 6, 7), lastDaily: null, lastLooked: {}, newSinceLastCheck: false, usedThisWeekBefore: 0, variant: "film", activity: [event(now, 4 * 1440, "Chidi", "patient", "Chidi shared medicines and doses taken with Dr Okafor.")], }; const halima: StoredPatient = { base: base("p-halima", "Halima Yusuf", "connected", "1955-06-14", now, { sex: "female", phone: "+234 803 555 0125", lastActivityAt: iso(now - 6 * DAY), sharing: sharing(["Halima"], ["medicines", "doses"], false, iso(now - DAY), "stopped"), }), shared: null, lastDaily: null, lastLooked: {}, newSinceLastCheck: false, usedThisWeekBefore: 0, variant: "film", activity: [event(now, 1440, "Halima", "patient", "Halima stopped sharing with Dr Okafor. Live access ended.")], }; const tunde: StoredPatient = { base: base("p-tunde", "Tunde Bakare", "clinic_added", "1981-04-09", now, { sex: "male", lastActivityAt: iso(now - 12 * DAY), hasOwnAccount: false }), shared: null, lastDaily: null, lastLooked: {}, newSinceLastCheck: false, usedThisWeekBefore: 0, variant: "film", activity: [event(now, 12 * 1440, "Dr Emeka Okafor", "clinician", "Dr Okafor added Tunde Bakare as a clinic patient.")], }; const records = [ // FIXTURE_FILM=1 leaves out the hospital note, so the demo film can add it as a photo. ...MARIAM_RECORDS.filter((r) => !(process.env.FIXTURE_FILM === "1" && r.key === "hospital")).map((r, i) => record("p-mariam", r, now - (180 - i) * 60_000)), ...ADAEZE_RECORDS.map((r, i) => record("p-adaeze", r, now - (3 * 1440 - i) * 60_000, i ? "app" : "upload", i ? "Adaeze (MARMAR Health)" : "Dr Emeka Okafor")), ...TUNDE_RECORDS.map((r) => record("p-tunde", r, now - 12 * DAY)), ]; const invitations: Seed["invitations"] = [ { id: "inv-mariam", token: "mariam-invite-demo", patientName: "Mariam Adeyemi", patientAge: 67, patientPronoun: "she", withPerson: { name: "Funke", relation: "daughter" }, scopes: ["medicines", "doses", "check_ins", "records"], allowsCheckInCalls: true, withheldSome: false, sentAt: iso(now - 25 * 60_000), expiresAt: iso(now + 14 * DAY - 25 * 60_000), possibleMatch: { patientId: "p-mariam", name: "Mariam Adeyemi", dateOfBirth: "1959-03-04" }, connect: () => mariam, }, { id: "inv-ibrahim", token: "ibrahim-invite-demo", patientName: "Ibrahim Musa", patientAge: 58, patientPronoun: "he", withPerson: null, scopes: ["medicines", "doses"], allowsCheckInCalls: false, withheldSome: false, sentAt: iso(now - 2 * DAY), expiresAt: iso(now + 12 * DAY), possibleMatch: null, connect: () => ({ base: base("p-ibrahim", "Ibrahim Musa", "connected", "1968-09-21", now, { sex: "male", pronoun: null, phone: "+234 803 555 0133", lastActivityAt: iso(now), sharing: sharing(["Ibrahim"], ["medicines", "doses"], false, iso(now)), }), shared: simpleShared(now, [["Nifedipine", "30 mg", "1 tablet once a day"]], 7, 7), lastDaily: null, lastLooked: {}, newSinceLastCheck: false, usedThisWeekBefore: 0, variant: "film", activity: [], }), }, ]; return { patients: [mariam, adaeze, ngozi, femi, chidi, halima, tunde], records, invitations }; } /** Mariam once she accepts: her shares, Funke, and the app record (REC-8). */ export function connectMariam(p: StoredPatient, now: number): StoredRecord { p.base = { ...p.base, kind: "connected", hasOwnAccount: true, pronoun: null, preferredTitle: "Mrs Adeyemi", phone: "+234 803 555 0147", sharing: sharing(["Mariam", "Funke"], ["medicines", "doses", "check_ins", "records"], true, iso(now)), lastActivityAt: iso(now), }; p.shared = mariamShared(now); p.lastDaily = iso(now - DAY); return { ...record("p-mariam", { key: "app", title: "From MARMAR Health", type: "other", place: "MARMAR Health app", recordDate: "2026-10-23", text: MARIAM_APP_RECORD }, now, "app", "Funke (MARMAR Health)"), }; }