/** The three host contexts the one pattern renders in. */ /** `questionnaire` is the contained platform surface: ONE questionnaire for * clinician and patient alike, plus the trend over its numeric answers. It * deliberately carries none of `compile`'s bespoke measurement blocks — a * practice that needs the two roles asked different things makes two forms. */ export type AnamnesisMode = 'intake' | 'compile' | 'questionnaire' | 'readOnly'; /** Recorded biological sex — gates the menstrual-cycle / pregnancy fields. */ export type PatientSex = 'female' | 'male' | 'unspecified'; /** Who authored a value. Patient-submitted values render a "from patient" * origin marker in compile mode until the clinician confirms or edits them, * at which point the origin flips to `clinician` and the marker drops. */ export type AnswerOrigin = 'patient' | 'clinician'; /** First visit vs any subsequent follow-up. */ export type VisitType = 'first' | 'followUp'; /** Structured yes/no answer (with optional free-text detail alongside). */ export type YesNo = 'yes' | 'no' | 'unknown'; /** A value carrying its provenance. */ export interface Sourced { value: T; origin: AnswerOrigin; } /** The fields every practice-configurable descriptor carries — numeric * parameters, intake fields, intake sections and pathology codes. * * PRESENTATION (`label` / `helpText` / `order`) is always editable, including * on a locked descriptor. EXISTENCE (`enabled`) is not: archiving a locked key * would break the derivation engine. Resolvers live in `anamnesis-config.ts`; * nothing here enforces the rule on its own. */ export interface ConfigurableDescriptor { /** Practice-authored label. When present it wins over the i18n key derived * from the descriptor's `key`/`code` — the same exemption * `PatientCustomFieldDef.label` carries: a practice's own wording is data, * not a design-system string. */ label?: string; /** Practice-authored help text rendered beside the control. */ helpText?: string; /** `false` ARCHIVES the descriptor: it stops being offered for capture but * still renders in history, so answers already recorded under it survive. * Defaults to `true`. Never honoured on a locked descriptor. */ enabled?: boolean; /** The derivation engine depends on this key: relabel and reorder freely, * but never archive, rename or delete it. Stamped by the resolvers from * `LOCKED_PARAMETER_KEYS` — do not hand-set it on seeded data. */ locked?: boolean; /** Sort position within the descriptor's own group or section. Lower first; * descriptors without one keep their seeded position. */ order?: number; } export interface PatientHeader { /** Given name. Placeholder data only — never a real patient. */ firstName: string; lastName: string; /** ISO-8601 date of birth. Age is derived, never stored. */ dateOfBirthIso: string; sex: PatientSex; } /** Discriminated union of every answer shape the intake form can hold. * The field registry (INTAKE_SECTIONS) declares which `kind` each field * uses; the renderer switches on `kind`. Keeping the union small keeps the * form strict without `any`. */ export type IntakeAnswer = { kind: 'text'; text: string; } | { kind: 'number'; value: number | null; unit?: string; } | { kind: 'yesNo'; present: YesNo; detail?: string; } | { kind: 'choice'; selected: string | null; detail?: string; } | { kind: 'multiChoice'; selected: string[]; } | { kind: 'frequency'; perWeek: number | null; detail?: string; } | { kind: 'meal'; time: string; description: string; }; export type IntakeAnswerKind = IntakeAnswer['kind']; /** Field keys used across the intake registry, the record, and i18n keys. * Kept as a string alias (not a closed enum) so the demo stays extensible. */ export type IntakeFieldKey = string; /** A patient's self-completed pre-visit questionnaire. Each answer is * `Sourced` so compile mode can mark which values still come straight from * the patient. In `intake` mode every origin is `patient`. */ export interface IntakeResponse { /** ISO-8601 submission timestamp; absent while still a draft. */ submittedAtIso?: string; answers: Partial>>; } /** How an intake field is captured. Drives which primitive renders. * `choice` is single-select (one answer); `multiChoice` is multi-select * (any number of the options); `yesNoDetail` is the binary choice. */ export type IntakeFieldType = 'text' | 'textarea' | 'number' | 'yesNoDetail' | 'choice' | 'multiChoice' | 'frequency' | 'meal'; /** Only show a field when it is relevant to the patient's recorded sex. */ export type SexRelevance = PatientSex | 'any'; export interface IntakeFieldDef extends ConfigurableDescriptor { key: IntakeFieldKey; type: IntakeFieldType; /** Display unit for `number` fields (kg, cm, glasses/day, cups/day…). */ unit?: string; /** `number` fields: capture with decimals instead of integer-only. The * kit-seeded decimal fields keep working through the legacy key set; this * flag is how a practice-added field opts in. */ decimal?: boolean; /** Choice options for `choice` / `multiChoice` fields — i18n key suffixes * for kit-seeded fields, verbatim practice wording for practice-added * ones. */ options?: string[]; /** Optional free-text detail beside a yes/no, frequency or single-choice * answer (the platform's `_with_text` question variants). */ detail?: boolean; /** Restrict visibility by recorded sex (menstrual cycle, pregnancies). */ relevance?: SexRelevance; /** The answer is mandatory: the missing-answers alert counts it and the * compile gaps block asks for it. Practice-configurable like `enabled` * and `order` — replaces the old kit-constant mandatory set. */ required?: boolean; /** Which answers the practice wants flagged for the clinician's attention * (the platform's per-question highlight rule). Omitted = never. See * `isHighlightedAnswer` for the evaluation. */ highlight?: IntakeHighlightRule; /** A `text` answer the practice expects prose in: it takes the whole row * like a `textarea` instead of sharing it with the questions beside it. */ longForm?: boolean; } /** `always` flags any non-blank answer; the other two flag a yes/no question * answered that way. */ export type IntakeHighlightRule = 'always' | 'whenYes' | 'whenNo'; export interface IntakeSectionDef extends ConfigurableDescriptor { key: string; fields: IntakeFieldDef[]; } /** Anthropometric measurements captured per visit. BMI is DERIVED from * weight + height and never stored. */ export interface AnthropometricSet { /** Body weight in kilograms. */ weightKg: number | null; /** Standing height in centimetres. Usually constant across visits. */ heightCm: number | null; waistCm: number | null; hipsCm: number | null; armCm: number | null; /** Bioimpedance phase angle in degrees — higher is healthier. */ phaseAngleDeg: number | null; } /** The direction of change that counts as an improvement for a numeric * parameter. `toRange` = moving into the reference range is good (the sign * of the delta alone cannot say whether it is good or bad). */ export type BetterDirection = 'down' | 'up' | 'toRange'; /** Which numeric family a parameter belongs to (drives grouping + which visit * block edits it). `dietary` is what the patient EATS — daily energy and * protein — and carries its own storage array on the visit. */ export type ParameterGroup = 'anthropometric' | 'lab' | 'dietary'; /** Definition of a single numeric parameter shown as a row in the evolution * grid. The catalogue of these is module-level config, not per-patient. */ export interface NumericParameterDef extends ConfigurableDescriptor { key: string; group: ParameterGroup; /** Display unit (kg, cm, mg/dL, %, °…). */ unit?: string; /** Reference range for out-of-range detection (labs, some anthropometrics). */ referenceRange?: { min?: number; max?: number; }; /** Which direction reads as improvement (drives delta valence). */ betterDirection: BetterDirection; /** Decimal places for display + delta formatting. */ precision?: number; /** Derived from other fields (BMI) — never entered by hand. */ computed?: boolean; /** Rides the goals strip above the clinician record: the practice CHOOSES * its tracked goals, sets each target on the tile, and reads * current-vs-target status there. Seeded `true` for the plan's four * figures; toggleable per practice like `enabled` and `order`. */ goal?: boolean; /** A per-patient target may be set for this parameter — on its tile in the * goals strip when `goal` is also true, otherwise from its capture block. * Not every number is one: a height is a fact about the patient, and a * blood test carries a population reference range rather than a target. * The kit seeds it on weight, BMI, waist, daily energy and protein; a host * sets it (or `goal`) on any numeric question a practice marks as a goal. * A tracked goal is targetable whether or not this is set. */ targetable?: boolean; } /** One numeric parameter's value at one visit, stored by key. The reference * range + out-of-range flag are derived by joining to the parameter definition, * so only the raw value lives here. */ export interface MeasurementResult { parameterKey: string; value: number | null; } /** A lab result value for one parameter at one visit. */ export type LabResult = MeasurementResult; /** A dietary-intake value (daily energy, protein) for one visit. Its own type * because it is not a lab result: `visit.labs` is what the Blood tests block * reads, and intake does not belong in that drawer. */ export type DietaryIntakeResult = MeasurementResult; /** Definition of a lab parameter — a NumericParameterDef pinned to the `lab` * group. Aliased for the name the brief asks for. */ export type LabParameterDefinition = NumericParameterDef & { group: 'lab'; }; /** A per-patient numeric target (e.g. target weight / target BMI), read * against the current value as well as against the previous visit. ONE * CONCEPT: the care plan's "Nutrition targets" tiles read this same goal, and * the anamnesis is its authoritative, editable side. No story shares live * state, so reconciling the two surfaces is a platform ticket. */ export interface ParameterTarget { parameterKey: string; target: number; } /** A pathology code in the (mock) taxonomy. Many-to-many with patients: a * patient can hold several at once, and the assigned set can change per visit. * `parentCode` present ⇒ this is a sub-code of a broader family. */ export interface PathologyCode extends ConfigurableDescriptor { code: string; /** Parent family code, when this is a sub-code. */ parentCode?: string; } /** Oncological history block (clinician-only). */ export interface OncologicalHistory { tumourType?: string; stage?: string; metastases?: { present: YesNo; site?: string; }; treatmentStatus?: string; } /** A single medication or supplement entry. Compare mode detects entries * added / removed / changed between two visits. */ export interface MedicationEntry { /** Stable id so compare can match the same entry across visits. */ id: string; name: string; dose?: string; kind: 'medication' | 'supplement'; } /** What kind of thing a clinical event was. One category per available * marker shape — the shape is the accessible channel, so the vocabulary is * closed at five. */ export type ClinicalEventCategory = 'therapy' | 'diet' | 'fmd' | 'surgery' | 'lifestyle'; /** Something that happened to the patient on a date, recorded against the * RECORD rather than against a visit — it need not have happened at one, and * need not have been this practice's own decision. */ export interface ClinicalEvent { /** Stable id — the toggle key and the sparkline annotation key. */ id: string; /** ISO-8601 calendar date. */ dateIso: string; category: ClinicalEventCategory; /** i18n key suffix under `anamnesis.event.label.*`. */ labelKey: string; /** i18n key suffix under `anamnesis.event.source.*`. Present ⇒ recorded by * a professional OUTSIDE this practice, so the practice owns the date but * not the decision. */ sourceKey?: string; } /** One cycle of a fasting-mimicking programme. */ export interface FmdCycle { cycleNumber: number; startDateIso: string; completed: boolean; notes?: string; } /** The annual-programme proposal block. */ export interface AnnualProgramme { proposed: boolean; accepted: boolean; /** Reason recorded when the programme was declined. */ declineReason?: string; } /** One selectable practitioner — the practice's own operators, supplied by * the host (the kit has no roster of its own). `value` is what lands in * `Visit.practitioner`, so it is the operator's display name, not an id: * the visit record carries a name, and the export reads it verbatim. */ export interface PractitionerOption { value: string; label: string; } /** Everything captured at a single visit. Anthropometrics + labs feed the * evolution grid; the non-numeric blocks feed compare mode. */ export interface Visit { /** Stable id (used as the grid column key + compare selection). */ id: string; visitType: VisitType; dateIso: string; /** Practitioner display name (placeholder only). */ practitioner?: string; /** Where this visit's numbers came from, when a record draws on more than * one source — the platform's module ("Esami del sangue"). Rendered under * the date in every column header, so two same-day columns from different * modules are told apart by more than their minute. */ sourceLabel?: string; anthropometrics: AnthropometricSet; labs: LabResult[]; /** Estimated daily intake at this visit (energy, protein). Optional: a * record migrated before intake was tracked has none, and absent reads as * "not measured" rather than as zero. */ dietaryIntake?: DietaryIntakeResult[]; /** Pathology codes assigned as of this visit (many-to-many). */ pathologyCodes: string[]; oncological?: OncologicalHistory; therapy: MedicationEntry[]; fmdCycles: FmdCycle[]; /** Free-text nutritional proposal for this visit. */ nutritionalProposal?: string; /** Free-text clinical notes. */ clinicalNotes?: string; annualProgramme?: AnnualProgramme; } /** The complete anamnesis record for one patient. `visits` is chronological * (oldest first); the newest visit is the editable one in compile mode. */ export interface AnamnesisRecord { patient: PatientHeader; /** The pre-visit self-completed questionnaire, when one was submitted. */ intake?: IntakeResponse; visits: Visit[]; targets: ParameterTarget[]; /** Dated things that happened between or outside the visits, drawn as * reference lines down the evolution trends. Absent = none recorded. */ events?: ClinicalEvent[]; } /** * What the reader asked to read, handed OUT of the pattern so the host's side * panel can navigate to it. An address, not a payload: the host already holds * the record, so an id is all it needs to find the note. */ export type AnamnesisNoteTarget = { kind: 'visit'; visitId: string; } | { kind: 'event'; eventId: string; }; /** * The host's side-panel router. Wire it to whatever the host uses to open a * panel (in the AlfaDocs shell, the Control Room's imperative `openTool`). * Absent = there is no panel to route to, and every surface that would route * falls back to reading in place. * * TWO OBLIGATIONS the host takes on by wiring this, because the pattern * cannot keep them from its side: * 1. Actually render a panel. Wired but panel-less leaves a live control * that does nothing — worse than the in-place fallback it replaced. * 2. Move focus to the addressed entry, or announce it. `openTool` selects * a tool and nothing more: it moves no focus and says nothing, so a * keyboard or screen-reader reader gets no perceivable answer to a * control whose name promises one. Feed `anamnesisNoteId(target)` to the * kit `NotesPanel`'s `currentNoteId` — it keeps this half. * 3. Offer a keyboard-reachable entry into the notes view itself. The * sparkline annotation bands that route events here are pointer-only by * design, and the event chips are pure line toggles — so without a host * affordance (a Notes button, a rail tool) a keyboard reader has no way * into the routed timeline. */ export type OpenAnamnesisNote = (target: AnamnesisNoteTarget) => void; //# sourceMappingURL=anamnesis-types.d.ts.map