@alfadocs/ui-kit-debug 0.88.0 → 0.89.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/{antenatal-schedule-timeline-BzJZqS-8.js → antenatal-schedule-timeline-UiZQze4P.js} +3 -3
- package/dist/_chunks/{antenatal-schedule-timeline-BzJZqS-8.js.map → antenatal-schedule-timeline-UiZQze4P.js.map} +1 -1
- package/dist/_chunks/{appointment-timeline-DQTG0btW.js → appointment-timeline-CwpbQGC1.js} +2 -2
- package/dist/_chunks/{appointment-timeline-DQTG0btW.js.map → appointment-timeline-CwpbQGC1.js.map} +1 -1
- package/dist/_chunks/{ar-B1GhtlBP.js → ar-C9FRRqJs.js} +2 -2
- package/dist/_chunks/{ar-B1GhtlBP.js.map → ar-C9FRRqJs.js.map} +1 -1
- package/dist/_chunks/{booking-BvCu2XWY.js → booking-DEwmDprH.js} +4 -4
- package/dist/_chunks/{booking-BvCu2XWY.js.map → booking-DEwmDprH.js.map} +1 -1
- package/dist/_chunks/{cycle-calculator-OQp8Ik5U.js → cycle-calculator-DXSNIWFk.js} +3 -3
- package/dist/_chunks/{cycle-calculator-OQp8Ik5U.js.map → cycle-calculator-DXSNIWFk.js.map} +1 -1
- package/dist/_chunks/{date-picker-DzQS8mvt.js → date-picker-ffmyjvlI.js} +2 -2
- package/dist/_chunks/{date-picker-DzQS8mvt.js.map → date-picker-ffmyjvlI.js.map} +1 -1
- package/dist/_chunks/{date-picker-variants-CYQAm1Gk.js → date-picker-variants-D9B6hg15.js} +37 -24
- package/dist/_chunks/{date-picker-variants-CYQAm1Gk.js.map → date-picker-variants-D9B6hg15.js.map} +1 -1
- package/dist/_chunks/{date-range-picker-Cbz1SKhm.js → date-range-picker-DSOH50-f.js} +48 -47
- package/dist/_chunks/date-range-picker-DSOH50-f.js.map +1 -0
- package/dist/_chunks/{date-time-picker-B5IpU-nd.js → date-time-picker-BU-j0qj4.js} +2 -2
- package/dist/_chunks/{date-time-picker-B5IpU-nd.js.map → date-time-picker-BU-j0qj4.js.map} +1 -1
- package/dist/_chunks/{de-DETDHQ0o.js → de-C3_KZ6mR.js} +2 -2
- package/dist/_chunks/{de-DETDHQ0o.js.map → de-C3_KZ6mR.js.map} +1 -1
- package/dist/_chunks/{due-date-calculator-DASNRgnH.js → due-date-calculator-BR_GuqHH.js} +3 -3
- package/dist/_chunks/{due-date-calculator-DASNRgnH.js.map → due-date-calculator-BR_GuqHH.js.map} +1 -1
- package/dist/_chunks/{el-HV3jOI0z.js → el-v9xDKHgu.js} +2 -2
- package/dist/_chunks/{el-HV3jOI0z.js.map → el-v9xDKHgu.js.map} +1 -1
- package/dist/_chunks/{es-Dpo7Olwo.js → es-Lgmjq9Rp.js} +2 -2
- package/dist/_chunks/{es-Dpo7Olwo.js.map → es-Lgmjq9Rp.js.map} +1 -1
- package/dist/_chunks/{fr-CgD4Jvtq.js → fr-INS1OD7m.js} +2 -2
- package/dist/_chunks/{fr-CgD4Jvtq.js.map → fr-INS1OD7m.js.map} +1 -1
- package/dist/_chunks/{gestational-age-calculator-BpYw-WF-.js → gestational-age-calculator-DBIcxfjP.js} +5 -5
- package/dist/_chunks/{gestational-age-calculator-BpYw-WF-.js.map → gestational-age-calculator-DBIcxfjP.js.map} +1 -1
- package/dist/_chunks/{hcg-doubling-Dqj-EqMT.js → hcg-doubling-BgCkds8R.js} +2 -2
- package/dist/_chunks/{hcg-doubling-Dqj-EqMT.js.map → hcg-doubling-BgCkds8R.js.map} +1 -1
- package/dist/_chunks/{hi-Bf8CAP6b.js → hi-BUYtlosF.js} +2 -2
- package/dist/_chunks/{hi-Bf8CAP6b.js.map → hi-BUYtlosF.js.map} +1 -1
- package/dist/_chunks/{isSameWeek-BYl5poz_.js → isSameWeek-BUNsv4OO.js} +2 -2
- package/dist/_chunks/{isSameWeek-BYl5poz_.js.map → isSameWeek-BUNsv4OO.js.map} +1 -1
- package/dist/_chunks/{it-mt1V2DFS.js → it-VfsdUqeV.js} +3 -3
- package/dist/_chunks/{it-mt1V2DFS.js.map → it-VfsdUqeV.js.map} +1 -1
- package/dist/_chunks/{ja-CbOHn2TO.js → ja-Dfk9qwzE.js} +2 -2
- package/dist/_chunks/{ja-CbOHn2TO.js.map → ja-Dfk9qwzE.js.map} +1 -1
- package/dist/_chunks/{link-cell-renderer-BQGUs45d.js → link-cell-renderer-BzITwSXq.js} +2 -2
- package/dist/_chunks/{link-cell-renderer-BQGUs45d.js.map → link-cell-renderer-BzITwSXq.js.map} +1 -1
- package/dist/_chunks/{marketplace-app-shell-DX5aLeIJ.js → marketplace-app-shell-35eRpT3r.js} +11 -5
- package/dist/_chunks/{marketplace-app-shell-DX5aLeIJ.js.map → marketplace-app-shell-35eRpT3r.js.map} +1 -1
- package/dist/_chunks/{nl-C6Y0QXnm.js → nl-Crxwg0IZ.js} +2 -2
- package/dist/_chunks/{nl-C6Y0QXnm.js.map → nl-Crxwg0IZ.js.map} +1 -1
- package/dist/_chunks/{patient-details-B9LCdyWW.js → patient-details-Dggsz-ki.js} +2 -2
- package/dist/_chunks/{patient-details-B9LCdyWW.js.map → patient-details-Dggsz-ki.js.map} +1 -1
- package/dist/_chunks/{patient-table-B6AKMTm3.js → patient-table-ps4byVwA.js} +2 -2
- package/dist/_chunks/{patient-table-B6AKMTm3.js.map → patient-table-ps4byVwA.js.map} +1 -1
- package/dist/_chunks/{pl-BdazJAOp.js → pl-IdHtxWTl.js} +3 -3
- package/dist/_chunks/{pl-BdazJAOp.js.map → pl-IdHtxWTl.js.map} +1 -1
- package/dist/_chunks/{pregnancy-dating-msMl_6wz.js → pregnancy-dating-CvIRwWaG.js} +5 -5
- package/dist/_chunks/{pregnancy-dating-msMl_6wz.js.map → pregnancy-dating-CvIRwWaG.js.map} +1 -1
- package/dist/_chunks/{pt-B3UKvhHp.js → pt-CY2m2bBz.js} +2 -2
- package/dist/_chunks/{pt-B3UKvhHp.js.map → pt-CY2m2bBz.js.map} +1 -1
- package/dist/_chunks/{radiograph-panel.agent-M9XD4ze4.js → radiograph-panel.agent-C93AXgtV.js} +3 -3
- package/dist/_chunks/{radiograph-panel.agent-M9XD4ze4.js.map → radiograph-panel.agent-C93AXgtV.js.map} +1 -1
- package/dist/_chunks/{ro-DVTEQpPK.js → ro-qoxLkXED.js} +2 -2
- package/dist/_chunks/{ro-DVTEQpPK.js.map → ro-qoxLkXED.js.map} +1 -1
- package/dist/_chunks/{ru-C-Mp40SC.js → ru-BVZlxoKf.js} +3 -3
- package/dist/_chunks/{ru-C-Mp40SC.js.map → ru-BVZlxoKf.js.map} +1 -1
- package/dist/_chunks/{sq-DierVlw-.js → sq--eT8hIiD.js} +2 -2
- package/dist/_chunks/{sq-DierVlw-.js.map → sq--eT8hIiD.js.map} +1 -1
- package/dist/_chunks/subDays-fEHRx4hg.js +8 -0
- package/dist/_chunks/{subDays-DIKRViZm.js.map → subDays-fEHRx4hg.js.map} +1 -1
- package/dist/_chunks/{sv-gQWo_19z.js → sv-BGNQ5gX6.js} +2 -2
- package/dist/_chunks/{sv-gQWo_19z.js.map → sv-BGNQ5gX6.js.map} +1 -1
- package/dist/_chunks/{tr-Btm6M3Km.js → tr-B2NEu6Gy.js} +2 -2
- package/dist/_chunks/{tr-Btm6M3Km.js.map → tr-B2NEu6Gy.js.map} +1 -1
- package/dist/_chunks/{zh-CN-DkVN4vsB.js → zh-CN-DClT1wDk.js} +3 -3
- package/dist/_chunks/{zh-CN-DkVN4vsB.js.map → zh-CN-DClT1wDk.js.map} +1 -1
- package/dist/agent-catalog.json +1 -1
- package/dist/components/_shared/date-picker-variants.d.ts +1 -0
- package/dist/components/_shared/date-picker-variants.d.ts.map +1 -1
- package/dist/components/appointment-timeline/index.js +1 -1
- package/dist/components/booking/index.js +1 -1
- package/dist/components/cycle-calculator/index.js +1 -1
- package/dist/components/data-table/index.js +1 -1
- package/dist/components/date-picker/index.js +1 -1
- package/dist/components/date-range-picker/date-range-picker.d.ts +10 -0
- package/dist/components/date-range-picker/date-range-picker.d.ts.map +1 -1
- package/dist/components/date-range-picker/index.js +1 -1
- package/dist/components/date-time-picker/index.js +1 -1
- package/dist/components/due-date-calculator/index.js +2 -2
- package/dist/components/gestational-age-calculator/index.js +1 -1
- package/dist/components/hcg-doubling/index.js +1 -1
- package/dist/components/patient-details/index.js +1 -1
- package/dist/components/patient-table/index.js +1 -1
- package/dist/components/pregnancy-dating/index.js +1 -1
- package/dist/components/radiograph-panel/index.js +1 -1
- package/dist/index.js +16 -16
- package/dist/patterns/marketplace-app-shell/index.js +1 -1
- package/dist/patterns/marketplace-app-shell/marketplace-app-shell.d.ts.map +1 -1
- package/dist/tokens.css +1 -1
- package/package.json +1 -1
- package/dist/_chunks/date-range-picker-Cbz1SKhm.js.map +0 -1
- package/dist/_chunks/subDays-DIKRViZm.js +0 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as y, j as N } from "./date-picker-variants-
|
|
2
|
-
import { s as p } from "./subDays-
|
|
1
|
+
import { a as y, j as N } from "./date-picker-variants-D9B6hg15.js";
|
|
2
|
+
import { s as p } from "./subDays-fEHRx4hg.js";
|
|
3
3
|
import { jsx as u, jsxs as f } from "react/jsx-runtime";
|
|
4
4
|
import { useMemo as M } from "react";
|
|
5
5
|
import { useTranslation as T } from "react-i18next";
|
|
@@ -410,4 +410,4 @@ export {
|
|
|
410
410
|
ht as r,
|
|
411
411
|
w as t
|
|
412
412
|
};
|
|
413
|
-
//# sourceMappingURL=antenatal-schedule-timeline-
|
|
413
|
+
//# sourceMappingURL=antenatal-schedule-timeline-UiZQze4P.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"antenatal-schedule-timeline-BzJZqS-8.js","sources":["../../src/components/_shared/obstetrics.ts","../../src/components/_shared/antenatal-schedule.ts","../../src/components/_shared/antenatal-schedule-timeline.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Obstetric maths — pure, framework-free, unit-testable. */\n/* */\n/* Estimated-due-date dating, gestational age, trimester classification */\n/* and milestone projection, plus the trimester→presentation maps the */\n/* obstetric calculators share. All three dating methods are normalised */\n/* onto a single \"gestational start\" (the notional first day of the last */\n/* menstrual period, GA day 0) so gestational age and trimester are */\n/* computed identically regardless of how the pregnancy was dated. */\n/* */\n/* Home for cross-component obstetric logic: the due-date and */\n/* gestational-age calculators both consume this module, so it lives in */\n/* `_shared/` rather than inside a single component directory. */\n/* `due-date-calculator/gestation.ts` re-exports this file for */\n/* back-compatibility with the public package barrel. */\n/* ------------------------------------------------------------------ */\n\nimport { addDays, subDays, differenceInCalendarDays } from 'date-fns';\n\nexport type DueDateMethod = 'lmp' | 'conception' | 'ivf';\n\n/** Embryo age at transfer, in days (cleavage vs blastocyst). */\nexport type EmbryoAge = 3 | 5;\n\nexport type Trimester =\n | 'preconception'\n | 'first'\n | 'second'\n | 'third'\n | 'postterm';\n\n/** Number of fetuses. Drives surveillance schedule and planned-birth timing. */\nexport type Plurality = 'singleton' | 'twin' | 'triplet';\n\n/**\n * Placentation of a multiple pregnancy. `unknown` is the safe default — managed\n * as monochorionic (the higher-risk assumption) until proven otherwise, per\n * NICE QS46. Only meaningful when `plurality !== 'singleton'`.\n */\nexport type Chorionicity =\n | 'dichorionic-diamniotic'\n | 'monochorionic-diamniotic'\n | 'monochorionic-monoamniotic'\n | 'unknown';\n\n/** Naegele's rule: a term pregnancy is 280 days from the LMP. */\nexport const GESTATION_DAYS = 280;\n/** Ovulation/conception sits ~14 days after the LMP in a 28-day cycle. */\nexport const CONCEPTION_OFFSET_DAYS = 14;\n/** A term pregnancy is 266 days from conception. */\nexport const TERM_FROM_CONCEPTION_DAYS = 266;\n/** Standard menstrual-cycle length Naegele assumes. */\nexport const DEFAULT_CYCLE_LENGTH = 28;\n\nexport interface DueDateInput {\n method: DueDateMethod;\n /** The reference date for the chosen method (LMP / conception / transfer). */\n date: Date;\n /** LMP only — average cycle length in days. Defaults to 28. */\n cycleLength?: number;\n /** IVF only — embryo age at transfer in days. Defaults to 5 (blastocyst). */\n embryoAge?: EmbryoAge;\n}\n\nexport interface GestationalAge {\n weeks: number;\n days: number;\n totalDays: number;\n}\n\nexport interface DueDateResult {\n /** Estimated date of delivery. */\n dueDate: Date;\n /** GA day-0 anchor (notional LMP) the trimester maths counts from. */\n gestationalStart: Date;\n /** Gestational age as of `today`; `null` before conception. */\n gestationalAge: GestationalAge | null;\n trimester: Trimester;\n}\n\n/** Classify gestational age (in completed weeks) into a trimester. */\nexport function trimesterForWeeks(totalDays: number): Trimester {\n if (totalDays < 0) return 'preconception';\n const weeks = Math.floor(totalDays / 7);\n if (weeks < 14) return 'first';\n if (weeks < 28) return 'second';\n if (weeks < 42) return 'third';\n return 'postterm';\n}\n\n/**\n * Resolve the gestational start (notional LMP) and due date for a method.\n * `cycleLength` shifts a longer/shorter cycle's ovulation, moving the due\n * date while GA stays counted from the LMP — the standard convention.\n */\nexport function computeDueDate(\n input: DueDateInput,\n today: Date,\n): DueDateResult {\n const { method, date } = input;\n\n let gestationalStart: Date;\n let dueDate: Date;\n\n if (method === 'lmp') {\n const cycle = input.cycleLength ?? DEFAULT_CYCLE_LENGTH;\n gestationalStart = date;\n dueDate = addDays(date, GESTATION_DAYS + (cycle - DEFAULT_CYCLE_LENGTH));\n } else if (method === 'conception') {\n gestationalStart = subDays(date, CONCEPTION_OFFSET_DAYS);\n dueDate = addDays(date, TERM_FROM_CONCEPTION_DAYS);\n } else {\n // IVF: back out the conception (egg-retrieval) date from the transfer.\n const embryo = input.embryoAge ?? 5;\n const conceptionDate = subDays(date, embryo);\n gestationalStart = subDays(conceptionDate, CONCEPTION_OFFSET_DAYS);\n dueDate = addDays(conceptionDate, TERM_FROM_CONCEPTION_DAYS);\n }\n\n const totalDays = differenceInCalendarDays(today, gestationalStart);\n const gestationalAge: GestationalAge | null =\n totalDays < 0\n ? null\n : {\n weeks: Math.floor(totalDays / 7),\n days: totalDays % 7,\n totalDays,\n };\n\n return {\n dueDate,\n gestationalStart,\n gestationalAge,\n trimester: trimesterForWeeks(totalDays),\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Milestones */\n/* ------------------------------------------------------------------ */\n\n/** Gestational weeks at which clinically notable milestones fall. */\nexport const MILESTONE_WEEKS = [12, 24, 28, 37, 40] as const;\n\nexport interface GestationalMilestone {\n /** Stable key (`w12`, `w24`, …) for i18n lookup. */\n key: string;\n /** Completed gestational weeks. */\n week: number;\n /** Calendar date the milestone is reached. */\n date: Date;\n}\n\n/**\n * Project the standard milestone dates from a gestational start (notional\n * LMP / GA day 0): end of first trimester (12w), viability (24w), third\n * trimester (28w), term (37w) and the 40-week due date.\n */\nexport function gestationalMilestones(\n gestationalStart: Date,\n): GestationalMilestone[] {\n return MILESTONE_WEEKS.map((week) => ({\n key: `w${week}`,\n week,\n date: addDays(gestationalStart, week * 7),\n }));\n}\n\n/* ------------------------------------------------------------------ */\n/* Trimester presentation */\n/* */\n/* Shared by DueDateCalculator and GestationalAgeCalculator so the */\n/* on-screen badge and the inserted result-card chip stay in lockstep. */\n/* These are plain string maps (no React/Badge import) — the same */\n/* pattern as `_shared/entity-card`'s state→badge maps. */\n/* ------------------------------------------------------------------ */\n\n/** Badge `variant` names used for trimesters (the Badge component's union). */\nexport type TrimesterBadgeVariant =\n | 'neutral'\n | 'info'\n | 'success'\n | 'warning'\n | 'error';\n\n/** Trimester → on-screen Badge variant. */\nexport const TRIMESTER_BADGE_VARIANT: Record<Trimester, TrimesterBadgeVariant> =\n {\n preconception: 'neutral',\n first: 'info',\n second: 'success',\n third: 'warning',\n postterm: 'error',\n };\n\n/**\n * DS token NAME backing each trimester's result-card chip — the solid-fill\n * companion of `TRIMESTER_BADGE_VARIANT` (Badge `error` fills `--destructive`;\n * `neutral` fills `--muted`). Passed as the card's `highlightToken` so the\n * inserted PNG chip matches the on-screen trimester badge; `InsertButton`\n * resolves the name to a concrete colour at raster time.\n *\n * `third` resolves to `--warning-readable`, which the result card's colour\n * probe resolves in the live themed DOM: orange-600 in light, non-accessible\n * mode (where bare `--warning` yellow only reaches ~3.2:1 on the white card)\n * and `--warning` elsewhere, whose deepened ramp already clears contrast.\n * Resolving the token NAME at the card means the choice tracks the live theme\n * regardless of where the theme class lives.\n */\nexport const TRIMESTER_HIGHLIGHT_TOKEN: Record<Trimester, string> = {\n preconception: '--muted',\n first: '--info',\n second: '--success',\n third: '--warning-readable',\n postterm: '--destructive',\n};\n\n/** The DS token name for a trimester's result-card chip. */\nexport function trimesterHighlightToken(trimester: Trimester): string {\n return TRIMESTER_HIGHLIGHT_TOKEN[trimester];\n}\n\n/* ------------------------------------------------------------------ */\n/* Multiple pregnancy — planned-birth timing */\n/* */\n/* For multiples the \"due date\" is not a single 40-week point but a */\n/* recommended planned-birth window that depends on chorionicity. The */\n/* UK (NICE NG137) and US (ACOG PB231) bodies diverge by ~1 week for */\n/* diamniotic twins, so the guideline is selectable; Italian practice */\n/* aligns closer to the earlier NICE stance, so `nice` is the default. */\n/* ------------------------------------------------------------------ */\n\n/** Which body's planned-birth timing to use for multiples. */\nexport type BirthGuideline = 'nice' | 'acog';\n\n/** Planned-birth window as GA days (week*7 + day), inclusive, with source. */\nexport interface BirthWindow {\n startDay: number;\n endDay: number;\n source: string;\n}\n\nconst day = (week: number, d = 0): number => week * 7 + d;\n\n/**\n * Recommended planned-birth window for a multiple pregnancy, by chorionicity.\n * Singletons return `null` (use the 40-week EDD instead). `unknown`\n * chorionicity is managed as monochorionic-diamniotic — the higher-risk\n * default (NICE QS46).\n */\nexport function plannedBirthWindow(\n plurality: Plurality,\n chorionicity: Chorionicity = 'unknown',\n guideline: BirthGuideline = 'nice',\n): BirthWindow | null {\n if (plurality === 'singleton') return null;\n\n if (plurality === 'triplet') {\n // NICE/ACOG: offer planned birth for uncomplicated triplets at ~35 weeks.\n return { startDay: day(35, 0), endDay: day(35, 6), source: 'NICE-NG137' };\n }\n\n // Twins. `unknown` is managed as monochorionic-diamniotic.\n const effective: Chorionicity =\n chorionicity === 'unknown' ? 'monochorionic-diamniotic' : chorionicity;\n\n if (guideline === 'acog') {\n switch (effective) {\n case 'dichorionic-diamniotic':\n return {\n startDay: day(38, 0),\n endDay: day(38, 6),\n source: 'ACOG-PB231',\n };\n case 'monochorionic-diamniotic':\n return {\n startDay: day(36, 0),\n endDay: day(37, 6),\n source: 'ACOG-PB231',\n };\n case 'monochorionic-monoamniotic':\n return {\n startDay: day(32, 0),\n endDay: day(34, 0),\n source: 'ACOG-PB231',\n };\n }\n }\n\n // NICE NG137 (default).\n switch (effective) {\n case 'dichorionic-diamniotic':\n return { startDay: day(37, 0), endDay: day(37, 6), source: 'NICE-NG137' };\n case 'monochorionic-diamniotic':\n return { startDay: day(36, 0), endDay: day(36, 6), source: 'NICE-NG137' };\n case 'monochorionic-monoamniotic':\n return { startDay: day(32, 0), endDay: day(33, 6), source: 'NICE-NG137' };\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* Ultrasound (CRL) redating — ACOG Committee Opinion 700 */\n/* */\n/* A first-trimester crown-rump length gives a gestational age via */\n/* Robinson–Fleming. When it disagrees with the LMP by more than the */\n/* ACOG CO 700 threshold (which widens with gestation), the ultrasound */\n/* estimate replaces the LMP dating. Below threshold, the LMP stands. */\n/* The returned `ecoDiffDays` mirrors the server-side `eco_diff_days` */\n/* offset so the kit and the editor-actions backend can be reconciled. */\n/* ------------------------------------------------------------------ */\n\n/** Robinson–Fleming dating is validated for CRL 45–84 mm. */\nexport const CRL_DATING_MIN_MM = 45;\nexport const CRL_DATING_MAX_MM = 84;\n\n/** Gestational age in days from a crown-rump length (mm), Robinson–Fleming. */\nexport function crlToGestationalAgeDays(crlMm: number): number {\n // GA(days) = 8.052·√(CRL·1.037) + 23.73\n return 8.052 * Math.sqrt(crlMm * 1.037) + 23.73;\n}\n\n/**\n * ACOG CO 700 redating threshold (in days) for a given GA-by-LMP: re-date the\n * EDD to the ultrasound estimate only when the discrepancy EXCEEDS this.\n */\nexport function redatingThresholdDays(gaByLmpDays: number): number {\n if (gaByLmpDays <= day(8, 6)) return 5; // ≤ 8+6\n if (gaByLmpDays <= day(15, 6)) return 7; // 9+0 – 15+6\n if (gaByLmpDays <= day(21, 6)) return 10; // 16+0 – 21+6\n if (gaByLmpDays <= day(27, 6)) return 14; // 22+0 – 27+6\n return 21; // ≥ 28+0 (with a growth-restriction caveat the UI should note)\n}\n\nexport interface CrlRedatingInput {\n /** Reported first day of the last menstrual period. */\n lmp: Date;\n /** Date the ultrasound CRL was measured. */\n scanDate: Date;\n /** Crown-rump length, mm. */\n crlMm: number;\n}\n\nexport interface CrlRedatingResult {\n /** Notional LMP (GA day 0) after the rule — the ultrasound's or the original. */\n gestationalStart: Date;\n /** Whether the ultrasound estimate replaced the LMP dating. */\n redated: boolean;\n /** |GA-by-LMP − GA-by-CRL| at the scan, in days. */\n discrepancyDays: number;\n /** The ACOG CO 700 threshold (days) that applied. */\n thresholdDays: number;\n /** CRL − LMP dating, in days (+ = fetus ahead of dates). Server `eco_diff_days`. */\n ecoDiffDays: number;\n /** Whether the CRL is within the 45–84 mm dating-validity window. */\n crlInRange: boolean;\n}\n\n/** Apply the ACOG CO 700 CRL-redating rule to an LMP. */\nexport function redateByCrl(input: CrlRedatingInput): CrlRedatingResult {\n const gaByLmpDays = differenceInCalendarDays(input.scanDate, input.lmp);\n const gaByCrlDays = Math.round(crlToGestationalAgeDays(input.crlMm));\n const discrepancyDays = Math.abs(gaByLmpDays - gaByCrlDays);\n const thresholdDays = redatingThresholdDays(gaByLmpDays);\n const redated = discrepancyDays > thresholdDays;\n return {\n gestationalStart: redated\n ? subDays(input.scanDate, gaByCrlDays)\n : input.lmp,\n redated,\n discrepancyDays,\n thresholdDays,\n ecoDiffDays: gaByCrlDays - gaByLmpDays,\n crlInRange:\n input.crlMm >= CRL_DATING_MIN_MM && input.crlMm <= CRL_DATING_MAX_MM,\n };\n}\n","/* ------------------------------------------------------------------ */\n/* Antenatal schedule — pure, framework-free, clinically cited. */\n/* */\n/* Given a gestational start (GA day 0 = notional LMP, from */\n/* `obstetrics.ts`), resolves the time-sensitive antenatal exams and */\n/* screenings to absolute date windows with a past / current / upcoming */\n/* status. The schedule is locale-configurable because guidelines */\n/* genuinely diverge (e.g. the anomaly scan is 19+0–21+6 in Italy but */\n/* 18+0–20+6 in the UK), and conditional items (anti-D, OGTT tier, */\n/* CVS/amnio) are opt-in so a patient is never shown every test. */\n/* */\n/* Every window carries a `source` citation key so the UI can show */\n/* provenance and a clinician can audit the data. Windows are NOT */\n/* ported from the original Metricare app — its constants carry a */\n/* documented 2-week-late dating shift (the first-trimester screening */\n/* rendered after its window had closed). These are the guideline */\n/* windows, anchored to the kit's correct `computeDueDate` maths. */\n/* */\n/* Sources (citation keys): */\n/* SIEOG-2021 — SIEOG ministerial ultrasound guidelines 2021 (IT) */\n/* ISS — ISS 'Gravidanza fisiologica' / LEA (IT) */\n/* SID-AMD — SID-AMD/ISS gestational-diabetes screening (IT) */\n/* ACOG-CO700 — ACOG Committee Opinion 700 (EDD methods) */\n/* NICE-NG201 — NICE NG201 routine antenatal care (UK) */\n/* NICE-NG137 — NICE NG137 twin & triplet pregnancy (UK) */\n/* ISUOG-2025 — ISUOG twin-pregnancy ultrasound guidelines 2025 */\n/* ------------------------------------------------------------------ */\n\nimport { addDays, differenceInCalendarDays } from 'date-fns';\nimport {\n plannedBirthWindow,\n type Plurality,\n type Chorionicity,\n type BirthGuideline,\n} from './obstetrics';\n\nexport type AntenatalCategory =\n | 'scan'\n | 'screening'\n | 'diagnostic'\n | 'lab'\n | 'prophylaxis'\n | 'milestone';\n\n/** Which national schedule to resolve. Italy is the default market. */\nexport type ScheduleLocale = 'it' | 'uk' | 'international';\n\n/** Status of a window relative to the reference date (keyed off its end). */\nexport type AntenatalStatus = 'past' | 'current' | 'upcoming';\n\n/** Gestational-diabetes risk tier (drives which OGTT window applies). */\nexport type GdmRisk = 'high' | 'medium';\n\n/** A clinical condition gating whether an event applies to a pregnancy. */\nexport type AntenatalCondition =\n | 'rh-negative'\n | 'gdm-high'\n | 'gdm-medium'\n | 'on-indication';\n\nexport interface AntenatalEvent {\n /** Stable key → i18n lookup `antenatalSchedule.event.<key>`. */\n key: string;\n category: AntenatalCategory;\n /** Window start as GA days from gestationalStart (week*7 + day). Inclusive. */\n startDay: number;\n /** Window end as GA days. Inclusive. Equals `startDay` for single dates. */\n endDay: number;\n /** Restrict to these pluralities (omit ⇒ applies to all). */\n plurality?: Plurality[];\n /** Restrict to these chorionicities (omit ⇒ any). */\n chorionicity?: Chorionicity[];\n /** Gate behind a clinical condition (omit ⇒ always applies). */\n condition?: AntenatalCondition;\n /** Citation key for provenance display. */\n source: string;\n /** Interpolation values for the event's i18n string (e.g. surveillance cadence). */\n meta?: Record<string, string | number>;\n}\n\nexport interface ResolvedAntenatalEvent extends AntenatalEvent {\n /** Absolute window start date. */\n start: Date;\n /** Absolute window end date (=== `start` for a single-date event). */\n end: Date;\n /** True when the event is a single calendar date, not a range. */\n single: boolean;\n /** Status relative to the reference date, keyed off the window end. */\n status: AntenatalStatus;\n /** Reference date is within `nearDays` before the window end. */\n near: boolean;\n}\n\nexport interface AntenatalScheduleOptions {\n /** Reference date for status. Defaults to the host's current date. */\n today?: Date;\n /** National schedule. Defaults to `'it'`. */\n schedule?: ScheduleLocale;\n /** Number of fetuses. Defaults to `'singleton'`. */\n plurality?: Plurality;\n /** Placentation, for multiples. Defaults to `'unknown'`. */\n chorionicity?: Chorionicity;\n /** Surface the anti-D prophylaxis event (Rh-negative, non-sensitised). */\n rhNegative?: boolean;\n /** Surface the matching OGTT window for the patient's GDM risk tier. */\n gdmRisk?: GdmRisk;\n /** Include `on-indication` diagnostics (CVS, amnio, NIPT). Default false. */\n includeOnIndication?: boolean;\n /** Planned-birth guideline for multiples. Defaults to `'nice'`. */\n birthGuideline?: BirthGuideline;\n /** Days before a window's end still counted as `current`. Default 14. */\n nearDays?: number;\n}\n\n/** Gestational-age days from a week + day pair (e.g. `ga(13, 6)` → 97). */\nconst ga = (week: number, day = 0): number => week * 7 + day;\n\n/* ------------------------------------------------------------------ */\n/* Schedule data — one table per locale. */\n/* Common events are defined once and reused; only the windows that */\n/* genuinely diverge between locales are overridden. */\n/* ------------------------------------------------------------------ */\n\n/**\n * First-trimester dating scan + nuchal translucency + combined/bi-test.\n * NT is only measurable at CRL 45–84 mm, which is the 11+0–13+6 window;\n * the combined-test bloods (PAPP-A, free β-hCG) share it. (UK opens it a\n * touch later, 11+2–14+1, anchored to NICE's CRL band.)\n */\nconst datingNt = (locale: ScheduleLocale): AntenatalEvent =>\n locale === 'uk'\n ? {\n key: 'dating-nt',\n category: 'screening',\n startDay: ga(11, 2),\n endDay: ga(14, 1),\n source: 'NICE-NG201',\n }\n : {\n key: 'dating-nt',\n category: 'screening',\n startDay: ga(11, 0),\n endDay: ga(13, 6),\n source: 'SIEOG-2021',\n };\n\n/** NIPT / cell-free DNA — earliest 10+0; co-timed with the combined test. */\nconst nipt: AntenatalEvent = {\n key: 'nipt',\n category: 'screening',\n startDay: ga(10, 0),\n endDay: ga(13, 6),\n condition: 'on-indication',\n source: 'ACOG-CO700',\n};\n\n/** Chorionic villus sampling — never before 10–11w (limb-reduction risk). */\nconst cvs: AntenatalEvent = {\n key: 'cvs',\n category: 'diagnostic',\n startDay: ga(11, 0),\n endDay: ga(13, 6),\n condition: 'on-indication',\n source: 'ISS',\n};\n\n/** Amniocentesis — from 15+0 (typically 15–18w). */\nconst amnio: AntenatalEvent = {\n key: 'amnio',\n category: 'diagnostic',\n startDay: ga(15, 0),\n endDay: ga(18, 0),\n condition: 'on-indication',\n source: 'ISS',\n};\n\n/** Second-trimester anomaly / morphology scan — the key locale divergence. */\nconst morphology = (locale: ScheduleLocale): AntenatalEvent => {\n if (locale === 'uk') {\n return {\n key: 'morphology',\n category: 'scan',\n startDay: ga(18, 0),\n endDay: ga(20, 6),\n source: 'NICE-NG201',\n };\n }\n if (locale === 'international') {\n return {\n key: 'morphology',\n category: 'scan',\n startDay: ga(18, 0),\n endDay: ga(22, 0),\n source: 'ACOG-CO700',\n };\n }\n return {\n key: 'morphology',\n category: 'scan',\n startDay: ga(19, 0),\n endDay: ga(21, 6),\n source: 'SIEOG-2021',\n };\n};\n\n/**\n * Third-trimester growth scan — `on-indication` everywhere: not routine in\n * the Italian low-risk pathway (ISS uses symphysis-fundal height from 24w);\n * NICE/ACOG also reserve late scans for indications in low-risk pregnancies.\n */\nconst growth: AntenatalEvent = {\n key: 'growth',\n category: 'scan',\n startDay: ga(30, 0),\n endDay: ga(33, 6),\n condition: 'on-indication',\n source: 'SIEOG-2021',\n};\n\n/** OGTT 75 g, high-risk tier (BMI ≥ 30 / prior GDM / fasting 100–125). */\nconst ogttHigh: AntenatalEvent = {\n key: 'ogtt-high',\n category: 'lab',\n startDay: ga(16, 0),\n endDay: ga(18, 6),\n condition: 'gdm-high',\n source: 'SID-AMD',\n};\n\n/** OGTT 75 g, medium-risk tier (≥ 1 of: family hx, BMI ≥ 25, age ≥ 35 …). */\nconst ogttMedium: AntenatalEvent = {\n key: 'ogtt-medium',\n category: 'lab',\n startDay: ga(24, 0),\n endDay: ga(28, 6),\n condition: 'gdm-medium',\n source: 'SID-AMD',\n};\n\n/** Group-B streptococcus vagino-rectal swab. */\nconst gbs = (locale: ScheduleLocale): AntenatalEvent =>\n locale === 'it'\n ? {\n key: 'gbs',\n category: 'lab',\n startDay: ga(36, 0),\n endDay: ga(37, 6),\n source: 'ISS',\n }\n : {\n // CDC / legacy international protocols open a week earlier.\n key: 'gbs',\n category: 'lab',\n startDay: ga(35, 0),\n endDay: ga(37, 6),\n source: 'NICE-NG201',\n };\n\n/** Routine antenatal anti-D prophylaxis for non-sensitised Rh(D)-neg women. */\nconst antiD: AntenatalEvent = {\n key: 'anti-d',\n category: 'prophylaxis',\n startDay: ga(28, 0),\n endDay: ga(28, 0),\n condition: 'rh-negative',\n source: 'ISS',\n};\n\n/**\n * Term threshold (37+0) — singletons only. Multiples are delivered earlier\n * (see `plannedBirthWindow`), so a 37-week \"term\" milestone would fall after\n * their recommended birth window and read as clinically contradictory.\n */\nconst term: AntenatalEvent = {\n key: 'term',\n category: 'milestone',\n startDay: ga(37, 0),\n endDay: ga(37, 0),\n plurality: ['singleton'],\n source: 'ACOG-CO700',\n};\n\n/** Estimated due date (40+0) — singleton only; twins use a birth window. */\nconst edd: AntenatalEvent = {\n key: 'edd',\n category: 'milestone',\n startDay: ga(40, 0),\n endDay: ga(40, 0),\n plurality: ['singleton'],\n source: 'ACOG-CO700',\n};\n\n/**\n * Chorionicity + amnionicity determination — twins/triplets only, at the\n * first-trimester scan (before 13+6 / CRL 45–84 mm). If presentation is\n * later, determine at the earliest opportunity.\n */\nconst chorionicityDetermination: AntenatalEvent = {\n key: 'chorionicity',\n category: 'scan',\n startDay: ga(11, 2),\n endDay: ga(14, 1),\n plurality: ['twin', 'triplet'],\n source: 'ISUOG-2025',\n};\n\n/** Build the ordered base event list for a locale (pre-filtering). */\nfunction baseEvents(locale: ScheduleLocale): AntenatalEvent[] {\n return [\n datingNt(locale),\n nipt,\n cvs,\n chorionicityDetermination,\n amnio,\n morphology(locale),\n ogttHigh,\n growth,\n ogttMedium,\n term,\n gbs(locale),\n antiD,\n edd,\n ];\n}\n\n/**\n * Extra events for a multiple pregnancy: increased ultrasound surveillance\n * (cadence by chorionicity) and the planned-birth window (which replaces the\n * singleton EDD). Monochorionic twins are scanned every 2 weeks from 16w to\n * catch TTTS; dichorionic every 4 weeks from 20w. `unknown` chorionicity is\n * managed as monochorionic (the higher-risk default).\n */\nfunction multipleEvents(\n plurality: Plurality,\n chorionicity: Chorionicity,\n guideline: BirthGuideline,\n): AntenatalEvent[] {\n if (plurality === 'singleton') return [];\n\n const isDichorionic = chorionicity === 'dichorionic-diamniotic';\n const everyWeeks = isDichorionic ? 4 : 2;\n const fromWeek = isDichorionic ? 20 : 16;\n const birth = plannedBirthWindow(plurality, chorionicity, guideline);\n\n const events: AntenatalEvent[] = [\n {\n key: 'surveillance',\n category: 'scan',\n startDay: ga(fromWeek, 0),\n // Runs until the planned-birth window opens (fallback 36w if none).\n endDay: birth ? birth.startDay : ga(36, 0),\n source: isDichorionic ? 'NICE-NG137' : 'ISUOG-2025',\n meta: { everyWeeks, fromWeek },\n },\n ];\n\n if (birth) {\n events.push({\n key: 'birth',\n category: 'milestone',\n startDay: birth.startDay,\n endDay: birth.endDay,\n source: birth.source,\n });\n }\n\n return events;\n}\n\n/* ------------------------------------------------------------------ */\n/* Resolution */\n/* ------------------------------------------------------------------ */\n\n/** Does an event apply, given the pregnancy's plurality/chorionicity/conds? */\nfunction eventApplies(\n event: AntenatalEvent,\n opts: Required<\n Pick<AntenatalScheduleOptions, 'plurality' | 'chorionicity'>\n > & {\n rhNegative: boolean;\n gdmRisk?: GdmRisk;\n includeOnIndication: boolean;\n },\n): boolean {\n if (event.plurality && !event.plurality.includes(opts.plurality)) {\n return false;\n }\n if (event.chorionicity && !event.chorionicity.includes(opts.chorionicity)) {\n return false;\n }\n switch (event.condition) {\n case 'rh-negative':\n return opts.rhNegative;\n case 'gdm-high':\n return opts.gdmRisk === 'high';\n case 'gdm-medium':\n return opts.gdmRisk === 'medium';\n case 'on-indication':\n return opts.includeOnIndication;\n default:\n return true;\n }\n}\n\nfunction statusFor(\n start: Date,\n end: Date,\n today: Date,\n nearDays: number,\n): { status: AntenatalStatus; near: boolean } {\n const daysUntilStart = differenceInCalendarDays(start, today);\n const daysUntilEnd = differenceInCalendarDays(end, today);\n // Window already closed.\n if (daysUntilEnd < 0) return { status: 'past', near: false };\n // Open now (start ≤ today ≤ end) → current, and always worth flagging.\n if (daysUntilStart <= 0) return { status: 'current', near: true };\n // Not yet open → upcoming; \"near\" if it opens within nearDays.\n return { status: 'upcoming', near: daysUntilStart <= nearDays };\n}\n\n/**\n * Resolve the antenatal schedule for a pregnancy to dated, status-tagged\n * windows, ordered by start date (then end date). Pure: the only clock is\n * `opts.today` (defaults to a fresh `Date`).\n */\nexport function computeAntenatalSchedule(\n gestationalStart: Date,\n opts: AntenatalScheduleOptions = {},\n): ResolvedAntenatalEvent[] {\n const {\n today = new Date(),\n schedule = 'it',\n plurality = 'singleton',\n chorionicity = 'unknown',\n rhNegative = false,\n gdmRisk,\n includeOnIndication = false,\n birthGuideline = 'nice',\n nearDays = 14,\n } = opts;\n\n return [\n ...baseEvents(schedule),\n ...multipleEvents(plurality, chorionicity, birthGuideline),\n ]\n .filter((event) =>\n eventApplies(event, {\n plurality,\n chorionicity,\n rhNegative,\n gdmRisk,\n includeOnIndication,\n }),\n )\n .map((event): ResolvedAntenatalEvent => {\n const start = addDays(gestationalStart, event.startDay);\n const end = addDays(gestationalStart, event.endDay);\n const { status, near } = statusFor(start, end, today, nearDays);\n return {\n ...event,\n start,\n end,\n single: event.startDay === event.endDay,\n status,\n near,\n };\n })\n .sort((a, b) => {\n const byStart = a.startDay - b.startDay;\n return byStart !== 0 ? byStart : a.endDay - b.endDay;\n });\n}\n","/* ------------------------------------------------------------------ */\n/* AntenatalScheduleTimeline — the presentational list of resolved */\n/* antenatal windows, shared by the `showSchedule` appendix on */\n/* GestationalAgeCalculator and DueDateCalculator. Pure display: it takes */\n/* already-resolved events and renders the Card + the sr-only now-due */\n/* summary. No inputs, no state, no maths. */\n/* ------------------------------------------------------------------ */\n\nimport { useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Card } from '../card';\nimport { Badge } from '../badge';\nimport type { IconKey, InsertCardField } from './insert-result';\nimport type {\n ResolvedAntenatalEvent,\n AntenatalStatus,\n AntenatalCategory,\n} from './antenatal-schedule';\n\n/** Status → on-screen Badge variant. `near` upcoming items borrow `warning`. */\nfunction statusBadgeVariant(\n status: AntenatalStatus,\n near: boolean,\n): 'neutral' | 'info' | 'warning' {\n if (status === 'past') return 'neutral';\n if (status === 'current') return 'warning';\n return near ? 'warning' : 'info';\n}\n\n/** Compact gestational-age label for a day offset, e.g. 97 → \"13+6\". */\nfunction gaLabel(days: number): string {\n return `${Math.floor(days / 7)}+${days % 7}`;\n}\n\n/** Each schedule category maps to one of the inserted-card glyph keys. */\nconst CATEGORY_ICON: Record<AntenatalCategory, IconKey> = {\n scan: 'activity',\n screening: 'activity',\n diagnostic: 'activity',\n lab: 'droplets',\n prophylaxis: 'heart',\n milestone: 'calendar',\n};\n\n/** Minimal translator shape this module needs (matches react-i18next's `t`). */\ntype Translate = (key: string, opts?: Record<string, unknown>) => string;\n\n/** Localised \"label · range\" for one event (e.g. for the sr-only summary). */\nfunction eventLabelFor(event: ResolvedAntenatalEvent, t: Translate): string {\n return t(`antenatalSchedule.event.${event.key}`, event.meta ?? {});\n}\n\n/**\n * Build the inserted-card rows for a resolved schedule, so a host calculator's\n * single Insert button can carry the schedule in its payload (rather than a\n * second Insert button on the schedule card). One row per event: a\n * category-tinted glyph, the localised label, and the date range.\n */\nexport function buildScheduleInsertFields(\n events: ResolvedAntenatalEvent[],\n t: Translate,\n formatDate: (date: Date) => string,\n): InsertCardField[] {\n return events.map((event) => ({\n icon: CATEGORY_ICON[event.category],\n label: eventLabelFor(event, t),\n value: event.single\n ? formatDate(event.start)\n : `${formatDate(event.start)} – ${formatDate(event.end)}`,\n }));\n}\n\nexport interface AntenatalScheduleTimelineProps {\n /** Resolved schedule events (from `computeAntenatalSchedule`). */\n events: ResolvedAntenatalEvent[];\n}\n\nexport function AntenatalScheduleTimeline({\n events,\n}: AntenatalScheduleTimelineProps) {\n const { t, i18n } = useTranslation();\n\n const dateFormatter = useMemo(\n () => new Intl.DateTimeFormat(i18n.language, { dateStyle: 'medium' }),\n [i18n.language],\n );\n\n const eventLabel = (event: ResolvedAntenatalEvent): string =>\n eventLabelFor(event, t);\n\n const formatRange = (event: ResolvedAntenatalEvent): string =>\n event.single\n ? dateFormatter.format(event.start)\n : `${dateFormatter.format(event.start)} – ${dateFormatter.format(\n event.end,\n )}`;\n\n // No own live region: this timeline is always embedded in a host calculator\n // that already owns a `role=\"status\"` result announcement, so a second one\n // here would double-announce. The schedule is fully present in the DOM\n // (labelled list + per-row status Badge) for assistive tech to browse.\n return (\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('antenatalSchedule.scheduleLabel')}\n </span>\n <ul className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {events.map((event) => (\n <li\n key={event.key}\n data-status={event.status}\n className=\"ds:flex ds:flex-wrap ds:items-start ds:justify-between ds:gap-x-[var(--spacing-md)] ds:gap-y-[var(--spacing-xs)] ds:border-b ds:border-border ds:pb-[var(--spacing-sm)] ds:last:border-b-0 ds:last:pb-0\"\n >\n <span className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-2xs)]\">\n <span className=\"type-body ds:text-foreground\">\n {eventLabel(event)}\n </span>\n <span className=\"type-meta ds:text-muted-foreground\">\n {t(`antenatalSchedule.category.${event.category}`)} ·{' '}\n {gaLabel(event.startDay)}\n {event.single ? '' : ` – ${gaLabel(event.endDay)}`} ·{' '}\n {t(`antenatalSchedule.source.${event.source}`)}\n {event.condition === 'on-indication'\n ? ` · ${t('antenatalSchedule.onIndication')}`\n : ''}\n </span>\n </span>\n <span className=\"ds:flex ds:flex-col ds:items-end ds:gap-[var(--spacing-2xs)] ds:text-end\">\n <span className=\"type-body ds:text-foreground\">\n {formatRange(event)}\n </span>\n <Badge variant={statusBadgeVariant(event.status, event.near)}>\n {event.status === 'upcoming' && event.near\n ? t('antenatalSchedule.near')\n : t(`antenatalSchedule.status.${event.status}`)}\n </Badge>\n </span>\n </li>\n ))}\n </ul>\n <p className=\"type-meta ds:text-muted-foreground\">\n {t('antenatalSchedule.disclaimer')}\n </p>\n </Card.Body>\n </Card>\n );\n}\n"],"names":["GESTATION_DAYS","CONCEPTION_OFFSET_DAYS","TERM_FROM_CONCEPTION_DAYS","DEFAULT_CYCLE_LENGTH","trimesterForWeeks","totalDays","weeks","computeDueDate","input","today","method","date","gestationalStart","dueDate","cycle","addDays","subDays","embryo","conceptionDate","differenceInCalendarDays","gestationalAge","MILESTONE_WEEKS","gestationalMilestones","week","TRIMESTER_BADGE_VARIANT","TRIMESTER_HIGHLIGHT_TOKEN","trimesterHighlightToken","trimester","day","d","plannedBirthWindow","plurality","chorionicity","guideline","effective","CRL_DATING_MIN_MM","CRL_DATING_MAX_MM","crlToGestationalAgeDays","crlMm","redatingThresholdDays","gaByLmpDays","redateByCrl","gaByCrlDays","discrepancyDays","thresholdDays","redated","ga","datingNt","locale","nipt","cvs","amnio","morphology","growth","ogttHigh","ogttMedium","gbs","antiD","term","edd","chorionicityDetermination","baseEvents","multipleEvents","isDichorionic","everyWeeks","fromWeek","birth","events","eventApplies","event","opts","statusFor","start","end","nearDays","daysUntilStart","computeAntenatalSchedule","schedule","rhNegative","gdmRisk","includeOnIndication","birthGuideline","status","near","a","b","byStart","statusBadgeVariant","gaLabel","days","CATEGORY_ICON","eventLabelFor","t","buildScheduleInsertFields","formatDate","AntenatalScheduleTimeline","i18n","useTranslation","dateFormatter","useMemo","eventLabel","formatRange","jsx","Card","jsxs","Badge"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"antenatal-schedule-timeline-UiZQze4P.js","sources":["../../src/components/_shared/obstetrics.ts","../../src/components/_shared/antenatal-schedule.ts","../../src/components/_shared/antenatal-schedule-timeline.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Obstetric maths — pure, framework-free, unit-testable. */\n/* */\n/* Estimated-due-date dating, gestational age, trimester classification */\n/* and milestone projection, plus the trimester→presentation maps the */\n/* obstetric calculators share. All three dating methods are normalised */\n/* onto a single \"gestational start\" (the notional first day of the last */\n/* menstrual period, GA day 0) so gestational age and trimester are */\n/* computed identically regardless of how the pregnancy was dated. */\n/* */\n/* Home for cross-component obstetric logic: the due-date and */\n/* gestational-age calculators both consume this module, so it lives in */\n/* `_shared/` rather than inside a single component directory. */\n/* `due-date-calculator/gestation.ts` re-exports this file for */\n/* back-compatibility with the public package barrel. */\n/* ------------------------------------------------------------------ */\n\nimport { addDays, subDays, differenceInCalendarDays } from 'date-fns';\n\nexport type DueDateMethod = 'lmp' | 'conception' | 'ivf';\n\n/** Embryo age at transfer, in days (cleavage vs blastocyst). */\nexport type EmbryoAge = 3 | 5;\n\nexport type Trimester =\n | 'preconception'\n | 'first'\n | 'second'\n | 'third'\n | 'postterm';\n\n/** Number of fetuses. Drives surveillance schedule and planned-birth timing. */\nexport type Plurality = 'singleton' | 'twin' | 'triplet';\n\n/**\n * Placentation of a multiple pregnancy. `unknown` is the safe default — managed\n * as monochorionic (the higher-risk assumption) until proven otherwise, per\n * NICE QS46. Only meaningful when `plurality !== 'singleton'`.\n */\nexport type Chorionicity =\n | 'dichorionic-diamniotic'\n | 'monochorionic-diamniotic'\n | 'monochorionic-monoamniotic'\n | 'unknown';\n\n/** Naegele's rule: a term pregnancy is 280 days from the LMP. */\nexport const GESTATION_DAYS = 280;\n/** Ovulation/conception sits ~14 days after the LMP in a 28-day cycle. */\nexport const CONCEPTION_OFFSET_DAYS = 14;\n/** A term pregnancy is 266 days from conception. */\nexport const TERM_FROM_CONCEPTION_DAYS = 266;\n/** Standard menstrual-cycle length Naegele assumes. */\nexport const DEFAULT_CYCLE_LENGTH = 28;\n\nexport interface DueDateInput {\n method: DueDateMethod;\n /** The reference date for the chosen method (LMP / conception / transfer). */\n date: Date;\n /** LMP only — average cycle length in days. Defaults to 28. */\n cycleLength?: number;\n /** IVF only — embryo age at transfer in days. Defaults to 5 (blastocyst). */\n embryoAge?: EmbryoAge;\n}\n\nexport interface GestationalAge {\n weeks: number;\n days: number;\n totalDays: number;\n}\n\nexport interface DueDateResult {\n /** Estimated date of delivery. */\n dueDate: Date;\n /** GA day-0 anchor (notional LMP) the trimester maths counts from. */\n gestationalStart: Date;\n /** Gestational age as of `today`; `null` before conception. */\n gestationalAge: GestationalAge | null;\n trimester: Trimester;\n}\n\n/** Classify gestational age (in completed weeks) into a trimester. */\nexport function trimesterForWeeks(totalDays: number): Trimester {\n if (totalDays < 0) return 'preconception';\n const weeks = Math.floor(totalDays / 7);\n if (weeks < 14) return 'first';\n if (weeks < 28) return 'second';\n if (weeks < 42) return 'third';\n return 'postterm';\n}\n\n/**\n * Resolve the gestational start (notional LMP) and due date for a method.\n * `cycleLength` shifts a longer/shorter cycle's ovulation, moving the due\n * date while GA stays counted from the LMP — the standard convention.\n */\nexport function computeDueDate(\n input: DueDateInput,\n today: Date,\n): DueDateResult {\n const { method, date } = input;\n\n let gestationalStart: Date;\n let dueDate: Date;\n\n if (method === 'lmp') {\n const cycle = input.cycleLength ?? DEFAULT_CYCLE_LENGTH;\n gestationalStart = date;\n dueDate = addDays(date, GESTATION_DAYS + (cycle - DEFAULT_CYCLE_LENGTH));\n } else if (method === 'conception') {\n gestationalStart = subDays(date, CONCEPTION_OFFSET_DAYS);\n dueDate = addDays(date, TERM_FROM_CONCEPTION_DAYS);\n } else {\n // IVF: back out the conception (egg-retrieval) date from the transfer.\n const embryo = input.embryoAge ?? 5;\n const conceptionDate = subDays(date, embryo);\n gestationalStart = subDays(conceptionDate, CONCEPTION_OFFSET_DAYS);\n dueDate = addDays(conceptionDate, TERM_FROM_CONCEPTION_DAYS);\n }\n\n const totalDays = differenceInCalendarDays(today, gestationalStart);\n const gestationalAge: GestationalAge | null =\n totalDays < 0\n ? null\n : {\n weeks: Math.floor(totalDays / 7),\n days: totalDays % 7,\n totalDays,\n };\n\n return {\n dueDate,\n gestationalStart,\n gestationalAge,\n trimester: trimesterForWeeks(totalDays),\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Milestones */\n/* ------------------------------------------------------------------ */\n\n/** Gestational weeks at which clinically notable milestones fall. */\nexport const MILESTONE_WEEKS = [12, 24, 28, 37, 40] as const;\n\nexport interface GestationalMilestone {\n /** Stable key (`w12`, `w24`, …) for i18n lookup. */\n key: string;\n /** Completed gestational weeks. */\n week: number;\n /** Calendar date the milestone is reached. */\n date: Date;\n}\n\n/**\n * Project the standard milestone dates from a gestational start (notional\n * LMP / GA day 0): end of first trimester (12w), viability (24w), third\n * trimester (28w), term (37w) and the 40-week due date.\n */\nexport function gestationalMilestones(\n gestationalStart: Date,\n): GestationalMilestone[] {\n return MILESTONE_WEEKS.map((week) => ({\n key: `w${week}`,\n week,\n date: addDays(gestationalStart, week * 7),\n }));\n}\n\n/* ------------------------------------------------------------------ */\n/* Trimester presentation */\n/* */\n/* Shared by DueDateCalculator and GestationalAgeCalculator so the */\n/* on-screen badge and the inserted result-card chip stay in lockstep. */\n/* These are plain string maps (no React/Badge import) — the same */\n/* pattern as `_shared/entity-card`'s state→badge maps. */\n/* ------------------------------------------------------------------ */\n\n/** Badge `variant` names used for trimesters (the Badge component's union). */\nexport type TrimesterBadgeVariant =\n | 'neutral'\n | 'info'\n | 'success'\n | 'warning'\n | 'error';\n\n/** Trimester → on-screen Badge variant. */\nexport const TRIMESTER_BADGE_VARIANT: Record<Trimester, TrimesterBadgeVariant> =\n {\n preconception: 'neutral',\n first: 'info',\n second: 'success',\n third: 'warning',\n postterm: 'error',\n };\n\n/**\n * DS token NAME backing each trimester's result-card chip — the solid-fill\n * companion of `TRIMESTER_BADGE_VARIANT` (Badge `error` fills `--destructive`;\n * `neutral` fills `--muted`). Passed as the card's `highlightToken` so the\n * inserted PNG chip matches the on-screen trimester badge; `InsertButton`\n * resolves the name to a concrete colour at raster time.\n *\n * `third` resolves to `--warning-readable`, which the result card's colour\n * probe resolves in the live themed DOM: orange-600 in light, non-accessible\n * mode (where bare `--warning` yellow only reaches ~3.2:1 on the white card)\n * and `--warning` elsewhere, whose deepened ramp already clears contrast.\n * Resolving the token NAME at the card means the choice tracks the live theme\n * regardless of where the theme class lives.\n */\nexport const TRIMESTER_HIGHLIGHT_TOKEN: Record<Trimester, string> = {\n preconception: '--muted',\n first: '--info',\n second: '--success',\n third: '--warning-readable',\n postterm: '--destructive',\n};\n\n/** The DS token name for a trimester's result-card chip. */\nexport function trimesterHighlightToken(trimester: Trimester): string {\n return TRIMESTER_HIGHLIGHT_TOKEN[trimester];\n}\n\n/* ------------------------------------------------------------------ */\n/* Multiple pregnancy — planned-birth timing */\n/* */\n/* For multiples the \"due date\" is not a single 40-week point but a */\n/* recommended planned-birth window that depends on chorionicity. The */\n/* UK (NICE NG137) and US (ACOG PB231) bodies diverge by ~1 week for */\n/* diamniotic twins, so the guideline is selectable; Italian practice */\n/* aligns closer to the earlier NICE stance, so `nice` is the default. */\n/* ------------------------------------------------------------------ */\n\n/** Which body's planned-birth timing to use for multiples. */\nexport type BirthGuideline = 'nice' | 'acog';\n\n/** Planned-birth window as GA days (week*7 + day), inclusive, with source. */\nexport interface BirthWindow {\n startDay: number;\n endDay: number;\n source: string;\n}\n\nconst day = (week: number, d = 0): number => week * 7 + d;\n\n/**\n * Recommended planned-birth window for a multiple pregnancy, by chorionicity.\n * Singletons return `null` (use the 40-week EDD instead). `unknown`\n * chorionicity is managed as monochorionic-diamniotic — the higher-risk\n * default (NICE QS46).\n */\nexport function plannedBirthWindow(\n plurality: Plurality,\n chorionicity: Chorionicity = 'unknown',\n guideline: BirthGuideline = 'nice',\n): BirthWindow | null {\n if (plurality === 'singleton') return null;\n\n if (plurality === 'triplet') {\n // NICE/ACOG: offer planned birth for uncomplicated triplets at ~35 weeks.\n return { startDay: day(35, 0), endDay: day(35, 6), source: 'NICE-NG137' };\n }\n\n // Twins. `unknown` is managed as monochorionic-diamniotic.\n const effective: Chorionicity =\n chorionicity === 'unknown' ? 'monochorionic-diamniotic' : chorionicity;\n\n if (guideline === 'acog') {\n switch (effective) {\n case 'dichorionic-diamniotic':\n return {\n startDay: day(38, 0),\n endDay: day(38, 6),\n source: 'ACOG-PB231',\n };\n case 'monochorionic-diamniotic':\n return {\n startDay: day(36, 0),\n endDay: day(37, 6),\n source: 'ACOG-PB231',\n };\n case 'monochorionic-monoamniotic':\n return {\n startDay: day(32, 0),\n endDay: day(34, 0),\n source: 'ACOG-PB231',\n };\n }\n }\n\n // NICE NG137 (default).\n switch (effective) {\n case 'dichorionic-diamniotic':\n return { startDay: day(37, 0), endDay: day(37, 6), source: 'NICE-NG137' };\n case 'monochorionic-diamniotic':\n return { startDay: day(36, 0), endDay: day(36, 6), source: 'NICE-NG137' };\n case 'monochorionic-monoamniotic':\n return { startDay: day(32, 0), endDay: day(33, 6), source: 'NICE-NG137' };\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* Ultrasound (CRL) redating — ACOG Committee Opinion 700 */\n/* */\n/* A first-trimester crown-rump length gives a gestational age via */\n/* Robinson–Fleming. When it disagrees with the LMP by more than the */\n/* ACOG CO 700 threshold (which widens with gestation), the ultrasound */\n/* estimate replaces the LMP dating. Below threshold, the LMP stands. */\n/* The returned `ecoDiffDays` mirrors the server-side `eco_diff_days` */\n/* offset so the kit and the editor-actions backend can be reconciled. */\n/* ------------------------------------------------------------------ */\n\n/** Robinson–Fleming dating is validated for CRL 45–84 mm. */\nexport const CRL_DATING_MIN_MM = 45;\nexport const CRL_DATING_MAX_MM = 84;\n\n/** Gestational age in days from a crown-rump length (mm), Robinson–Fleming. */\nexport function crlToGestationalAgeDays(crlMm: number): number {\n // GA(days) = 8.052·√(CRL·1.037) + 23.73\n return 8.052 * Math.sqrt(crlMm * 1.037) + 23.73;\n}\n\n/**\n * ACOG CO 700 redating threshold (in days) for a given GA-by-LMP: re-date the\n * EDD to the ultrasound estimate only when the discrepancy EXCEEDS this.\n */\nexport function redatingThresholdDays(gaByLmpDays: number): number {\n if (gaByLmpDays <= day(8, 6)) return 5; // ≤ 8+6\n if (gaByLmpDays <= day(15, 6)) return 7; // 9+0 – 15+6\n if (gaByLmpDays <= day(21, 6)) return 10; // 16+0 – 21+6\n if (gaByLmpDays <= day(27, 6)) return 14; // 22+0 – 27+6\n return 21; // ≥ 28+0 (with a growth-restriction caveat the UI should note)\n}\n\nexport interface CrlRedatingInput {\n /** Reported first day of the last menstrual period. */\n lmp: Date;\n /** Date the ultrasound CRL was measured. */\n scanDate: Date;\n /** Crown-rump length, mm. */\n crlMm: number;\n}\n\nexport interface CrlRedatingResult {\n /** Notional LMP (GA day 0) after the rule — the ultrasound's or the original. */\n gestationalStart: Date;\n /** Whether the ultrasound estimate replaced the LMP dating. */\n redated: boolean;\n /** |GA-by-LMP − GA-by-CRL| at the scan, in days. */\n discrepancyDays: number;\n /** The ACOG CO 700 threshold (days) that applied. */\n thresholdDays: number;\n /** CRL − LMP dating, in days (+ = fetus ahead of dates). Server `eco_diff_days`. */\n ecoDiffDays: number;\n /** Whether the CRL is within the 45–84 mm dating-validity window. */\n crlInRange: boolean;\n}\n\n/** Apply the ACOG CO 700 CRL-redating rule to an LMP. */\nexport function redateByCrl(input: CrlRedatingInput): CrlRedatingResult {\n const gaByLmpDays = differenceInCalendarDays(input.scanDate, input.lmp);\n const gaByCrlDays = Math.round(crlToGestationalAgeDays(input.crlMm));\n const discrepancyDays = Math.abs(gaByLmpDays - gaByCrlDays);\n const thresholdDays = redatingThresholdDays(gaByLmpDays);\n const redated = discrepancyDays > thresholdDays;\n return {\n gestationalStart: redated\n ? subDays(input.scanDate, gaByCrlDays)\n : input.lmp,\n redated,\n discrepancyDays,\n thresholdDays,\n ecoDiffDays: gaByCrlDays - gaByLmpDays,\n crlInRange:\n input.crlMm >= CRL_DATING_MIN_MM && input.crlMm <= CRL_DATING_MAX_MM,\n };\n}\n","/* ------------------------------------------------------------------ */\n/* Antenatal schedule — pure, framework-free, clinically cited. */\n/* */\n/* Given a gestational start (GA day 0 = notional LMP, from */\n/* `obstetrics.ts`), resolves the time-sensitive antenatal exams and */\n/* screenings to absolute date windows with a past / current / upcoming */\n/* status. The schedule is locale-configurable because guidelines */\n/* genuinely diverge (e.g. the anomaly scan is 19+0–21+6 in Italy but */\n/* 18+0–20+6 in the UK), and conditional items (anti-D, OGTT tier, */\n/* CVS/amnio) are opt-in so a patient is never shown every test. */\n/* */\n/* Every window carries a `source` citation key so the UI can show */\n/* provenance and a clinician can audit the data. Windows are NOT */\n/* ported from the original Metricare app — its constants carry a */\n/* documented 2-week-late dating shift (the first-trimester screening */\n/* rendered after its window had closed). These are the guideline */\n/* windows, anchored to the kit's correct `computeDueDate` maths. */\n/* */\n/* Sources (citation keys): */\n/* SIEOG-2021 — SIEOG ministerial ultrasound guidelines 2021 (IT) */\n/* ISS — ISS 'Gravidanza fisiologica' / LEA (IT) */\n/* SID-AMD — SID-AMD/ISS gestational-diabetes screening (IT) */\n/* ACOG-CO700 — ACOG Committee Opinion 700 (EDD methods) */\n/* NICE-NG201 — NICE NG201 routine antenatal care (UK) */\n/* NICE-NG137 — NICE NG137 twin & triplet pregnancy (UK) */\n/* ISUOG-2025 — ISUOG twin-pregnancy ultrasound guidelines 2025 */\n/* ------------------------------------------------------------------ */\n\nimport { addDays, differenceInCalendarDays } from 'date-fns';\nimport {\n plannedBirthWindow,\n type Plurality,\n type Chorionicity,\n type BirthGuideline,\n} from './obstetrics';\n\nexport type AntenatalCategory =\n | 'scan'\n | 'screening'\n | 'diagnostic'\n | 'lab'\n | 'prophylaxis'\n | 'milestone';\n\n/** Which national schedule to resolve. Italy is the default market. */\nexport type ScheduleLocale = 'it' | 'uk' | 'international';\n\n/** Status of a window relative to the reference date (keyed off its end). */\nexport type AntenatalStatus = 'past' | 'current' | 'upcoming';\n\n/** Gestational-diabetes risk tier (drives which OGTT window applies). */\nexport type GdmRisk = 'high' | 'medium';\n\n/** A clinical condition gating whether an event applies to a pregnancy. */\nexport type AntenatalCondition =\n | 'rh-negative'\n | 'gdm-high'\n | 'gdm-medium'\n | 'on-indication';\n\nexport interface AntenatalEvent {\n /** Stable key → i18n lookup `antenatalSchedule.event.<key>`. */\n key: string;\n category: AntenatalCategory;\n /** Window start as GA days from gestationalStart (week*7 + day). Inclusive. */\n startDay: number;\n /** Window end as GA days. Inclusive. Equals `startDay` for single dates. */\n endDay: number;\n /** Restrict to these pluralities (omit ⇒ applies to all). */\n plurality?: Plurality[];\n /** Restrict to these chorionicities (omit ⇒ any). */\n chorionicity?: Chorionicity[];\n /** Gate behind a clinical condition (omit ⇒ always applies). */\n condition?: AntenatalCondition;\n /** Citation key for provenance display. */\n source: string;\n /** Interpolation values for the event's i18n string (e.g. surveillance cadence). */\n meta?: Record<string, string | number>;\n}\n\nexport interface ResolvedAntenatalEvent extends AntenatalEvent {\n /** Absolute window start date. */\n start: Date;\n /** Absolute window end date (=== `start` for a single-date event). */\n end: Date;\n /** True when the event is a single calendar date, not a range. */\n single: boolean;\n /** Status relative to the reference date, keyed off the window end. */\n status: AntenatalStatus;\n /** Reference date is within `nearDays` before the window end. */\n near: boolean;\n}\n\nexport interface AntenatalScheduleOptions {\n /** Reference date for status. Defaults to the host's current date. */\n today?: Date;\n /** National schedule. Defaults to `'it'`. */\n schedule?: ScheduleLocale;\n /** Number of fetuses. Defaults to `'singleton'`. */\n plurality?: Plurality;\n /** Placentation, for multiples. Defaults to `'unknown'`. */\n chorionicity?: Chorionicity;\n /** Surface the anti-D prophylaxis event (Rh-negative, non-sensitised). */\n rhNegative?: boolean;\n /** Surface the matching OGTT window for the patient's GDM risk tier. */\n gdmRisk?: GdmRisk;\n /** Include `on-indication` diagnostics (CVS, amnio, NIPT). Default false. */\n includeOnIndication?: boolean;\n /** Planned-birth guideline for multiples. Defaults to `'nice'`. */\n birthGuideline?: BirthGuideline;\n /** Days before a window's end still counted as `current`. Default 14. */\n nearDays?: number;\n}\n\n/** Gestational-age days from a week + day pair (e.g. `ga(13, 6)` → 97). */\nconst ga = (week: number, day = 0): number => week * 7 + day;\n\n/* ------------------------------------------------------------------ */\n/* Schedule data — one table per locale. */\n/* Common events are defined once and reused; only the windows that */\n/* genuinely diverge between locales are overridden. */\n/* ------------------------------------------------------------------ */\n\n/**\n * First-trimester dating scan + nuchal translucency + combined/bi-test.\n * NT is only measurable at CRL 45–84 mm, which is the 11+0–13+6 window;\n * the combined-test bloods (PAPP-A, free β-hCG) share it. (UK opens it a\n * touch later, 11+2–14+1, anchored to NICE's CRL band.)\n */\nconst datingNt = (locale: ScheduleLocale): AntenatalEvent =>\n locale === 'uk'\n ? {\n key: 'dating-nt',\n category: 'screening',\n startDay: ga(11, 2),\n endDay: ga(14, 1),\n source: 'NICE-NG201',\n }\n : {\n key: 'dating-nt',\n category: 'screening',\n startDay: ga(11, 0),\n endDay: ga(13, 6),\n source: 'SIEOG-2021',\n };\n\n/** NIPT / cell-free DNA — earliest 10+0; co-timed with the combined test. */\nconst nipt: AntenatalEvent = {\n key: 'nipt',\n category: 'screening',\n startDay: ga(10, 0),\n endDay: ga(13, 6),\n condition: 'on-indication',\n source: 'ACOG-CO700',\n};\n\n/** Chorionic villus sampling — never before 10–11w (limb-reduction risk). */\nconst cvs: AntenatalEvent = {\n key: 'cvs',\n category: 'diagnostic',\n startDay: ga(11, 0),\n endDay: ga(13, 6),\n condition: 'on-indication',\n source: 'ISS',\n};\n\n/** Amniocentesis — from 15+0 (typically 15–18w). */\nconst amnio: AntenatalEvent = {\n key: 'amnio',\n category: 'diagnostic',\n startDay: ga(15, 0),\n endDay: ga(18, 0),\n condition: 'on-indication',\n source: 'ISS',\n};\n\n/** Second-trimester anomaly / morphology scan — the key locale divergence. */\nconst morphology = (locale: ScheduleLocale): AntenatalEvent => {\n if (locale === 'uk') {\n return {\n key: 'morphology',\n category: 'scan',\n startDay: ga(18, 0),\n endDay: ga(20, 6),\n source: 'NICE-NG201',\n };\n }\n if (locale === 'international') {\n return {\n key: 'morphology',\n category: 'scan',\n startDay: ga(18, 0),\n endDay: ga(22, 0),\n source: 'ACOG-CO700',\n };\n }\n return {\n key: 'morphology',\n category: 'scan',\n startDay: ga(19, 0),\n endDay: ga(21, 6),\n source: 'SIEOG-2021',\n };\n};\n\n/**\n * Third-trimester growth scan — `on-indication` everywhere: not routine in\n * the Italian low-risk pathway (ISS uses symphysis-fundal height from 24w);\n * NICE/ACOG also reserve late scans for indications in low-risk pregnancies.\n */\nconst growth: AntenatalEvent = {\n key: 'growth',\n category: 'scan',\n startDay: ga(30, 0),\n endDay: ga(33, 6),\n condition: 'on-indication',\n source: 'SIEOG-2021',\n};\n\n/** OGTT 75 g, high-risk tier (BMI ≥ 30 / prior GDM / fasting 100–125). */\nconst ogttHigh: AntenatalEvent = {\n key: 'ogtt-high',\n category: 'lab',\n startDay: ga(16, 0),\n endDay: ga(18, 6),\n condition: 'gdm-high',\n source: 'SID-AMD',\n};\n\n/** OGTT 75 g, medium-risk tier (≥ 1 of: family hx, BMI ≥ 25, age ≥ 35 …). */\nconst ogttMedium: AntenatalEvent = {\n key: 'ogtt-medium',\n category: 'lab',\n startDay: ga(24, 0),\n endDay: ga(28, 6),\n condition: 'gdm-medium',\n source: 'SID-AMD',\n};\n\n/** Group-B streptococcus vagino-rectal swab. */\nconst gbs = (locale: ScheduleLocale): AntenatalEvent =>\n locale === 'it'\n ? {\n key: 'gbs',\n category: 'lab',\n startDay: ga(36, 0),\n endDay: ga(37, 6),\n source: 'ISS',\n }\n : {\n // CDC / legacy international protocols open a week earlier.\n key: 'gbs',\n category: 'lab',\n startDay: ga(35, 0),\n endDay: ga(37, 6),\n source: 'NICE-NG201',\n };\n\n/** Routine antenatal anti-D prophylaxis for non-sensitised Rh(D)-neg women. */\nconst antiD: AntenatalEvent = {\n key: 'anti-d',\n category: 'prophylaxis',\n startDay: ga(28, 0),\n endDay: ga(28, 0),\n condition: 'rh-negative',\n source: 'ISS',\n};\n\n/**\n * Term threshold (37+0) — singletons only. Multiples are delivered earlier\n * (see `plannedBirthWindow`), so a 37-week \"term\" milestone would fall after\n * their recommended birth window and read as clinically contradictory.\n */\nconst term: AntenatalEvent = {\n key: 'term',\n category: 'milestone',\n startDay: ga(37, 0),\n endDay: ga(37, 0),\n plurality: ['singleton'],\n source: 'ACOG-CO700',\n};\n\n/** Estimated due date (40+0) — singleton only; twins use a birth window. */\nconst edd: AntenatalEvent = {\n key: 'edd',\n category: 'milestone',\n startDay: ga(40, 0),\n endDay: ga(40, 0),\n plurality: ['singleton'],\n source: 'ACOG-CO700',\n};\n\n/**\n * Chorionicity + amnionicity determination — twins/triplets only, at the\n * first-trimester scan (before 13+6 / CRL 45–84 mm). If presentation is\n * later, determine at the earliest opportunity.\n */\nconst chorionicityDetermination: AntenatalEvent = {\n key: 'chorionicity',\n category: 'scan',\n startDay: ga(11, 2),\n endDay: ga(14, 1),\n plurality: ['twin', 'triplet'],\n source: 'ISUOG-2025',\n};\n\n/** Build the ordered base event list for a locale (pre-filtering). */\nfunction baseEvents(locale: ScheduleLocale): AntenatalEvent[] {\n return [\n datingNt(locale),\n nipt,\n cvs,\n chorionicityDetermination,\n amnio,\n morphology(locale),\n ogttHigh,\n growth,\n ogttMedium,\n term,\n gbs(locale),\n antiD,\n edd,\n ];\n}\n\n/**\n * Extra events for a multiple pregnancy: increased ultrasound surveillance\n * (cadence by chorionicity) and the planned-birth window (which replaces the\n * singleton EDD). Monochorionic twins are scanned every 2 weeks from 16w to\n * catch TTTS; dichorionic every 4 weeks from 20w. `unknown` chorionicity is\n * managed as monochorionic (the higher-risk default).\n */\nfunction multipleEvents(\n plurality: Plurality,\n chorionicity: Chorionicity,\n guideline: BirthGuideline,\n): AntenatalEvent[] {\n if (plurality === 'singleton') return [];\n\n const isDichorionic = chorionicity === 'dichorionic-diamniotic';\n const everyWeeks = isDichorionic ? 4 : 2;\n const fromWeek = isDichorionic ? 20 : 16;\n const birth = plannedBirthWindow(plurality, chorionicity, guideline);\n\n const events: AntenatalEvent[] = [\n {\n key: 'surveillance',\n category: 'scan',\n startDay: ga(fromWeek, 0),\n // Runs until the planned-birth window opens (fallback 36w if none).\n endDay: birth ? birth.startDay : ga(36, 0),\n source: isDichorionic ? 'NICE-NG137' : 'ISUOG-2025',\n meta: { everyWeeks, fromWeek },\n },\n ];\n\n if (birth) {\n events.push({\n key: 'birth',\n category: 'milestone',\n startDay: birth.startDay,\n endDay: birth.endDay,\n source: birth.source,\n });\n }\n\n return events;\n}\n\n/* ------------------------------------------------------------------ */\n/* Resolution */\n/* ------------------------------------------------------------------ */\n\n/** Does an event apply, given the pregnancy's plurality/chorionicity/conds? */\nfunction eventApplies(\n event: AntenatalEvent,\n opts: Required<\n Pick<AntenatalScheduleOptions, 'plurality' | 'chorionicity'>\n > & {\n rhNegative: boolean;\n gdmRisk?: GdmRisk;\n includeOnIndication: boolean;\n },\n): boolean {\n if (event.plurality && !event.plurality.includes(opts.plurality)) {\n return false;\n }\n if (event.chorionicity && !event.chorionicity.includes(opts.chorionicity)) {\n return false;\n }\n switch (event.condition) {\n case 'rh-negative':\n return opts.rhNegative;\n case 'gdm-high':\n return opts.gdmRisk === 'high';\n case 'gdm-medium':\n return opts.gdmRisk === 'medium';\n case 'on-indication':\n return opts.includeOnIndication;\n default:\n return true;\n }\n}\n\nfunction statusFor(\n start: Date,\n end: Date,\n today: Date,\n nearDays: number,\n): { status: AntenatalStatus; near: boolean } {\n const daysUntilStart = differenceInCalendarDays(start, today);\n const daysUntilEnd = differenceInCalendarDays(end, today);\n // Window already closed.\n if (daysUntilEnd < 0) return { status: 'past', near: false };\n // Open now (start ≤ today ≤ end) → current, and always worth flagging.\n if (daysUntilStart <= 0) return { status: 'current', near: true };\n // Not yet open → upcoming; \"near\" if it opens within nearDays.\n return { status: 'upcoming', near: daysUntilStart <= nearDays };\n}\n\n/**\n * Resolve the antenatal schedule for a pregnancy to dated, status-tagged\n * windows, ordered by start date (then end date). Pure: the only clock is\n * `opts.today` (defaults to a fresh `Date`).\n */\nexport function computeAntenatalSchedule(\n gestationalStart: Date,\n opts: AntenatalScheduleOptions = {},\n): ResolvedAntenatalEvent[] {\n const {\n today = new Date(),\n schedule = 'it',\n plurality = 'singleton',\n chorionicity = 'unknown',\n rhNegative = false,\n gdmRisk,\n includeOnIndication = false,\n birthGuideline = 'nice',\n nearDays = 14,\n } = opts;\n\n return [\n ...baseEvents(schedule),\n ...multipleEvents(plurality, chorionicity, birthGuideline),\n ]\n .filter((event) =>\n eventApplies(event, {\n plurality,\n chorionicity,\n rhNegative,\n gdmRisk,\n includeOnIndication,\n }),\n )\n .map((event): ResolvedAntenatalEvent => {\n const start = addDays(gestationalStart, event.startDay);\n const end = addDays(gestationalStart, event.endDay);\n const { status, near } = statusFor(start, end, today, nearDays);\n return {\n ...event,\n start,\n end,\n single: event.startDay === event.endDay,\n status,\n near,\n };\n })\n .sort((a, b) => {\n const byStart = a.startDay - b.startDay;\n return byStart !== 0 ? byStart : a.endDay - b.endDay;\n });\n}\n","/* ------------------------------------------------------------------ */\n/* AntenatalScheduleTimeline — the presentational list of resolved */\n/* antenatal windows, shared by the `showSchedule` appendix on */\n/* GestationalAgeCalculator and DueDateCalculator. Pure display: it takes */\n/* already-resolved events and renders the Card + the sr-only now-due */\n/* summary. No inputs, no state, no maths. */\n/* ------------------------------------------------------------------ */\n\nimport { useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Card } from '../card';\nimport { Badge } from '../badge';\nimport type { IconKey, InsertCardField } from './insert-result';\nimport type {\n ResolvedAntenatalEvent,\n AntenatalStatus,\n AntenatalCategory,\n} from './antenatal-schedule';\n\n/** Status → on-screen Badge variant. `near` upcoming items borrow `warning`. */\nfunction statusBadgeVariant(\n status: AntenatalStatus,\n near: boolean,\n): 'neutral' | 'info' | 'warning' {\n if (status === 'past') return 'neutral';\n if (status === 'current') return 'warning';\n return near ? 'warning' : 'info';\n}\n\n/** Compact gestational-age label for a day offset, e.g. 97 → \"13+6\". */\nfunction gaLabel(days: number): string {\n return `${Math.floor(days / 7)}+${days % 7}`;\n}\n\n/** Each schedule category maps to one of the inserted-card glyph keys. */\nconst CATEGORY_ICON: Record<AntenatalCategory, IconKey> = {\n scan: 'activity',\n screening: 'activity',\n diagnostic: 'activity',\n lab: 'droplets',\n prophylaxis: 'heart',\n milestone: 'calendar',\n};\n\n/** Minimal translator shape this module needs (matches react-i18next's `t`). */\ntype Translate = (key: string, opts?: Record<string, unknown>) => string;\n\n/** Localised \"label · range\" for one event (e.g. for the sr-only summary). */\nfunction eventLabelFor(event: ResolvedAntenatalEvent, t: Translate): string {\n return t(`antenatalSchedule.event.${event.key}`, event.meta ?? {});\n}\n\n/**\n * Build the inserted-card rows for a resolved schedule, so a host calculator's\n * single Insert button can carry the schedule in its payload (rather than a\n * second Insert button on the schedule card). One row per event: a\n * category-tinted glyph, the localised label, and the date range.\n */\nexport function buildScheduleInsertFields(\n events: ResolvedAntenatalEvent[],\n t: Translate,\n formatDate: (date: Date) => string,\n): InsertCardField[] {\n return events.map((event) => ({\n icon: CATEGORY_ICON[event.category],\n label: eventLabelFor(event, t),\n value: event.single\n ? formatDate(event.start)\n : `${formatDate(event.start)} – ${formatDate(event.end)}`,\n }));\n}\n\nexport interface AntenatalScheduleTimelineProps {\n /** Resolved schedule events (from `computeAntenatalSchedule`). */\n events: ResolvedAntenatalEvent[];\n}\n\nexport function AntenatalScheduleTimeline({\n events,\n}: AntenatalScheduleTimelineProps) {\n const { t, i18n } = useTranslation();\n\n const dateFormatter = useMemo(\n () => new Intl.DateTimeFormat(i18n.language, { dateStyle: 'medium' }),\n [i18n.language],\n );\n\n const eventLabel = (event: ResolvedAntenatalEvent): string =>\n eventLabelFor(event, t);\n\n const formatRange = (event: ResolvedAntenatalEvent): string =>\n event.single\n ? dateFormatter.format(event.start)\n : `${dateFormatter.format(event.start)} – ${dateFormatter.format(\n event.end,\n )}`;\n\n // No own live region: this timeline is always embedded in a host calculator\n // that already owns a `role=\"status\"` result announcement, so a second one\n // here would double-announce. The schedule is fully present in the DOM\n // (labelled list + per-row status Badge) for assistive tech to browse.\n return (\n <Card variant=\"elevated\">\n <Card.Body className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('antenatalSchedule.scheduleLabel')}\n </span>\n <ul className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {events.map((event) => (\n <li\n key={event.key}\n data-status={event.status}\n className=\"ds:flex ds:flex-wrap ds:items-start ds:justify-between ds:gap-x-[var(--spacing-md)] ds:gap-y-[var(--spacing-xs)] ds:border-b ds:border-border ds:pb-[var(--spacing-sm)] ds:last:border-b-0 ds:last:pb-0\"\n >\n <span className=\"ds:flex ds:flex-col ds:items-start ds:gap-[var(--spacing-2xs)]\">\n <span className=\"type-body ds:text-foreground\">\n {eventLabel(event)}\n </span>\n <span className=\"type-meta ds:text-muted-foreground\">\n {t(`antenatalSchedule.category.${event.category}`)} ·{' '}\n {gaLabel(event.startDay)}\n {event.single ? '' : ` – ${gaLabel(event.endDay)}`} ·{' '}\n {t(`antenatalSchedule.source.${event.source}`)}\n {event.condition === 'on-indication'\n ? ` · ${t('antenatalSchedule.onIndication')}`\n : ''}\n </span>\n </span>\n <span className=\"ds:flex ds:flex-col ds:items-end ds:gap-[var(--spacing-2xs)] ds:text-end\">\n <span className=\"type-body ds:text-foreground\">\n {formatRange(event)}\n </span>\n <Badge variant={statusBadgeVariant(event.status, event.near)}>\n {event.status === 'upcoming' && event.near\n ? t('antenatalSchedule.near')\n : t(`antenatalSchedule.status.${event.status}`)}\n </Badge>\n </span>\n </li>\n ))}\n </ul>\n <p className=\"type-meta ds:text-muted-foreground\">\n {t('antenatalSchedule.disclaimer')}\n </p>\n </Card.Body>\n </Card>\n );\n}\n"],"names":["GESTATION_DAYS","CONCEPTION_OFFSET_DAYS","TERM_FROM_CONCEPTION_DAYS","DEFAULT_CYCLE_LENGTH","trimesterForWeeks","totalDays","weeks","computeDueDate","input","today","method","date","gestationalStart","dueDate","cycle","addDays","subDays","embryo","conceptionDate","differenceInCalendarDays","gestationalAge","MILESTONE_WEEKS","gestationalMilestones","week","TRIMESTER_BADGE_VARIANT","TRIMESTER_HIGHLIGHT_TOKEN","trimesterHighlightToken","trimester","day","d","plannedBirthWindow","plurality","chorionicity","guideline","effective","CRL_DATING_MIN_MM","CRL_DATING_MAX_MM","crlToGestationalAgeDays","crlMm","redatingThresholdDays","gaByLmpDays","redateByCrl","gaByCrlDays","discrepancyDays","thresholdDays","redated","ga","datingNt","locale","nipt","cvs","amnio","morphology","growth","ogttHigh","ogttMedium","gbs","antiD","term","edd","chorionicityDetermination","baseEvents","multipleEvents","isDichorionic","everyWeeks","fromWeek","birth","events","eventApplies","event","opts","statusFor","start","end","nearDays","daysUntilStart","computeAntenatalSchedule","schedule","rhNegative","gdmRisk","includeOnIndication","birthGuideline","status","near","a","b","byStart","statusBadgeVariant","gaLabel","days","CATEGORY_ICON","eventLabelFor","t","buildScheduleInsertFields","formatDate","AntenatalScheduleTimeline","i18n","useTranslation","dateFormatter","useMemo","eventLabel","formatRange","jsx","Card","jsxs","Badge"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { useTranslation as De } from "react-i18next";
|
|
|
5
5
|
import { A as we } from "./appointment-card-DgKI5QKD.js";
|
|
6
6
|
import { B as K } from "./button-CPGiR_bH.js";
|
|
7
7
|
import { B as Re } from "./button-group-DzV5wgnx.js";
|
|
8
|
-
import { D as Ie } from "./date-picker-
|
|
8
|
+
import { D as Ie } from "./date-picker-ffmyjvlI.js";
|
|
9
9
|
import { P as Se } from "./panel-header-D313n_9-.js";
|
|
10
10
|
import { S as je } from "./skeleton-CZbwyJAA.js";
|
|
11
11
|
import { E as Ce } from "./empty-state-CrLsi0XI.js";
|
|
@@ -385,4 +385,4 @@ export {
|
|
|
385
385
|
Ke as A,
|
|
386
386
|
Ne as a
|
|
387
387
|
};
|
|
388
|
-
//# sourceMappingURL=appointment-timeline-
|
|
388
|
+
//# sourceMappingURL=appointment-timeline-CwpbQGC1.js.map
|
package/dist/_chunks/{appointment-timeline-DQTG0btW.js.map → appointment-timeline-CwpbQGC1.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appointment-timeline-DQTG0btW.js","sources":["../../src/components/appointment-timeline/appointment-timeline.agent.ts","../../src/components/appointment-timeline/appointment-timeline.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — AppointmentTimeline. */\n/* */\n/* Timeline surface: read appointment ids + lifecycle state (never */\n/* patient names / treatments), activate a row through the curated handle, */\n/* and re-anchor the scroll on today. In-page (no open/close). */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { AppointmentTimelineHandle } from './appointment-timeline';\n\nexport const appointmentTimelineAgent: AgentAdapter<AppointmentTimelineHandle> =\n {\n id: 'appointment-timeline',\n // 'pick' backs select_item (non-persisting activation through\n // onOpenAppointment); 'scroll' backs jump_to_today, which re-anchors the\n // scroll viewport (mirrors practice-results' scroll_to_result).\n capabilities: ['scroll', 'pick'],\n state: {\n items: {\n type: 'Array<{ id: string; state: string }>',\n descriptionKey: 'ui.agent.appointmentTimeline.state.items',\n description:\n 'Currently-rendered appointments. Ids + lifecycle state only — never patient identity.',\n read: (handle) => handle.getItems(),\n },\n },\n actions: {\n select_item: {\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: { type: 'string' } },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.appointmentTimeline.actions.selectItem',\n description: 'Activate the appointment with the given id.',\n invoke: (handle, args: { id: string }) => {\n handle.selectItem(args.id);\n },\n },\n jump_to_today: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.appointmentTimeline.actions.jumpToToday',\n description: \"Re-anchor the timeline scroll on today's appointments.\",\n invoke: (handle) => {\n handle.jumpToToday();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'appointment-timeline',\n description: 'Marks the AppointmentTimeline wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Required to address a specific timeline from the agent.',\n },\n item: {\n attr: 'data-appointment-id',\n description:\n 'Stable appointment id emitted on each rendered AppointmentCard inside the timeline.',\n },\n },\n };\n","import {\n forwardRef,\n useCallback,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { CalendarDays } from 'lucide-react';\nimport { AppointmentCard, type AppointmentItem } from '../appointment-card';\nimport { Button } from '../button';\nimport { ButtonGroup } from '../button-group';\nimport { DatePicker } from '../date-picker';\nimport { PanelHeader } from '../panel-header';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect';\nimport { usePrefersReducedMotion } from '../../hooks/use-prefers-reduced-motion';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { appointmentTimelineAgent } from './appointment-timeline.agent';\n\n/* -------------------------------------------------------------------- */\n/* AppointmentTimeline */\n/* */\n/* A vertical, day-grouped appointment timeline for the Control Room */\n/* rail. Unlike AppointmentTray (a bordered card of a flat list), this */\n/* fills the rail edge-to-edge: appointments are grouped under day */\n/* headers, the scroll is anchored on TODAY, and it scrolls infinitely */\n/* into the past and future — the consumer feeds more via onLoadEarlier */\n/* / onLoadLater as either edge is reached (scroll position is preserved */\n/* when earlier items prepend). Date-filter presets + a jump-to-date */\n/* picker re-anchor the scroll. Top + bottom transparent gradient fades */\n/* (a block-axis mask) signal more content beyond each edge. */\n/* */\n/* Each row reuses AppointmentCard (care-plan + chair chips included). */\n/* All visible text flows through useTranslation('ui') against the */\n/* `appointmentTimeline.*` namespace; no PHI reaches the agent adapter. */\n/* -------------------------------------------------------------------- */\n\n/* How close (px) to an edge before the matching load callback fires. */\nconst EDGE_LOAD_THRESHOLD = 96;\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** Curated imperative handle — identifiers + lifecycle state only. */\nexport interface AppointmentTimelineHandle {\n getItems: () => Array<{ id: string; state: string }>;\n selectItem: (id: string) => void;\n jumpToToday: () => void;\n}\n\nexport interface AppointmentTimelineProps extends Omit<\n HTMLAttributes<HTMLElement>,\n 'onClick' | 'id' | 'title'\n> {\n /** Stable instance id, surfaced as `data-component-id`. */\n id?: string;\n /** Appointments to render — any order; grouped + sorted internally. */\n items: AppointmentItem[];\n /**\n * ISO-8601 \"now\" anchor — the timeline scrolls to this day on mount and the\n * \"Today\" preset returns to it. Defaults to the current instant. Stories pass\n * a fixed value for deterministic snapshots.\n */\n nowIso?: string;\n /** Panel heading. Defaults to the localised \"Appointments\". */\n title?: string;\n /** BCP-47 locale override for day headers + each AppointmentCard. */\n locale?: string;\n /** ISO-4217 currency code passed to each AppointmentCard. */\n currency?: string;\n /** Visual density passed to each AppointmentCard. */\n size?: 'sm' | 'md';\n /** Fires when a row is activated. */\n onOpenAppointment?: (item: AppointmentItem) => void;\n\n /* ----- Infinite scroll ----- */\n /** Load earlier (past) appointments — fired when the top edge is reached. */\n onLoadEarlier?: () => void;\n /** Load later (future) appointments — fired when the bottom edge is reached. */\n onLoadLater?: () => void;\n /** Whether more past appointments exist. Defaults to `true` when `onLoadEarlier` is set. */\n hasMoreEarlier?: boolean;\n /** Whether more future appointments exist. Defaults to `true` when `onLoadLater` is set. */\n hasMoreLater?: boolean;\n /** Show a loading row at the top edge. */\n loadingEarlier?: boolean;\n /** Show a loading row at the bottom edge. */\n loadingLater?: boolean;\n\n /* ----- Filters ----- */\n /** Show the date-filter row (presets + jump-to-date). Default `true`. */\n showFilters?: boolean;\n /**\n * Fired when a jump-to-date / preset targets a day outside the loaded range,\n * so the consumer can load it. The timeline scrolls to the nearest rendered\n * day until then.\n */\n onJumpToDate?: (dateIso: string) => void;\n\n /* ----- Empty ----- */\n emptyTitle?: string;\n emptyDescription?: string;\n\n /** Accessible name for the panel region (defaults to `title`). */\n 'aria-label'?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* CVA */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n [\n 'ds:flex ds:flex-col ds:h-full ds:min-h-0',\n 'ds:bg-[color:var(--card)] ds:text-[color:var(--card-foreground)]',\n ].join(' '),\n { variants: { size: { sm: '', md: '' } }, defaultVariants: { size: 'sm' } },\n);\n\n/* Scroll region with a block-axis fade mask. The CSS custom properties flip to\n `transparent` at whichever edge has more content (data-fade-top /\n data-fade-bottom), mirroring the TabBar edge-mask technique on the Y axis.\n Disabled under reduced-motion, the accessible theme, and forced-colors. */\nconst scrollRegionClasses = [\n 'ds:flex-1 ds:min-h-0 ds:overflow-y-auto',\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]',\n 'ds:px-[var(--spacing-sm)] ds:py-[var(--spacing-xs)]',\n 'ds:[--atl-mask-top:black] ds:[--atl-mask-bottom:black]',\n 'ds:data-[fade-top=true]:[--atl-mask-top:transparent]',\n 'ds:data-[fade-bottom=true]:[--atl-mask-bottom:transparent]',\n 'ds:[mask-image:linear-gradient(to_bottom,var(--atl-mask-top)_0,black_var(--atl-fade-distance,var(--spacing-lg)),black_calc(100%-var(--atl-fade-distance,var(--spacing-lg))),var(--atl-mask-bottom)_100%)]',\n 'ds:motion-reduce:[mask-image:none]',\n 'ds:[.theme-accessible_&]:[mask-image:none]',\n 'ds:forced-colors:[mask-image:none]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:[outline-offset:-2px]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n].join(' ');\n\n/* -------------------------------------------------------------------- */\n/* Day grouping + header labels */\n/* -------------------------------------------------------------------- */\n\ninterface DayGroup {\n key: string;\n date: Date;\n items: AppointmentItem[];\n}\n\n/** Local calendar day key (`YYYY-MM-DD`) — sorts chronologically as a string. */\nfunction localDayKey(d: Date): string {\n const m = `${d.getMonth() + 1}`.padStart(2, '0');\n const day = `${d.getDate()}`.padStart(2, '0');\n return `${d.getFullYear()}-${m}-${day}`;\n}\n\nfunction groupByDay(items: AppointmentItem[]): DayGroup[] {\n const sorted = [...items].sort(\n (a, b) => +new Date(a.startIso) - +new Date(b.startIso),\n );\n const map = new Map<string, DayGroup>();\n for (const item of sorted) {\n const d = new Date(item.startIso);\n const key = localDayKey(d);\n let group = map.get(key);\n if (!group) {\n group = {\n key,\n date: new Date(d.getFullYear(), d.getMonth(), d.getDate()),\n items: [],\n };\n map.set(key, group);\n }\n group.items.push(item);\n }\n return [...map.values()];\n}\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\nexport const AppointmentTimeline = forwardRef<\n HTMLElement,\n AppointmentTimelineProps\n>(\n (\n {\n id,\n items,\n nowIso,\n title,\n locale,\n currency,\n size = 'sm',\n onOpenAppointment,\n onLoadEarlier,\n onLoadLater,\n hasMoreEarlier,\n hasMoreLater,\n loadingEarlier = false,\n loadingLater = false,\n showFilters = true,\n onJumpToDate,\n emptyTitle,\n emptyDescription,\n className,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const jumpId = useId();\n const prefersReducedMotion = usePrefersReducedMotion();\n const resolvedLocale = locale ?? i18n.language;\n\n const heading = title ?? t('appointmentTimeline.title');\n const now = useMemo(\n () => (nowIso ? new Date(nowIso) : new Date()),\n [nowIso],\n );\n\n const groups = useMemo(() => groupByDay(items), [items]);\n\n const moreEarlier = hasMoreEarlier ?? !!onLoadEarlier;\n const moreLater = hasMoreLater ?? !!onLoadLater;\n\n /* The day to anchor on: today if present, else the nearest upcoming day,\n else the last day we have. */\n const anchorKey = useMemo(() => {\n if (groups.length === 0) return undefined;\n const todayKey = localDayKey(now);\n const keys = groups.map((g) => g.key);\n if (keys.includes(todayKey)) return todayKey;\n return keys.find((k) => k >= todayKey) ?? keys[keys.length - 1];\n }, [groups, now]);\n\n const scrollRef = useRef<HTMLDivElement>(null);\n const dayElRefs = useRef<Map<string, HTMLElement>>(new Map());\n const hasAnchored = useRef(false);\n const prevFirstKey = useRef<string | undefined>(undefined);\n const prevScrollHeight = useRef(0);\n\n const [fadeTop, setFadeTop] = useState(false);\n const [fadeBottom, setFadeBottom] = useState(false);\n\n const updateEdges = useCallback(() => {\n const region = scrollRef.current;\n if (!region) return;\n const { scrollTop, scrollHeight, clientHeight } = region;\n setFadeTop(scrollTop > 1);\n setFadeBottom(scrollTop + clientHeight < scrollHeight - 1);\n }, []);\n\n /* Scroll a day into view at the top of the region. Falls back to the\n nearest rendered day on/after the target (presets may land on an empty\n day). Uses getBoundingClientRect so it is offsetParent-agnostic. */\n const scrollToKey = useCallback(\n (targetKey: string, smooth: boolean) => {\n const region = scrollRef.current;\n if (!region) return;\n let el = dayElRefs.current.get(targetKey);\n if (!el) {\n const keys = [...dayElRefs.current.keys()].sort();\n const next =\n keys.find((k) => k >= targetKey) ?? keys[keys.length - 1];\n el = next ? dayElRefs.current.get(next) : undefined;\n }\n if (!el) return;\n const delta =\n el.getBoundingClientRect().top - region.getBoundingClientRect().top;\n region.scrollTo({\n top: region.scrollTop + delta,\n behavior: smooth && !prefersReducedMotion ? 'smooth' : 'auto',\n });\n },\n [prefersReducedMotion],\n );\n\n /* Anchor on mount; preserve the viewport when earlier days prepend. */\n useIsomorphicLayoutEffect(() => {\n const region = scrollRef.current;\n if (!region) return;\n const firstKey = groups[0]?.key;\n if (!hasAnchored.current) {\n if (groups.length > 0 && anchorKey) {\n scrollToKey(anchorKey, false);\n hasAnchored.current = true;\n }\n } else if (\n prevFirstKey.current &&\n firstKey &&\n firstKey < prevFirstKey.current\n ) {\n const grew = region.scrollHeight - prevScrollHeight.current;\n if (grew > 0) region.scrollTop += grew;\n }\n prevFirstKey.current = firstKey;\n prevScrollHeight.current = region.scrollHeight;\n updateEdges();\n }, [groups, anchorKey, scrollToKey, updateEdges]);\n\n /* Recompute the fades when the region resizes. */\n useIsomorphicLayoutEffect(() => {\n const region = scrollRef.current;\n if (!region || typeof ResizeObserver === 'undefined') return undefined;\n const observer = new ResizeObserver(updateEdges);\n observer.observe(region);\n return () => observer.disconnect();\n }, [updateEdges]);\n\n const handleScroll = useCallback(() => {\n updateEdges();\n const region = scrollRef.current;\n if (!region) return;\n const { scrollTop, scrollHeight, clientHeight } = region;\n if (\n onLoadEarlier &&\n moreEarlier &&\n !loadingEarlier &&\n scrollTop <= EDGE_LOAD_THRESHOLD\n ) {\n onLoadEarlier();\n }\n if (\n onLoadLater &&\n moreLater &&\n !loadingLater &&\n scrollHeight - scrollTop - clientHeight <= EDGE_LOAD_THRESHOLD\n ) {\n onLoadLater();\n }\n }, [\n updateEdges,\n onLoadEarlier,\n onLoadLater,\n moreEarlier,\n moreLater,\n loadingEarlier,\n loadingLater,\n ]);\n\n /* ----- Filter actions ----- */\n const jumpTo = useCallback(\n (date: Date) => {\n const key = localDayKey(date);\n const known = dayElRefs.current.has(key);\n scrollToKey(key, true);\n if (!known) onJumpToDate?.(date.toISOString());\n },\n [scrollToKey, onJumpToDate],\n );\n\n const handleToday = useCallback(() => jumpTo(now), [jumpTo, now]);\n const handleThisWeek = useCallback(() => {\n const start = new Date(now);\n const dow = (start.getDay() + 6) % 7; // Monday-based week start\n start.setDate(start.getDate() - dow);\n jumpTo(start);\n }, [jumpTo, now]);\n const handleThisMonth = useCallback(() => {\n jumpTo(new Date(now.getFullYear(), now.getMonth(), 1));\n }, [jumpTo, now]);\n\n /* ----- Agent handle ----- */\n const itemsRef = useRef<AppointmentItem[]>(items);\n itemsRef.current = items;\n const innerRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => innerRef.current as HTMLElement, []);\n\n const handle = useMemo<AppointmentTimelineHandle>(\n () => ({\n getItems: () =>\n itemsRef.current.map((i) => ({ id: i.id, state: i.state })),\n selectItem: (targetId: string) => {\n const found = itemsRef.current.find((i) => i.id === targetId);\n if (found) onOpenAppointment?.(found);\n },\n jumpToToday: () => scrollToKey(localDayKey(now), true),\n }),\n [onOpenAppointment, scrollToKey, now],\n );\n useAgentRegistration(appointmentTimelineAgent, handle, id);\n\n const isEmpty = groups.length === 0 && !loadingEarlier && !loadingLater;\n\n const loadingRow = (key: string) => (\n <div\n key={key}\n aria-hidden=\"true\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)] ds:py-[var(--spacing-xs)]\"\n >\n <Skeleton variant=\"rectangular\" height=\"3.5rem\" />\n </div>\n );\n\n return (\n <section\n ref={innerRef}\n data-component=\"appointment-timeline\"\n data-component-id={id}\n aria-label={ariaLabel ?? heading}\n className={rootVariants({ size, className })}\n {...rest}\n >\n {/* Shared rail header: title + (optional) filter row + divider. This\n panel owns its own scroll/fill, so the header stays a fixed band\n (sticky={false}). The rail injects the favourite/close cluster via\n PanelHeaderActionsProvider; standalone it renders none. */}\n <PanelHeader\n icon={<CalendarDays />}\n title={heading}\n filters={\n showFilters ? (\n <>\n <ButtonGroup label={t('appointmentTimeline.filtersLabel')}>\n <Button intent=\"outline\" size=\"sm\" onClick={handleToday}>\n {t('appointmentTimeline.today')}\n </Button>\n <Button intent=\"outline\" size=\"sm\" onClick={handleThisWeek}>\n {t('appointmentTimeline.thisWeek')}\n </Button>\n <Button intent=\"outline\" size=\"sm\" onClick={handleThisMonth}>\n {t('appointmentTimeline.thisMonth')}\n </Button>\n </ButtonGroup>\n <label htmlFor={jumpId} className=\"ds:sr-only\">\n {t('appointmentTimeline.jumpToDate')}\n </label>\n <DatePicker\n id={jumpId}\n size=\"sm\"\n placeholder={t('appointmentTimeline.jumpToDate')}\n onChange={(date) => {\n if (date) jumpTo(date);\n }}\n />\n </>\n ) : undefined\n }\n />\n\n {isEmpty ? (\n <div className=\"ds:flex-1 ds:p-[var(--spacing-md)]\">\n <EmptyState\n variant=\"first-use\"\n size=\"sm\"\n title={emptyTitle ?? t('appointmentTimeline.empty.title')}\n description={\n emptyDescription ?? t('appointmentTimeline.empty.description')\n }\n />\n </div>\n ) : (\n <div\n ref={scrollRef}\n // The scroll region must be keyboard-focusable so arrow / Home /\n // End can drive it (axe scrollable-region-focusable); jsx-a11y's\n // generic rule doesn't know this element scrolls.\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n tabIndex={0}\n onScroll={handleScroll}\n data-fade-top={fadeTop ? 'true' : undefined}\n data-fade-bottom={fadeBottom ? 'true' : undefined}\n className={scrollRegionClasses}\n >\n {loadingEarlier ? loadingRow('loading-earlier') : null}\n <ol className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-md)] ds:p-0\">\n {groups.map((group) => (\n <li\n key={group.key}\n ref={(node) => {\n if (node) dayElRefs.current.set(group.key, node);\n else dayElRefs.current.delete(group.key);\n }}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n <DayHeader\n date={group.date}\n now={now}\n locale={resolvedLocale}\n todayLabel={t('appointmentTimeline.today')}\n tomorrowLabel={t('appointmentTimeline.tomorrow')}\n yesterdayLabel={t('appointmentTimeline.yesterday')}\n />\n <ul className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-0\">\n {group.items.map((item) => (\n <AppointmentCard\n key={item.id}\n item={item}\n size={size}\n surface=\"elevated\"\n showPatient={false}\n locale={resolvedLocale}\n currency={currency}\n onActivate={onOpenAppointment}\n data-appointment-id={item.id}\n />\n ))}\n </ul>\n </li>\n ))}\n </ol>\n {loadingLater ? loadingRow('loading-later') : null}\n </div>\n )}\n </section>\n );\n },\n);\n\nAppointmentTimeline.displayName = 'AppointmentTimeline';\n\n/* -------------------------------------------------------------------- */\n/* Day header — sticky label per day group */\n/* -------------------------------------------------------------------- */\n\ninterface DayHeaderProps {\n date: Date;\n now: Date;\n locale: string | undefined;\n todayLabel: string;\n tomorrowLabel: string;\n yesterdayLabel: string;\n}\n\nfunction DayHeader({\n date,\n now,\n locale,\n todayLabel,\n tomorrowLabel,\n yesterdayLabel,\n}: DayHeaderProps): ReactNode {\n const key = localDayKey(date);\n const todayKey = localDayKey(now);\n const tomorrow = new Date(now);\n tomorrow.setDate(now.getDate() + 1);\n const yesterday = new Date(now);\n yesterday.setDate(now.getDate() - 1);\n\n const isToday = key === todayKey;\n\n let label: string;\n if (isToday) label = todayLabel;\n else if (key === localDayKey(tomorrow)) label = tomorrowLabel;\n else if (key === localDayKey(yesterday)) label = yesterdayLabel;\n else {\n const sameYear = date.getFullYear() === now.getFullYear();\n label = new Intl.DateTimeFormat(locale, {\n weekday: 'short',\n day: 'numeric',\n month: 'short',\n ...(sameYear ? {} : { year: 'numeric' }),\n }).format(date);\n }\n\n // Today's group is marked with the brand colour + a leading dot so the\n // current day reads at a glance. `--primary` clears AA on the card in every\n // theme (violet-500 light, magenta on dark); the dot is decorative.\n return (\n <div\n data-today={isToday ? 'true' : undefined}\n className={[\n 'ds:sticky ds:top-0 ds:z-[1] ds:bg-[color:var(--card)] ds:py-[var(--spacing-2xs)]',\n 'ds:flex ds:items-center ds:gap-[var(--spacing-2xs)] type-eyebrow',\n isToday\n ? 'ds:text-[color:var(--primary)] ds:font-semibold'\n : 'ds:text-[color:var(--muted-foreground)]',\n ].join(' ')}\n >\n {isToday ? (\n <span\n aria-hidden=\"true\"\n className=\"ds:size-1.5 ds:shrink-0 ds:rounded-[var(--radius-full)] ds:bg-[color:var(--primary)]\"\n />\n ) : null}\n {label}\n </div>\n );\n}\n\n/** Re-export of the size variant union for downstream typing. */\nexport type AppointmentTimelineSize = NonNullable<\n VariantProps<typeof rootVariants>['size']\n>;\n"],"names":["appointmentTimelineAgent","handle","args","EDGE_LOAD_THRESHOLD","rootVariants","cva","scrollRegionClasses","localDayKey","d","m","day","groupByDay","items","sorted","a","b","map","item","key","group","AppointmentTimeline","forwardRef","id","nowIso","title","locale","currency","size","onOpenAppointment","onLoadEarlier","onLoadLater","hasMoreEarlier","hasMoreLater","loadingEarlier","loadingLater","showFilters","onJumpToDate","emptyTitle","emptyDescription","className","ariaLabel","rest","ref","t","i18n","useTranslation","jumpId","useId","prefersReducedMotion","usePrefersReducedMotion","resolvedLocale","heading","now","useMemo","groups","moreEarlier","moreLater","anchorKey","todayKey","keys","g","k","scrollRef","useRef","dayElRefs","hasAnchored","prevFirstKey","prevScrollHeight","fadeTop","setFadeTop","useState","fadeBottom","setFadeBottom","updateEdges","useCallback","region","scrollTop","scrollHeight","clientHeight","scrollToKey","targetKey","smooth","el","next","delta","useIsomorphicLayoutEffect","firstKey","_a","grew","observer","handleScroll","jumpTo","date","known","handleToday","handleThisWeek","start","dow","handleThisMonth","itemsRef","innerRef","useImperativeHandle","i","targetId","found","useAgentRegistration","isEmpty","loadingRow","jsx","Skeleton","jsxs","PanelHeader","CalendarDays","Fragment","ButtonGroup","Button","DatePicker","EmptyState","node","DayHeader","AppointmentCard","todayLabel","tomorrowLabel","yesterdayLabel","tomorrow","yesterday","isToday","label","sameYear"],"mappings":";;;;;;;;;;;;;;;AAWO,MAAMA,KACX;AAAA,EACE,IAAI;AAAA;AAAA;AAAA;AAAA,EAIJ,cAAc,CAAC,UAAU,MAAM;AAAA,EAC/B,OAAO;AAAA,IACL,OAAO;AAAA,MACL,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,MAAM,CAACC,MAAWA,EAAO,SAAA;AAAA,IAAS;AAAA,EACpC;AAAA,EAEF,SAAS;AAAA,IACP,aAAa;AAAA,MACX,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY,EAAE,IAAI,EAAE,MAAM,WAAS;AAAA,QACnC,UAAU,CAAC,IAAI;AAAA,MAAA;AAAA,MAEjB,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,QAAQ,CAACA,GAAQC,MAAyB;AACxC,QAAAD,EAAO,WAAWC,EAAK,EAAE;AAAA,MAC3B;AAAA,IAAA;AAAA,IAEF,eAAe;AAAA,MACb,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,QAAQ,CAACD,MAAW;AAClB,QAAAA,EAAO,YAAA;AAAA,MACT;AAAA,IAAA;AAAA,EACF;AAAA,EAEF,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,IAAA;AAAA,IAEf,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,aACE;AAAA,IAAA;AAAA,IAEJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,IAAA;AAAA,EACJ;AAEJ,GC1BIE,IAAsB,IA0EtBC,KAAeC;AAAA,EACnB;AAAA,IACE;AAAA,IACA;AAAA,EAAA,EACA,KAAK,GAAG;AAAA,EACV,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,IAAI,IAAI,GAAA,KAAQ,iBAAiB,EAAE,MAAM,OAAK;AAC1E,GAMMC,KAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAaV,SAASC,EAAYC,GAAiB;AACpC,QAAMC,IAAI,GAAGD,EAAE,SAAA,IAAa,CAAC,GAAG,SAAS,GAAG,GAAG,GACzCE,IAAM,GAAGF,EAAE,QAAA,CAAS,GAAG,SAAS,GAAG,GAAG;AAC5C,SAAO,GAAGA,EAAE,YAAA,CAAa,IAAIC,CAAC,IAAIC,CAAG;AACvC;AAEA,SAASC,GAAWC,GAAsC;AACxD,QAAMC,IAAS,CAAC,GAAGD,CAAK,EAAE;AAAA,IACxB,CAACE,GAAGC,MAAM,CAAC,IAAI,KAAKD,EAAE,QAAQ,IAAI,CAAC,IAAI,KAAKC,EAAE,QAAQ;AAAA,EAAA,GAElDC,wBAAU,IAAA;AAChB,aAAWC,KAAQJ,GAAQ;AACzB,UAAML,IAAI,IAAI,KAAKS,EAAK,QAAQ,GAC1BC,IAAMX,EAAYC,CAAC;AACzB,QAAIW,IAAQH,EAAI,IAAIE,CAAG;AACvB,IAAKC,MACHA,IAAQ;AAAA,MACN,KAAAD;AAAA,MACA,MAAM,IAAI,KAAKV,EAAE,YAAA,GAAeA,EAAE,SAAA,GAAYA,EAAE,SAAS;AAAA,MACzD,OAAO,CAAA;AAAA,IAAC,GAEVQ,EAAI,IAAIE,GAAKC,CAAK,IAEpBA,EAAM,MAAM,KAAKF,CAAI;AAAA,EACvB;AACA,SAAO,CAAC,GAAGD,EAAI,QAAQ;AACzB;AAMO,MAAMI,KAAsBC;AAAA,EAIjC,CACE;AAAA,IACE,IAAAC;AAAA,IACA,OAAAV;AAAA,IACA,QAAAW;AAAA,IACA,OAAAC;AAAA,IACA,QAAAC;AAAA,IACA,UAAAC;AAAA,IACA,MAAAC,IAAO;AAAA,IACP,mBAAAC;AAAA,IACA,eAAAC;AAAA,IACA,aAAAC;AAAA,IACA,gBAAAC;AAAA,IACA,cAAAC;AAAA,IACA,gBAAAC,IAAiB;AAAA,IACjB,cAAAC,IAAe;AAAA,IACf,aAAAC,IAAc;AAAA,IACd,cAAAC;AAAA,IACA,YAAAC;AAAA,IACA,kBAAAC;AAAA,IACA,WAAAC;AAAA,IACA,cAAcC;AAAA,IACd,GAAGC;AAAA,EAAA,GAELC,OACG;AACH,UAAM,EAAE,GAAAC,GAAG,MAAAC,GAAA,IAASC,GAAA,GACdC,IAASC,GAAA,GACTC,IAAuBC,GAAA,GACvBC,IAAiBzB,KAAUmB,GAAK,UAEhCO,IAAU3B,KAASmB,EAAE,2BAA2B,GAChDS,IAAMC;AAAA,MACV,MAAO9B,IAAS,IAAI,KAAKA,CAAM,wBAAQ,KAAA;AAAA,MACvC,CAACA,CAAM;AAAA,IAAA,GAGH+B,IAASD,EAAQ,MAAM1C,GAAWC,CAAK,GAAG,CAACA,CAAK,CAAC,GAEjD2C,IAAcxB,KAAkB,CAAC,CAACF,GAClC2B,IAAYxB,KAAgB,CAAC,CAACF,GAI9B2B,IAAYJ,EAAQ,MAAM;AAC9B,UAAIC,EAAO,WAAW,EAAG;AACzB,YAAMI,IAAWnD,EAAY6C,CAAG,GAC1BO,IAAOL,EAAO,IAAI,CAACM,MAAMA,EAAE,GAAG;AACpC,aAAID,EAAK,SAASD,CAAQ,IAAUA,IAC7BC,EAAK,KAAK,CAACE,MAAMA,KAAKH,CAAQ,KAAKC,EAAKA,EAAK,SAAS,CAAC;AAAA,IAChE,GAAG,CAACL,GAAQF,CAAG,CAAC,GAEVU,IAAYC,EAAuB,IAAI,GACvCC,IAAYD,EAAiC,oBAAI,KAAK,GACtDE,IAAcF,EAAO,EAAK,GAC1BG,IAAeH,EAA2B,MAAS,GACnDI,IAAmBJ,EAAO,CAAC,GAE3B,CAACK,IAASC,EAAU,IAAIC,EAAS,EAAK,GACtC,CAACC,IAAYC,EAAa,IAAIF,EAAS,EAAK,GAE5CG,IAAcC,EAAY,MAAM;AACpC,YAAMC,IAASb,EAAU;AACzB,UAAI,CAACa,EAAQ;AACb,YAAM,EAAE,WAAAC,GAAW,cAAAC,GAAc,cAAAC,EAAA,IAAiBH;AAClD,MAAAN,GAAWO,IAAY,CAAC,GACxBJ,GAAcI,IAAYE,IAAeD,IAAe,CAAC;AAAA,IAC3D,GAAG,CAAA,CAAE,GAKCE,IAAcL;AAAA,MAClB,CAACM,GAAmBC,MAAoB;AACtC,cAAMN,IAASb,EAAU;AACzB,YAAI,CAACa,EAAQ;AACb,YAAIO,IAAKlB,EAAU,QAAQ,IAAIgB,CAAS;AACxC,YAAI,CAACE,GAAI;AACP,gBAAMvB,IAAO,CAAC,GAAGK,EAAU,QAAQ,KAAA,CAAM,EAAE,KAAA,GACrCmB,IACJxB,EAAK,KAAK,CAACE,OAAMA,MAAKmB,CAAS,KAAKrB,EAAKA,EAAK,SAAS,CAAC;AAC1D,UAAAuB,IAAKC,IAAOnB,EAAU,QAAQ,IAAImB,CAAI,IAAI;AAAA,QAC5C;AACA,YAAI,CAACD,EAAI;AACT,cAAME,KACJF,EAAG,sBAAA,EAAwB,MAAMP,EAAO,wBAAwB;AAClE,QAAAA,EAAO,SAAS;AAAA,UACd,KAAKA,EAAO,YAAYS;AAAA,UACxB,UAAUH,KAAU,CAACjC,IAAuB,WAAW;AAAA,QAAA,CACxD;AAAA,MACH;AAAA,MACA,CAACA,CAAoB;AAAA,IAAA;AAIvB,IAAAqC,EAA0B,MAAM;;AAC9B,YAAMV,IAASb,EAAU;AACzB,UAAI,CAACa,EAAQ;AACb,YAAMW,KAAWC,IAAAjC,EAAO,CAAC,MAAR,gBAAAiC,EAAW;AAC5B,UAAI,CAACtB,EAAY;AACf,QAAIX,EAAO,SAAS,KAAKG,MACvBsB,EAAYtB,GAAW,EAAK,GAC5BQ,EAAY,UAAU;AAAA,eAGxBC,EAAa,WACboB,KACAA,IAAWpB,EAAa,SACxB;AACA,cAAMsB,IAAOb,EAAO,eAAeR,EAAiB;AACpD,QAAIqB,IAAO,MAAGb,EAAO,aAAaa;AAAA,MACpC;AACA,MAAAtB,EAAa,UAAUoB,GACvBnB,EAAiB,UAAUQ,EAAO,cAClCF,EAAA;AAAA,IACF,GAAG,CAACnB,GAAQG,GAAWsB,GAAaN,CAAW,CAAC,GAGhDY,EAA0B,MAAM;AAC9B,YAAMV,IAASb,EAAU;AACzB,UAAI,CAACa,KAAU,OAAO,iBAAmB,IAAa;AACtD,YAAMc,IAAW,IAAI,eAAehB,CAAW;AAC/C,aAAAgB,EAAS,QAAQd,CAAM,GAChB,MAAMc,EAAS,WAAA;AAAA,IACxB,GAAG,CAAChB,CAAW,CAAC;AAEhB,UAAMiB,KAAehB,EAAY,MAAM;AACrC,MAAAD,EAAA;AACA,YAAME,IAASb,EAAU;AACzB,UAAI,CAACa,EAAQ;AACb,YAAM,EAAE,WAAAC,GAAW,cAAAC,GAAc,cAAAC,EAAA,IAAiBH;AAClD,MACE9C,KACA0B,KACA,CAACtB,KACD2C,KAAazE,KAEb0B,EAAA,GAGAC,KACA0B,KACA,CAACtB,KACD2C,IAAeD,IAAYE,KAAgB3E,KAE3C2B,EAAA;AAAA,IAEJ,GAAG;AAAA,MACD2C;AAAA,MACA5C;AAAA,MACAC;AAAA,MACAyB;AAAA,MACAC;AAAA,MACAvB;AAAA,MACAC;AAAA,IAAA,CACD,GAGKyD,IAASjB;AAAA,MACb,CAACkB,MAAe;AACd,cAAM1E,IAAMX,EAAYqF,CAAI,GACtBC,IAAQ7B,EAAU,QAAQ,IAAI9C,CAAG;AACvC,QAAA6D,EAAY7D,GAAK,EAAI,GAChB2E,KAAOzD,KAAA,QAAAA,EAAewD,EAAK;MAClC;AAAA,MACA,CAACb,GAAa3C,CAAY;AAAA,IAAA,GAGtB0D,KAAcpB,EAAY,MAAMiB,EAAOvC,CAAG,GAAG,CAACuC,GAAQvC,CAAG,CAAC,GAC1D2C,KAAiBrB,EAAY,MAAM;AACvC,YAAMsB,IAAQ,IAAI,KAAK5C,CAAG,GACpB6C,KAAOD,EAAM,OAAA,IAAW,KAAK;AACnC,MAAAA,EAAM,QAAQA,EAAM,QAAA,IAAYC,CAAG,GACnCN,EAAOK,CAAK;AAAA,IACd,GAAG,CAACL,GAAQvC,CAAG,CAAC,GACV8C,KAAkBxB,EAAY,MAAM;AACxC,MAAAiB,EAAO,IAAI,KAAKvC,EAAI,YAAA,GAAeA,EAAI,YAAY,CAAC,CAAC;AAAA,IACvD,GAAG,CAACuC,GAAQvC,CAAG,CAAC,GAGV+C,IAAWpC,EAA0BnD,CAAK;AAChD,IAAAuF,EAAS,UAAUvF;AACnB,UAAMwF,IAAWrC,EAAoB,IAAI;AACzC,IAAAsC,GAAoB3D,IAAK,MAAM0D,EAAS,SAAwB,CAAA,CAAE;AAElE,UAAMnG,KAASoD;AAAA,MACb,OAAO;AAAA,QACL,UAAU,MACR8C,EAAS,QAAQ,IAAI,CAACG,OAAO,EAAE,IAAIA,EAAE,IAAI,OAAOA,EAAE,QAAQ;AAAA,QAC5D,YAAY,CAACC,MAAqB;AAChC,gBAAMC,IAAQL,EAAS,QAAQ,KAAK,CAACG,MAAMA,EAAE,OAAOC,CAAQ;AAC5D,UAAIC,qBAA2BA;AAAA,QACjC;AAAA,QACA,aAAa,MAAMzB,EAAYxE,EAAY6C,CAAG,GAAG,EAAI;AAAA,MAAA;AAAA,MAEvD,CAACxB,GAAmBmD,GAAa3B,CAAG;AAAA,IAAA;AAEtC,IAAAqD,GAAqBzG,IAA0BC,IAAQqB,CAAE;AAEzD,UAAMoF,KAAUpD,EAAO,WAAW,KAAK,CAACrB,KAAkB,CAACC,GAErDyE,IAAa,CAACzF,MAClB,gBAAA0F;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC,eAAY;AAAA,QACZ,WAAU;AAAA,QAEV,UAAA,gBAAAA,EAACC,IAAA,EAAS,SAAQ,eAAc,QAAO,SAAA,CAAS;AAAA,MAAA;AAAA,MAJ3C3F;AAAA,IAAA;AAQT,WACE,gBAAA4F;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAKV;AAAA,QACL,kBAAe;AAAA,QACf,qBAAmB9E;AAAA,QACnB,cAAYkB,MAAaW;AAAA,QACzB,WAAW/C,GAAa,EAAE,MAAAuB,GAAM,WAAAY,IAAW;AAAA,QAC1C,GAAGE;AAAA,QAMJ,UAAA;AAAA,UAAA,gBAAAmE;AAAA,YAACG;AAAA,YAAA;AAAA,cACC,wBAAOC,IAAA,EAAa;AAAA,cACpB,OAAO7D;AAAA,cACP,SACEhB,IACE,gBAAA2E,EAAAG,IAAA,EACE,UAAA;AAAA,gBAAA,gBAAAH,EAACI,IAAA,EAAY,OAAOvE,EAAE,kCAAkC,GACtD,UAAA;AAAA,kBAAA,gBAAAiE,EAACO,GAAA,EAAO,QAAO,WAAU,MAAK,MAAK,SAASrB,IACzC,UAAAnD,EAAE,2BAA2B,EAAA,CAChC;AAAA,kBACA,gBAAAiE,EAACO,GAAA,EAAO,QAAO,WAAU,MAAK,MAAK,SAASpB,IACzC,UAAApD,EAAE,8BAA8B,EAAA,CACnC;AAAA,kBACA,gBAAAiE,EAACO,GAAA,EAAO,QAAO,WAAU,MAAK,MAAK,SAASjB,IACzC,UAAAvD,EAAE,+BAA+B,EAAA,CACpC;AAAA,gBAAA,GACF;AAAA,gBACA,gBAAAiE,EAAC,WAAM,SAAS9D,GAAQ,WAAU,cAC/B,UAAAH,EAAE,gCAAgC,GACrC;AAAA,gBACA,gBAAAiE;AAAA,kBAACQ;AAAA,kBAAA;AAAA,oBACC,IAAItE;AAAA,oBACJ,MAAK;AAAA,oBACL,aAAaH,EAAE,gCAAgC;AAAA,oBAC/C,UAAU,CAACiD,MAAS;AAClB,sBAAIA,OAAaA,CAAI;AAAA,oBACvB;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACF,EAAA,CACF,IACE;AAAA,YAAA;AAAA,UAAA;AAAA,UAIPc,KACC,gBAAAE,EAAC,OAAA,EAAI,WAAU,sCACb,UAAA,gBAAAA;AAAA,YAACS;AAAA,YAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,OAAOhF,KAAcM,EAAE,iCAAiC;AAAA,cACxD,aACEL,MAAoBK,EAAE,uCAAuC;AAAA,YAAA;AAAA,UAAA,GAGnE,IAEA,gBAAAmE;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,KAAKhD;AAAA,cAKL,UAAU;AAAA,cACV,UAAU4B;AAAA,cACV,iBAAetB,KAAU,SAAS;AAAA,cAClC,oBAAkBG,KAAa,SAAS;AAAA,cACxC,WAAWjE;AAAA,cAEV,UAAA;AAAA,gBAAA2B,IAAiB0E,EAAW,iBAAiB,IAAI;AAAA,kCACjD,MAAA,EAAG,WAAU,6EACX,UAAArD,EAAO,IAAI,CAACnC,MACX,gBAAA2F;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBAEC,KAAK,CAACQ,MAAS;AACb,sBAAIA,IAAMtD,EAAU,QAAQ,IAAI7C,EAAM,KAAKmG,CAAI,IAC1CtD,EAAU,QAAQ,OAAO7C,EAAM,GAAG;AAAA,oBACzC;AAAA,oBACA,WAAU;AAAA,oBAEV,UAAA;AAAA,sBAAA,gBAAAyF;AAAA,wBAACW;AAAA,wBAAA;AAAA,0BACC,MAAMpG,EAAM;AAAA,0BACZ,KAAAiC;AAAA,0BACA,QAAQF;AAAA,0BACR,YAAYP,EAAE,2BAA2B;AAAA,0BACzC,eAAeA,EAAE,8BAA8B;AAAA,0BAC/C,gBAAgBA,EAAE,+BAA+B;AAAA,wBAAA;AAAA,sBAAA;AAAA,sBAEnD,gBAAAiE,EAAC,QAAG,WAAU,6EACX,YAAM,MAAM,IAAI,CAAC3F,MAChB,gBAAA2F;AAAA,wBAACY;AAAA,wBAAA;AAAA,0BAEC,MAAAvG;AAAA,0BACA,MAAAU;AAAA,0BACA,SAAQ;AAAA,0BACR,aAAa;AAAA,0BACb,QAAQuB;AAAA,0BACR,UAAAxB;AAAA,0BACA,YAAYE;AAAA,0BACZ,uBAAqBX,EAAK;AAAA,wBAAA;AAAA,wBARrBA,EAAK;AAAA,sBAAA,CAUb,EAAA,CACH;AAAA,oBAAA;AAAA,kBAAA;AAAA,kBA7BKE,EAAM;AAAA,gBAAA,CA+Bd,GACH;AAAA,gBACCe,IAAeyE,EAAW,eAAe,IAAI;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QAChD;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEAvF,GAAoB,cAAc;AAelC,SAASmG,GAAU;AAAA,EACjB,MAAA3B;AAAA,EACA,KAAAxC;AAAA,EACA,QAAA3B;AAAA,EACA,YAAAgG;AAAA,EACA,eAAAC;AAAA,EACA,gBAAAC;AACF,GAA8B;AAC5B,QAAMzG,IAAMX,EAAYqF,CAAI,GACtBlC,IAAWnD,EAAY6C,CAAG,GAC1BwE,IAAW,IAAI,KAAKxE,CAAG;AAC7B,EAAAwE,EAAS,QAAQxE,EAAI,QAAA,IAAY,CAAC;AAClC,QAAMyE,IAAY,IAAI,KAAKzE,CAAG;AAC9B,EAAAyE,EAAU,QAAQzE,EAAI,QAAA,IAAY,CAAC;AAEnC,QAAM0E,IAAU5G,MAAQwC;AAExB,MAAIqE;AACJ,MAAID,EAAS,CAAAC,IAAQN;AAAA,WACZvG,MAAQX,EAAYqH,CAAQ,EAAG,CAAAG,IAAQL;AAAA,WACvCxG,MAAQX,EAAYsH,CAAS,EAAG,CAAAE,IAAQJ;AAAA,OAC5C;AACH,UAAMK,IAAWpC,EAAK,YAAA,MAAkBxC,EAAI,YAAA;AAC5C,IAAA2E,IAAQ,IAAI,KAAK,eAAetG,GAAQ;AAAA,MACtC,SAAS;AAAA,MACT,KAAK;AAAA,MACL,OAAO;AAAA,MACP,GAAIuG,IAAW,CAAA,IAAK,EAAE,MAAM,UAAA;AAAA,IAAU,CACvC,EAAE,OAAOpC,CAAI;AAAA,EAChB;AAKA,SACE,gBAAAkB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,cAAYgB,IAAU,SAAS;AAAA,MAC/B,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACAA,IACI,oDACA;AAAA,MAAA,EACJ,KAAK,GAAG;AAAA,MAET,UAAA;AAAA,QAAAA,IACC,gBAAAlB;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,eAAY;AAAA,YACZ,WAAU;AAAA,UAAA;AAAA,QAAA,IAEV;AAAA,QACHmB;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGP;"}
|
|
1
|
+
{"version":3,"file":"appointment-timeline-CwpbQGC1.js","sources":["../../src/components/appointment-timeline/appointment-timeline.agent.ts","../../src/components/appointment-timeline/appointment-timeline.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — AppointmentTimeline. */\n/* */\n/* Timeline surface: read appointment ids + lifecycle state (never */\n/* patient names / treatments), activate a row through the curated handle, */\n/* and re-anchor the scroll on today. In-page (no open/close). */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { AppointmentTimelineHandle } from './appointment-timeline';\n\nexport const appointmentTimelineAgent: AgentAdapter<AppointmentTimelineHandle> =\n {\n id: 'appointment-timeline',\n // 'pick' backs select_item (non-persisting activation through\n // onOpenAppointment); 'scroll' backs jump_to_today, which re-anchors the\n // scroll viewport (mirrors practice-results' scroll_to_result).\n capabilities: ['scroll', 'pick'],\n state: {\n items: {\n type: 'Array<{ id: string; state: string }>',\n descriptionKey: 'ui.agent.appointmentTimeline.state.items',\n description:\n 'Currently-rendered appointments. Ids + lifecycle state only — never patient identity.',\n read: (handle) => handle.getItems(),\n },\n },\n actions: {\n select_item: {\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: { type: 'string' } },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.appointmentTimeline.actions.selectItem',\n description: 'Activate the appointment with the given id.',\n invoke: (handle, args: { id: string }) => {\n handle.selectItem(args.id);\n },\n },\n jump_to_today: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.appointmentTimeline.actions.jumpToToday',\n description: \"Re-anchor the timeline scroll on today's appointments.\",\n invoke: (handle) => {\n handle.jumpToToday();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'appointment-timeline',\n description: 'Marks the AppointmentTimeline wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description:\n 'Sourced from the id prop. Required to address a specific timeline from the agent.',\n },\n item: {\n attr: 'data-appointment-id',\n description:\n 'Stable appointment id emitted on each rendered AppointmentCard inside the timeline.',\n },\n },\n };\n","import {\n forwardRef,\n useCallback,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { CalendarDays } from 'lucide-react';\nimport { AppointmentCard, type AppointmentItem } from '../appointment-card';\nimport { Button } from '../button';\nimport { ButtonGroup } from '../button-group';\nimport { DatePicker } from '../date-picker';\nimport { PanelHeader } from '../panel-header';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect';\nimport { usePrefersReducedMotion } from '../../hooks/use-prefers-reduced-motion';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { appointmentTimelineAgent } from './appointment-timeline.agent';\n\n/* -------------------------------------------------------------------- */\n/* AppointmentTimeline */\n/* */\n/* A vertical, day-grouped appointment timeline for the Control Room */\n/* rail. Unlike AppointmentTray (a bordered card of a flat list), this */\n/* fills the rail edge-to-edge: appointments are grouped under day */\n/* headers, the scroll is anchored on TODAY, and it scrolls infinitely */\n/* into the past and future — the consumer feeds more via onLoadEarlier */\n/* / onLoadLater as either edge is reached (scroll position is preserved */\n/* when earlier items prepend). Date-filter presets + a jump-to-date */\n/* picker re-anchor the scroll. Top + bottom transparent gradient fades */\n/* (a block-axis mask) signal more content beyond each edge. */\n/* */\n/* Each row reuses AppointmentCard (care-plan + chair chips included). */\n/* All visible text flows through useTranslation('ui') against the */\n/* `appointmentTimeline.*` namespace; no PHI reaches the agent adapter. */\n/* -------------------------------------------------------------------- */\n\n/* How close (px) to an edge before the matching load callback fires. */\nconst EDGE_LOAD_THRESHOLD = 96;\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\n/** Curated imperative handle — identifiers + lifecycle state only. */\nexport interface AppointmentTimelineHandle {\n getItems: () => Array<{ id: string; state: string }>;\n selectItem: (id: string) => void;\n jumpToToday: () => void;\n}\n\nexport interface AppointmentTimelineProps extends Omit<\n HTMLAttributes<HTMLElement>,\n 'onClick' | 'id' | 'title'\n> {\n /** Stable instance id, surfaced as `data-component-id`. */\n id?: string;\n /** Appointments to render — any order; grouped + sorted internally. */\n items: AppointmentItem[];\n /**\n * ISO-8601 \"now\" anchor — the timeline scrolls to this day on mount and the\n * \"Today\" preset returns to it. Defaults to the current instant. Stories pass\n * a fixed value for deterministic snapshots.\n */\n nowIso?: string;\n /** Panel heading. Defaults to the localised \"Appointments\". */\n title?: string;\n /** BCP-47 locale override for day headers + each AppointmentCard. */\n locale?: string;\n /** ISO-4217 currency code passed to each AppointmentCard. */\n currency?: string;\n /** Visual density passed to each AppointmentCard. */\n size?: 'sm' | 'md';\n /** Fires when a row is activated. */\n onOpenAppointment?: (item: AppointmentItem) => void;\n\n /* ----- Infinite scroll ----- */\n /** Load earlier (past) appointments — fired when the top edge is reached. */\n onLoadEarlier?: () => void;\n /** Load later (future) appointments — fired when the bottom edge is reached. */\n onLoadLater?: () => void;\n /** Whether more past appointments exist. Defaults to `true` when `onLoadEarlier` is set. */\n hasMoreEarlier?: boolean;\n /** Whether more future appointments exist. Defaults to `true` when `onLoadLater` is set. */\n hasMoreLater?: boolean;\n /** Show a loading row at the top edge. */\n loadingEarlier?: boolean;\n /** Show a loading row at the bottom edge. */\n loadingLater?: boolean;\n\n /* ----- Filters ----- */\n /** Show the date-filter row (presets + jump-to-date). Default `true`. */\n showFilters?: boolean;\n /**\n * Fired when a jump-to-date / preset targets a day outside the loaded range,\n * so the consumer can load it. The timeline scrolls to the nearest rendered\n * day until then.\n */\n onJumpToDate?: (dateIso: string) => void;\n\n /* ----- Empty ----- */\n emptyTitle?: string;\n emptyDescription?: string;\n\n /** Accessible name for the panel region (defaults to `title`). */\n 'aria-label'?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* CVA */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n [\n 'ds:flex ds:flex-col ds:h-full ds:min-h-0',\n 'ds:bg-[color:var(--card)] ds:text-[color:var(--card-foreground)]',\n ].join(' '),\n { variants: { size: { sm: '', md: '' } }, defaultVariants: { size: 'sm' } },\n);\n\n/* Scroll region with a block-axis fade mask. The CSS custom properties flip to\n `transparent` at whichever edge has more content (data-fade-top /\n data-fade-bottom), mirroring the TabBar edge-mask technique on the Y axis.\n Disabled under reduced-motion, the accessible theme, and forced-colors. */\nconst scrollRegionClasses = [\n 'ds:flex-1 ds:min-h-0 ds:overflow-y-auto',\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]',\n 'ds:px-[var(--spacing-sm)] ds:py-[var(--spacing-xs)]',\n 'ds:[--atl-mask-top:black] ds:[--atl-mask-bottom:black]',\n 'ds:data-[fade-top=true]:[--atl-mask-top:transparent]',\n 'ds:data-[fade-bottom=true]:[--atl-mask-bottom:transparent]',\n 'ds:[mask-image:linear-gradient(to_bottom,var(--atl-mask-top)_0,black_var(--atl-fade-distance,var(--spacing-lg)),black_calc(100%-var(--atl-fade-distance,var(--spacing-lg))),var(--atl-mask-bottom)_100%)]',\n 'ds:motion-reduce:[mask-image:none]',\n 'ds:[.theme-accessible_&]:[mask-image:none]',\n 'ds:forced-colors:[mask-image:none]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:[outline-offset:-2px]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n].join(' ');\n\n/* -------------------------------------------------------------------- */\n/* Day grouping + header labels */\n/* -------------------------------------------------------------------- */\n\ninterface DayGroup {\n key: string;\n date: Date;\n items: AppointmentItem[];\n}\n\n/** Local calendar day key (`YYYY-MM-DD`) — sorts chronologically as a string. */\nfunction localDayKey(d: Date): string {\n const m = `${d.getMonth() + 1}`.padStart(2, '0');\n const day = `${d.getDate()}`.padStart(2, '0');\n return `${d.getFullYear()}-${m}-${day}`;\n}\n\nfunction groupByDay(items: AppointmentItem[]): DayGroup[] {\n const sorted = [...items].sort(\n (a, b) => +new Date(a.startIso) - +new Date(b.startIso),\n );\n const map = new Map<string, DayGroup>();\n for (const item of sorted) {\n const d = new Date(item.startIso);\n const key = localDayKey(d);\n let group = map.get(key);\n if (!group) {\n group = {\n key,\n date: new Date(d.getFullYear(), d.getMonth(), d.getDate()),\n items: [],\n };\n map.set(key, group);\n }\n group.items.push(item);\n }\n return [...map.values()];\n}\n\n/* -------------------------------------------------------------------- */\n/* Component */\n/* -------------------------------------------------------------------- */\n\nexport const AppointmentTimeline = forwardRef<\n HTMLElement,\n AppointmentTimelineProps\n>(\n (\n {\n id,\n items,\n nowIso,\n title,\n locale,\n currency,\n size = 'sm',\n onOpenAppointment,\n onLoadEarlier,\n onLoadLater,\n hasMoreEarlier,\n hasMoreLater,\n loadingEarlier = false,\n loadingLater = false,\n showFilters = true,\n onJumpToDate,\n emptyTitle,\n emptyDescription,\n className,\n 'aria-label': ariaLabel,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const jumpId = useId();\n const prefersReducedMotion = usePrefersReducedMotion();\n const resolvedLocale = locale ?? i18n.language;\n\n const heading = title ?? t('appointmentTimeline.title');\n const now = useMemo(\n () => (nowIso ? new Date(nowIso) : new Date()),\n [nowIso],\n );\n\n const groups = useMemo(() => groupByDay(items), [items]);\n\n const moreEarlier = hasMoreEarlier ?? !!onLoadEarlier;\n const moreLater = hasMoreLater ?? !!onLoadLater;\n\n /* The day to anchor on: today if present, else the nearest upcoming day,\n else the last day we have. */\n const anchorKey = useMemo(() => {\n if (groups.length === 0) return undefined;\n const todayKey = localDayKey(now);\n const keys = groups.map((g) => g.key);\n if (keys.includes(todayKey)) return todayKey;\n return keys.find((k) => k >= todayKey) ?? keys[keys.length - 1];\n }, [groups, now]);\n\n const scrollRef = useRef<HTMLDivElement>(null);\n const dayElRefs = useRef<Map<string, HTMLElement>>(new Map());\n const hasAnchored = useRef(false);\n const prevFirstKey = useRef<string | undefined>(undefined);\n const prevScrollHeight = useRef(0);\n\n const [fadeTop, setFadeTop] = useState(false);\n const [fadeBottom, setFadeBottom] = useState(false);\n\n const updateEdges = useCallback(() => {\n const region = scrollRef.current;\n if (!region) return;\n const { scrollTop, scrollHeight, clientHeight } = region;\n setFadeTop(scrollTop > 1);\n setFadeBottom(scrollTop + clientHeight < scrollHeight - 1);\n }, []);\n\n /* Scroll a day into view at the top of the region. Falls back to the\n nearest rendered day on/after the target (presets may land on an empty\n day). Uses getBoundingClientRect so it is offsetParent-agnostic. */\n const scrollToKey = useCallback(\n (targetKey: string, smooth: boolean) => {\n const region = scrollRef.current;\n if (!region) return;\n let el = dayElRefs.current.get(targetKey);\n if (!el) {\n const keys = [...dayElRefs.current.keys()].sort();\n const next =\n keys.find((k) => k >= targetKey) ?? keys[keys.length - 1];\n el = next ? dayElRefs.current.get(next) : undefined;\n }\n if (!el) return;\n const delta =\n el.getBoundingClientRect().top - region.getBoundingClientRect().top;\n region.scrollTo({\n top: region.scrollTop + delta,\n behavior: smooth && !prefersReducedMotion ? 'smooth' : 'auto',\n });\n },\n [prefersReducedMotion],\n );\n\n /* Anchor on mount; preserve the viewport when earlier days prepend. */\n useIsomorphicLayoutEffect(() => {\n const region = scrollRef.current;\n if (!region) return;\n const firstKey = groups[0]?.key;\n if (!hasAnchored.current) {\n if (groups.length > 0 && anchorKey) {\n scrollToKey(anchorKey, false);\n hasAnchored.current = true;\n }\n } else if (\n prevFirstKey.current &&\n firstKey &&\n firstKey < prevFirstKey.current\n ) {\n const grew = region.scrollHeight - prevScrollHeight.current;\n if (grew > 0) region.scrollTop += grew;\n }\n prevFirstKey.current = firstKey;\n prevScrollHeight.current = region.scrollHeight;\n updateEdges();\n }, [groups, anchorKey, scrollToKey, updateEdges]);\n\n /* Recompute the fades when the region resizes. */\n useIsomorphicLayoutEffect(() => {\n const region = scrollRef.current;\n if (!region || typeof ResizeObserver === 'undefined') return undefined;\n const observer = new ResizeObserver(updateEdges);\n observer.observe(region);\n return () => observer.disconnect();\n }, [updateEdges]);\n\n const handleScroll = useCallback(() => {\n updateEdges();\n const region = scrollRef.current;\n if (!region) return;\n const { scrollTop, scrollHeight, clientHeight } = region;\n if (\n onLoadEarlier &&\n moreEarlier &&\n !loadingEarlier &&\n scrollTop <= EDGE_LOAD_THRESHOLD\n ) {\n onLoadEarlier();\n }\n if (\n onLoadLater &&\n moreLater &&\n !loadingLater &&\n scrollHeight - scrollTop - clientHeight <= EDGE_LOAD_THRESHOLD\n ) {\n onLoadLater();\n }\n }, [\n updateEdges,\n onLoadEarlier,\n onLoadLater,\n moreEarlier,\n moreLater,\n loadingEarlier,\n loadingLater,\n ]);\n\n /* ----- Filter actions ----- */\n const jumpTo = useCallback(\n (date: Date) => {\n const key = localDayKey(date);\n const known = dayElRefs.current.has(key);\n scrollToKey(key, true);\n if (!known) onJumpToDate?.(date.toISOString());\n },\n [scrollToKey, onJumpToDate],\n );\n\n const handleToday = useCallback(() => jumpTo(now), [jumpTo, now]);\n const handleThisWeek = useCallback(() => {\n const start = new Date(now);\n const dow = (start.getDay() + 6) % 7; // Monday-based week start\n start.setDate(start.getDate() - dow);\n jumpTo(start);\n }, [jumpTo, now]);\n const handleThisMonth = useCallback(() => {\n jumpTo(new Date(now.getFullYear(), now.getMonth(), 1));\n }, [jumpTo, now]);\n\n /* ----- Agent handle ----- */\n const itemsRef = useRef<AppointmentItem[]>(items);\n itemsRef.current = items;\n const innerRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => innerRef.current as HTMLElement, []);\n\n const handle = useMemo<AppointmentTimelineHandle>(\n () => ({\n getItems: () =>\n itemsRef.current.map((i) => ({ id: i.id, state: i.state })),\n selectItem: (targetId: string) => {\n const found = itemsRef.current.find((i) => i.id === targetId);\n if (found) onOpenAppointment?.(found);\n },\n jumpToToday: () => scrollToKey(localDayKey(now), true),\n }),\n [onOpenAppointment, scrollToKey, now],\n );\n useAgentRegistration(appointmentTimelineAgent, handle, id);\n\n const isEmpty = groups.length === 0 && !loadingEarlier && !loadingLater;\n\n const loadingRow = (key: string) => (\n <div\n key={key}\n aria-hidden=\"true\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)] ds:py-[var(--spacing-xs)]\"\n >\n <Skeleton variant=\"rectangular\" height=\"3.5rem\" />\n </div>\n );\n\n return (\n <section\n ref={innerRef}\n data-component=\"appointment-timeline\"\n data-component-id={id}\n aria-label={ariaLabel ?? heading}\n className={rootVariants({ size, className })}\n {...rest}\n >\n {/* Shared rail header: title + (optional) filter row + divider. This\n panel owns its own scroll/fill, so the header stays a fixed band\n (sticky={false}). The rail injects the favourite/close cluster via\n PanelHeaderActionsProvider; standalone it renders none. */}\n <PanelHeader\n icon={<CalendarDays />}\n title={heading}\n filters={\n showFilters ? (\n <>\n <ButtonGroup label={t('appointmentTimeline.filtersLabel')}>\n <Button intent=\"outline\" size=\"sm\" onClick={handleToday}>\n {t('appointmentTimeline.today')}\n </Button>\n <Button intent=\"outline\" size=\"sm\" onClick={handleThisWeek}>\n {t('appointmentTimeline.thisWeek')}\n </Button>\n <Button intent=\"outline\" size=\"sm\" onClick={handleThisMonth}>\n {t('appointmentTimeline.thisMonth')}\n </Button>\n </ButtonGroup>\n <label htmlFor={jumpId} className=\"ds:sr-only\">\n {t('appointmentTimeline.jumpToDate')}\n </label>\n <DatePicker\n id={jumpId}\n size=\"sm\"\n placeholder={t('appointmentTimeline.jumpToDate')}\n onChange={(date) => {\n if (date) jumpTo(date);\n }}\n />\n </>\n ) : undefined\n }\n />\n\n {isEmpty ? (\n <div className=\"ds:flex-1 ds:p-[var(--spacing-md)]\">\n <EmptyState\n variant=\"first-use\"\n size=\"sm\"\n title={emptyTitle ?? t('appointmentTimeline.empty.title')}\n description={\n emptyDescription ?? t('appointmentTimeline.empty.description')\n }\n />\n </div>\n ) : (\n <div\n ref={scrollRef}\n // The scroll region must be keyboard-focusable so arrow / Home /\n // End can drive it (axe scrollable-region-focusable); jsx-a11y's\n // generic rule doesn't know this element scrolls.\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n tabIndex={0}\n onScroll={handleScroll}\n data-fade-top={fadeTop ? 'true' : undefined}\n data-fade-bottom={fadeBottom ? 'true' : undefined}\n className={scrollRegionClasses}\n >\n {loadingEarlier ? loadingRow('loading-earlier') : null}\n <ol className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-md)] ds:p-0\">\n {groups.map((group) => (\n <li\n key={group.key}\n ref={(node) => {\n if (node) dayElRefs.current.set(group.key, node);\n else dayElRefs.current.delete(group.key);\n }}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n <DayHeader\n date={group.date}\n now={now}\n locale={resolvedLocale}\n todayLabel={t('appointmentTimeline.today')}\n tomorrowLabel={t('appointmentTimeline.tomorrow')}\n yesterdayLabel={t('appointmentTimeline.yesterday')}\n />\n <ul className=\"ds:m-0 ds:flex ds:list-none ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-0\">\n {group.items.map((item) => (\n <AppointmentCard\n key={item.id}\n item={item}\n size={size}\n surface=\"elevated\"\n showPatient={false}\n locale={resolvedLocale}\n currency={currency}\n onActivate={onOpenAppointment}\n data-appointment-id={item.id}\n />\n ))}\n </ul>\n </li>\n ))}\n </ol>\n {loadingLater ? loadingRow('loading-later') : null}\n </div>\n )}\n </section>\n );\n },\n);\n\nAppointmentTimeline.displayName = 'AppointmentTimeline';\n\n/* -------------------------------------------------------------------- */\n/* Day header — sticky label per day group */\n/* -------------------------------------------------------------------- */\n\ninterface DayHeaderProps {\n date: Date;\n now: Date;\n locale: string | undefined;\n todayLabel: string;\n tomorrowLabel: string;\n yesterdayLabel: string;\n}\n\nfunction DayHeader({\n date,\n now,\n locale,\n todayLabel,\n tomorrowLabel,\n yesterdayLabel,\n}: DayHeaderProps): ReactNode {\n const key = localDayKey(date);\n const todayKey = localDayKey(now);\n const tomorrow = new Date(now);\n tomorrow.setDate(now.getDate() + 1);\n const yesterday = new Date(now);\n yesterday.setDate(now.getDate() - 1);\n\n const isToday = key === todayKey;\n\n let label: string;\n if (isToday) label = todayLabel;\n else if (key === localDayKey(tomorrow)) label = tomorrowLabel;\n else if (key === localDayKey(yesterday)) label = yesterdayLabel;\n else {\n const sameYear = date.getFullYear() === now.getFullYear();\n label = new Intl.DateTimeFormat(locale, {\n weekday: 'short',\n day: 'numeric',\n month: 'short',\n ...(sameYear ? {} : { year: 'numeric' }),\n }).format(date);\n }\n\n // Today's group is marked with the brand colour + a leading dot so the\n // current day reads at a glance. `--primary` clears AA on the card in every\n // theme (violet-500 light, magenta on dark); the dot is decorative.\n return (\n <div\n data-today={isToday ? 'true' : undefined}\n className={[\n 'ds:sticky ds:top-0 ds:z-[1] ds:bg-[color:var(--card)] ds:py-[var(--spacing-2xs)]',\n 'ds:flex ds:items-center ds:gap-[var(--spacing-2xs)] type-eyebrow',\n isToday\n ? 'ds:text-[color:var(--primary)] ds:font-semibold'\n : 'ds:text-[color:var(--muted-foreground)]',\n ].join(' ')}\n >\n {isToday ? (\n <span\n aria-hidden=\"true\"\n className=\"ds:size-1.5 ds:shrink-0 ds:rounded-[var(--radius-full)] ds:bg-[color:var(--primary)]\"\n />\n ) : null}\n {label}\n </div>\n );\n}\n\n/** Re-export of the size variant union for downstream typing. */\nexport type AppointmentTimelineSize = NonNullable<\n VariantProps<typeof rootVariants>['size']\n>;\n"],"names":["appointmentTimelineAgent","handle","args","EDGE_LOAD_THRESHOLD","rootVariants","cva","scrollRegionClasses","localDayKey","d","m","day","groupByDay","items","sorted","a","b","map","item","key","group","AppointmentTimeline","forwardRef","id","nowIso","title","locale","currency","size","onOpenAppointment","onLoadEarlier","onLoadLater","hasMoreEarlier","hasMoreLater","loadingEarlier","loadingLater","showFilters","onJumpToDate","emptyTitle","emptyDescription","className","ariaLabel","rest","ref","t","i18n","useTranslation","jumpId","useId","prefersReducedMotion","usePrefersReducedMotion","resolvedLocale","heading","now","useMemo","groups","moreEarlier","moreLater","anchorKey","todayKey","keys","g","k","scrollRef","useRef","dayElRefs","hasAnchored","prevFirstKey","prevScrollHeight","fadeTop","setFadeTop","useState","fadeBottom","setFadeBottom","updateEdges","useCallback","region","scrollTop","scrollHeight","clientHeight","scrollToKey","targetKey","smooth","el","next","delta","useIsomorphicLayoutEffect","firstKey","_a","grew","observer","handleScroll","jumpTo","date","known","handleToday","handleThisWeek","start","dow","handleThisMonth","itemsRef","innerRef","useImperativeHandle","i","targetId","found","useAgentRegistration","isEmpty","loadingRow","jsx","Skeleton","jsxs","PanelHeader","CalendarDays","Fragment","ButtonGroup","Button","DatePicker","EmptyState","node","DayHeader","AppointmentCard","todayLabel","tomorrowLabel","yesterdayLabel","tomorrow","yesterday","isToday","label","sameYear"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as s, q as r, r as i, v as d } from "./date-picker-variants-
|
|
1
|
+
import { o as s, q as r, r as i, v as d } from "./date-picker-variants-D9B6hg15.js";
|
|
2
2
|
const h = {
|
|
3
3
|
lessThanXSeconds: {
|
|
4
4
|
one: "أقل من ثانية",
|
|
@@ -396,4 +396,4 @@ const h = {
|
|
|
396
396
|
export {
|
|
397
397
|
z as ar
|
|
398
398
|
};
|
|
399
|
-
//# sourceMappingURL=ar-
|
|
399
|
+
//# sourceMappingURL=ar-C9FRRqJs.js.map
|