@alfadocs/ui-kit-debug 0.31.4 → 0.31.7
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/{agenda-card-UJA6Arbs.js → agenda-card-DtGlQde1.js} +2 -2
- package/dist/_chunks/{agenda-card-UJA6Arbs.js.map → agenda-card-DtGlQde1.js.map} +1 -1
- package/dist/_chunks/{agenda-tray-1fAxNOMz.js → agenda-tray-CXmlwt2K.js} +2 -2
- package/dist/_chunks/{agenda-tray-1fAxNOMz.js.map → agenda-tray-CXmlwt2K.js.map} +1 -1
- package/dist/_chunks/{badge-CptERaHx.js → badge-B9Cr6iEB.js} +30 -21
- package/dist/_chunks/badge-B9Cr6iEB.js.map +1 -0
- package/dist/_chunks/{benefit-card-CLPNpiQE.js → benefit-card-B86DH-PE.js} +14 -10
- package/dist/_chunks/benefit-card-B86DH-PE.js.map +1 -0
- package/dist/_chunks/{card-DKTMLVrw.js → card-CNri9ssR.js} +43 -37
- package/dist/_chunks/card-CNri9ssR.js.map +1 -0
- package/dist/_chunks/{contact-card-DYbp--s-.js → contact-card-Dos7Tley.js} +2 -2
- package/dist/_chunks/{contact-card-DYbp--s-.js.map → contact-card-Dos7Tley.js.map} +1 -1
- package/dist/_chunks/{editable-currency-cell-renderer-BpicmolK.js → editable-currency-cell-renderer-YvTwkFrD.js} +2 -2
- package/dist/_chunks/{editable-currency-cell-renderer-BpicmolK.js.map → editable-currency-cell-renderer-YvTwkFrD.js.map} +1 -1
- package/dist/_chunks/key-value-pair-CYE7NSpM.js +134 -0
- package/dist/_chunks/key-value-pair-CYE7NSpM.js.map +1 -0
- package/dist/_chunks/message-circle-C23eicgb.js +20 -0
- package/dist/_chunks/message-circle-C23eicgb.js.map +1 -0
- package/dist/_chunks/operator-hero-BsjE-kJF.js +171 -0
- package/dist/_chunks/operator-hero-BsjE-kJF.js.map +1 -0
- package/dist/_chunks/{patient-search-hFiYbqcl.js → patient-search-CSDru7QW.js} +2 -2
- package/dist/_chunks/{patient-search-hFiYbqcl.js.map → patient-search-CSDru7QW.js.map} +1 -1
- package/dist/_chunks/practice-profile-card-DwZo7ggW.js +305 -0
- package/dist/_chunks/practice-profile-card-DwZo7ggW.js.map +1 -0
- package/dist/_chunks/{practice-results-dOlljaAK.js → practice-results-CIkAdwRm.js} +552 -592
- package/dist/_chunks/practice-results-CIkAdwRm.js.map +1 -0
- package/dist/_chunks/{reviews-panel-yanuBZs-.js → reviews-panel-Cjys8G8K.js} +2 -2
- package/dist/_chunks/{reviews-panel-yanuBZs-.js.map → reviews-panel-Cjys8G8K.js.map} +1 -1
- package/dist/_chunks/shield-check-BhHyReu8.js +52 -0
- package/dist/_chunks/shield-check-BhHyReu8.js.map +1 -0
- package/dist/_chunks/{timeline-D0Wo7v_o.js → timeline-BZC7qGdy.js} +2 -2
- package/dist/_chunks/{timeline-D0Wo7v_o.js.map → timeline-BZC7qGdy.js.map} +1 -1
- package/dist/_chunks/{workflow-map-BSvQS3be.js → workflow-map-DfpjDZHK.js} +93 -107
- package/dist/_chunks/workflow-map-DfpjDZHK.js.map +1 -0
- package/dist/agent-catalog.json +55 -1
- package/dist/components/agenda-card/index.js +1 -1
- package/dist/components/agenda-tray/index.js +1 -1
- package/dist/components/badge/badge.d.ts +1 -1
- package/dist/components/badge/badge.d.ts.map +1 -1
- package/dist/components/badge/index.js +1 -1
- package/dist/components/benefit-card/benefit-card.d.ts.map +1 -1
- package/dist/components/benefit-card/index.js +1 -1
- package/dist/components/card/card.d.ts.map +1 -1
- package/dist/components/card/index.js +1 -1
- package/dist/components/contact-card/index.js +1 -1
- package/dist/components/data-table/index.js +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/key-value-pair/index.d.ts +2 -2
- package/dist/components/key-value-pair/index.d.ts.map +1 -1
- package/dist/components/key-value-pair/index.js +3 -2
- package/dist/components/key-value-pair/key-value-pair.d.ts +10 -0
- package/dist/components/key-value-pair/key-value-pair.d.ts.map +1 -1
- package/dist/components/operator-hero/index.d.ts +4 -0
- package/dist/components/operator-hero/index.d.ts.map +1 -0
- package/dist/components/operator-hero/index.js +6 -0
- package/dist/components/operator-hero/index.js.map +1 -0
- package/dist/components/operator-hero/operator-hero.agent.d.ts +4 -0
- package/dist/components/operator-hero/operator-hero.agent.d.ts.map +1 -0
- package/dist/components/operator-hero/operator-hero.d.ts +64 -0
- package/dist/components/operator-hero/operator-hero.d.ts.map +1 -0
- package/dist/components/patient-search/index.js +1 -1
- package/dist/components/practice-profile-card/index.d.ts +4 -0
- package/dist/components/practice-profile-card/index.d.ts.map +1 -0
- package/dist/components/practice-profile-card/index.js +8 -0
- package/dist/components/practice-profile-card/index.js.map +1 -0
- package/dist/components/practice-profile-card/practice-profile-card.agent.d.ts +4 -0
- package/dist/components/practice-profile-card/practice-profile-card.agent.d.ts.map +1 -0
- package/dist/components/practice-profile-card/practice-profile-card.d.ts +128 -0
- package/dist/components/practice-profile-card/practice-profile-card.d.ts.map +1 -0
- package/dist/components/practice-results/index.js +1 -1
- package/dist/components/practice-results/practice-results.d.ts.map +1 -1
- package/dist/components/reviews-panel/index.js +1 -1
- package/dist/components/timeline/index.js +1 -1
- package/dist/components/workflow/index.js +1 -1
- package/dist/i18n/locales/ar.d.ts +43 -0
- package/dist/i18n/locales/ar.d.ts.map +1 -1
- package/dist/i18n/locales/ar.js +47 -0
- package/dist/i18n/locales/ar.js.map +1 -1
- package/dist/i18n/locales/de.d.ts +43 -0
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/de.js +47 -0
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/el.d.ts +43 -0
- package/dist/i18n/locales/el.d.ts.map +1 -1
- package/dist/i18n/locales/el.js +47 -0
- package/dist/i18n/locales/el.js.map +1 -1
- package/dist/i18n/locales/en.d.ts +43 -0
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +47 -0
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +43 -0
- package/dist/i18n/locales/es.d.ts.map +1 -1
- package/dist/i18n/locales/es.js +47 -0
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +43 -0
- package/dist/i18n/locales/fr.d.ts.map +1 -1
- package/dist/i18n/locales/fr.js +47 -0
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hi.d.ts +43 -0
- package/dist/i18n/locales/hi.d.ts.map +1 -1
- package/dist/i18n/locales/hi.js +47 -0
- package/dist/i18n/locales/hi.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +43 -0
- package/dist/i18n/locales/it.d.ts.map +1 -1
- package/dist/i18n/locales/it.js +47 -0
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/ja.d.ts +43 -0
- package/dist/i18n/locales/ja.d.ts.map +1 -1
- package/dist/i18n/locales/ja.js +47 -0
- package/dist/i18n/locales/ja.js.map +1 -1
- package/dist/i18n/locales/nl.d.ts +43 -0
- package/dist/i18n/locales/nl.d.ts.map +1 -1
- package/dist/i18n/locales/nl.js +47 -0
- package/dist/i18n/locales/nl.js.map +1 -1
- package/dist/i18n/locales/pl.d.ts +43 -0
- package/dist/i18n/locales/pl.d.ts.map +1 -1
- package/dist/i18n/locales/pl.js +47 -0
- package/dist/i18n/locales/pl.js.map +1 -1
- package/dist/i18n/locales/pt.d.ts +43 -0
- package/dist/i18n/locales/pt.d.ts.map +1 -1
- package/dist/i18n/locales/pt.js +47 -0
- package/dist/i18n/locales/pt.js.map +1 -1
- package/dist/i18n/locales/ro.d.ts +43 -0
- package/dist/i18n/locales/ro.d.ts.map +1 -1
- package/dist/i18n/locales/ro.js +47 -0
- package/dist/i18n/locales/ro.js.map +1 -1
- package/dist/i18n/locales/ru.d.ts +43 -0
- package/dist/i18n/locales/ru.d.ts.map +1 -1
- package/dist/i18n/locales/ru.js +47 -0
- package/dist/i18n/locales/ru.js.map +1 -1
- package/dist/i18n/locales/sq.d.ts +43 -0
- package/dist/i18n/locales/sq.d.ts.map +1 -1
- package/dist/i18n/locales/sq.js +47 -0
- package/dist/i18n/locales/sq.js.map +1 -1
- package/dist/i18n/locales/sv.d.ts +43 -0
- package/dist/i18n/locales/sv.d.ts.map +1 -1
- package/dist/i18n/locales/sv.js +47 -0
- package/dist/i18n/locales/sv.js.map +1 -1
- package/dist/i18n/locales/tr.d.ts +43 -0
- package/dist/i18n/locales/tr.d.ts.map +1 -1
- package/dist/i18n/locales/tr.js +47 -0
- package/dist/i18n/locales/tr.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +43 -0
- package/dist/i18n/locales/zh.d.ts.map +1 -1
- package/dist/i18n/locales/zh.js +47 -0
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/index.js +400 -391
- package/dist/index.js.map +1 -1
- package/dist/locales/ar.json +43 -0
- package/dist/locales/de.json +43 -0
- package/dist/locales/el.json +43 -0
- package/dist/locales/en.json +43 -0
- package/dist/locales/es.json +43 -0
- package/dist/locales/fr.json +43 -0
- package/dist/locales/hi.json +43 -0
- package/dist/locales/it.json +43 -0
- package/dist/locales/ja.json +43 -0
- package/dist/locales/nl.json +43 -0
- package/dist/locales/pl.json +43 -0
- package/dist/locales/pt.json +43 -0
- package/dist/locales/ro.json +43 -0
- package/dist/locales/ru.json +43 -0
- package/dist/locales/sq.json +43 -0
- package/dist/locales/sv.json +43 -0
- package/dist/locales/tr.json +43 -0
- package/dist/locales/zh.json +43 -0
- package/dist/tokens.css +1 -1
- package/package.json +9 -1
- package/dist/_chunks/badge-CptERaHx.js.map +0 -1
- package/dist/_chunks/benefit-card-CLPNpiQE.js.map +0 -1
- package/dist/_chunks/card-DKTMLVrw.js.map +0 -1
- package/dist/_chunks/key-value-pair-C8DaR3Xs.js +0 -98
- package/dist/_chunks/key-value-pair-C8DaR3Xs.js.map +0 -1
- package/dist/_chunks/practice-results-dOlljaAK.js.map +0 -1
- package/dist/_chunks/workflow-map-BSvQS3be.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patient-search-hFiYbqcl.js","sources":["../../node_modules/lucide-react/dist/esm/icons/locate-fixed.js","../../src/components/patient-search/patient-search.agent.ts","../../src/components/patient-search/patient-search.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"line\", { x1: \"2\", x2: \"5\", y1: \"12\", y2: \"12\", key: \"bvdh0s\" }],\n [\"line\", { x1: \"19\", x2: \"22\", y1: \"12\", y2: \"12\", key: \"1tbv5k\" }],\n [\"line\", { x1: \"12\", x2: \"12\", y1: \"2\", y2: \"5\", key: \"11lu5j\" }],\n [\"line\", { x1: \"12\", x2: \"12\", y1: \"19\", y2: \"22\", key: \"x3vr5v\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"7\", key: \"fim9np\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst LocateFixed = createLucideIcon(\"locate-fixed\", __iconNode);\n\nexport { __iconNode, LocateFixed as default };\n//# sourceMappingURL=locate-fixed.js.map\n","/* -------------------------------------------------------------------- */\n/* Agent adapter — PatientSearch. */\n/* */\n/* PatientSearch is a top-of-funnel widget with four variants that vary */\n/* which search dimensions are exposed. The adapter surfaces the active */\n/* variant + lightweight actions (`reset`, `focus_primary`) so an agent */\n/* can drive the form across surfaces without knowing which variant is */\n/* in play. The selection state itself is owned by the consumer via the */\n/* `value` / `onChange` contract — never read here. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { PatientSearchHandle } from './patient-search';\n\nexport const patientSearchAgent: AgentAdapter<PatientSearchHandle> = {\n id: 'patient-search',\n capabilities: ['navigate', 'view_change'],\n state: {\n variant: {\n type: 'string',\n descriptionKey: 'ui.agent.patientSearch.state.variant',\n description:\n 'Active variant — one of `who-where`, `who-where-when`, `faceted`, `discovery`.',\n read: (handle) => handle.getVariant(),\n },\n },\n actions: {\n reset: {\n safety: 'write',\n descriptionKey: 'ui.agent.patientSearch.actions.reset',\n description:\n 'Clear the current selection (WHO, WHERE, WHEN, facets, geo) via the consumer-owned onChange.',\n invoke: (handle) => {\n handle.reset();\n },\n },\n focus_primary: {\n safety: 'read',\n descriptionKey: 'ui.agent.patientSearch.actions.focusPrimary',\n description:\n 'Move keyboard focus to the most-relevant field for the current variant.',\n invoke: (handle) => {\n handle.focusPrimary();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'patient-search',\n description: 'Marks the PatientSearch root region.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport {\n ChevronDown,\n LocateFixed,\n MapPin,\n Search as SearchIcon,\n Stethoscope,\n X,\n} from 'lucide-react';\nimport { Autocomplete } from '../autocomplete/autocomplete';\nimport { Button } from '../button/button';\nimport { Card } from '../card/card';\nimport { Slider } from '../slider/slider';\nimport { Alert } from '../alert/alert';\nimport { DropdownMenu } from '../dropdown-menu/dropdown-menu';\nimport type { OptionShape } from '../_shared/option';\nimport { useAgentRegistration } from '../../agent';\nimport { patientSearchAgent } from './patient-search.agent';\n\n/* -------------------------------------------------------------------- */\n/* PatientSearch */\n/* */\n/* Top-of-funnel search primitive with four A/B-testable variants. Each */\n/* variant exposes a different combination of patient decision-making */\n/* dimensions (WHO, WHERE, WHEN, INSURANCE/LANGUAGE, DISCOVERY) so */\n/* consumers can run PostHog-driven funnel experiments without having to */\n/* re-implement the form per surface. */\n/* */\n/* Every variant's WHERE input bakes in a \"use my location\" affordance — */\n/* a `LocateFixed` crosshair button on the inline-end of the field, with */\n/* a radius slider that appears beneath on grant. Geolocation isn't a */\n/* variant of its own; it's a universal WHERE behaviour the kit always */\n/* offers. */\n/* */\n/* Consumers own the data loaders (`loadObjects`, `loadPlaces`, */\n/* `reverseGeocode`, `discoveryCards`) + the routing logic in */\n/* `onSubmit` / `onObjectSelect` / `onPlaceSelect`. The kit owns layout, */\n/* variant gating, accessibility, telemetry, and the shared `SearchValue`*/\n/* contract that every variant feeds into. */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* -------------------------------------------------------------------- */\n\nexport type PatientSearchVariant =\n | 'who-where'\n | 'who-where-when'\n | 'faceted'\n | 'discovery';\n\nexport type WhenFilter = 'today' | 'tomorrow' | 'this_week' | 'anytime';\n\n/** Result from `loadObjects` — a profession, specialty, operator, or practice. */\nexport interface PatientSearchObject {\n /** Stable opaque key, e.g. `\"profession:dentista\"` or `\"operator:mario-rossi-9001\"`. */\n value: string;\n label: string;\n description?: string;\n /** Opaque consumer payload — used in `onObjectSelect` to route. */\n __payload?: unknown;\n}\n\n/** Result from `loadPlaces` — a Google-Places-style place prediction. */\nexport interface PatientSearchPlace {\n /** Place ID or stable text. */\n value: string;\n /** Display label, e.g. `\"Milano, Italia\"`. */\n label: string;\n lat?: number;\n lng?: number;\n}\n\n/** Curated card for the `discovery` variant. */\nexport interface DiscoveryCard {\n id: string;\n label: string;\n description?: string;\n href: string;\n icon?: ReactNode;\n /**\n * Accent token. `auto` rotates through the brand palette by row index.\n * Defaults to `auto`.\n */\n accent?: 'violet' | 'purple' | 'magenta' | 'blue' | 'auto';\n badge?: string;\n}\n\nexport interface FacetOption {\n value: string;\n label: string;\n}\n\nexport interface FacetGroup {\n /** Stable key — e.g. `\"insurance\"`, `\"language\"`. */\n key: string;\n /** Display label, e.g. `\"Convenzione\"`. */\n label: string;\n /** Empty array = boolean toggle chip. Non-empty = dropdown chip. */\n options: FacetOption[];\n /** Whether multiple options can be selected. Default false. */\n multi?: boolean;\n}\n\nexport interface SearchValue {\n object?: PatientSearchObject;\n place?: PatientSearchPlace;\n when?: WhenFilter;\n facets?: Record<string, string[]>;\n geo?: {\n lat: number;\n lng: number;\n radiusKm: number;\n label?: string;\n };\n}\n\n/** Curated imperative handle — used by the agent adapter. */\nexport interface PatientSearchHandle {\n getVariant: () => PatientSearchVariant;\n reset: () => void;\n /** Move focus to the most-relevant field for the current variant. */\n focusPrimary: () => void;\n}\n\n/* -------------------------------------------------------------------- */\n/* Telemetry */\n/* */\n/* Mirrors the booking-website convention (`window.track?.(event, p)`). */\n/* Soft-fails outside the browser and when no host has wired `track`. */\n/* -------------------------------------------------------------------- */\n\ninterface TrackFn {\n (event: string, payload?: Record<string, unknown>): void;\n}\n\nfunction track(event: string, payload?: Record<string, unknown>): void {\n if (typeof window === 'undefined') return;\n const fn = (window as unknown as { track?: TrackFn }).track;\n try {\n fn?.(event, payload);\n } catch {\n // Telemetry must never throw into the UI. Swallow.\n }\n}\n\n/* -------------------------------------------------------------------- */\n/* Constants */\n/* -------------------------------------------------------------------- */\n\nconst WHEN_OPTIONS: readonly WhenFilter[] = [\n 'today',\n 'tomorrow',\n 'this_week',\n 'anytime',\n] as const;\n\n/** Discrete radius presets per spec (1 / 5 / 10 / 25 / 50 km). */\nconst RADIUS_PRESETS: readonly number[] = [1, 5, 10, 25, 50] as const;\n\nconst DEFAULT_RADIUS_KM = 5;\n\nconst AUTOCOMPLETE_DEBOUNCE_MS = 200;\n\n/** Maps `defaultRadiusKm` to the nearest preset index. */\nfunction radiusToStepIndex(km: number): number {\n let best = 0;\n let bestDelta = Number.POSITIVE_INFINITY;\n RADIUS_PRESETS.forEach((preset, index) => {\n const delta = Math.abs(preset - km);\n if (delta < bestDelta) {\n bestDelta = delta;\n best = index;\n }\n });\n return best;\n}\n\n/* -------------------------------------------------------------------- */\n/* Root variants */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n 'ds:flex ds:w-full ds:flex-col ds:text-[var(--foreground)]',\n {\n variants: {\n variant: {\n 'who-where': 'ds:gap-[var(--spacing-md)]',\n 'who-where-when': 'ds:gap-[var(--spacing-md)]',\n faceted: 'ds:gap-[var(--spacing-md)]',\n discovery: 'ds:gap-[var(--spacing-lg)]',\n },\n },\n defaultVariants: { variant: 'who-where' },\n },\n);\n\n/* -------------------------------------------------------------------- */\n/* Public props */\n/* -------------------------------------------------------------------- */\n\nexport interface PatientSearchProps\n extends\n Omit<\n ComponentPropsWithoutRef<'div'>,\n 'aria-label' | 'onChange' | 'onSubmit'\n >,\n VariantProps<typeof rootVariants> {\n variant: PatientSearchVariant;\n\n // Data loaders — consumer-injected; kit calls them debounced.\n loadObjects: (\n query: string,\n ctx: { signal: AbortSignal },\n ) => Promise<PatientSearchObject[]>;\n loadPlaces: (\n query: string,\n ctx: { signal: AbortSignal },\n ) => Promise<PatientSearchPlace[]>;\n\n // Selection callbacks — the consumer decides whether to route.\n onObjectSelect?: (option: PatientSearchObject) => void;\n onPlaceSelect?: (place: PatientSearchPlace) => void;\n\n // Facet config — used by the `faceted` variant; ignored by others.\n facets?: FacetGroup[];\n\n // Geo config — applies to every variant's WHERE input. The crosshair\n // overlay on the inline-end of the input triggers geolocation on click;\n // on success the input fills with the reverse-geocoded label and a\n // radius slider appears beneath. Consumer-supplied `reverseGeocode` is\n // optional — when omitted, `value.geo.label` falls back to the localised\n // \"Vicino a me\" string.\n /** Default radius in km when geolocation is granted. Snaps to the nearest preset. */\n defaultRadiusKm?: number;\n /** Reverse-geocode lat/lng → human label. Optional. */\n reverseGeocode?: (lat: number, lng: number) => Promise<string>;\n\n // Discovery cards — used by the `discovery` variant.\n discoveryCards?: DiscoveryCard[];\n\n /** Forwarded to the consumer's loaders if they want to constrain by country. */\n countryCode?: 'it' | 'de' | string;\n\n // State — controlled.\n value: SearchValue;\n onChange: (next: SearchValue) => void;\n onSubmit: (value: SearchValue) => void | Promise<void>;\n\n // UX\n submitting?: boolean;\n /** Custom CTA label override; defaults to `ui.patientSearch.submit.cta`. */\n submitLabel?: string;\n /**\n * @default false\n * By default the kit disables the submit button until at least one\n * search dimension is set (object, place, geo, non-anytime when, or\n * a facet). Set to `true` to allow an empty submit — useful for\n * \"browse all\" patterns where the consumer wants to show all results\n * with no initial filter.\n */\n allowEmptySubmit?: boolean;\n\n // a11y\n 'aria-label'?: string;\n /** Agent-readiness instance id. */\n id?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* WhoField — Autocomplete over union(profession, specialty, operator, */\n/* practice). */\n/* -------------------------------------------------------------------- */\n\ninterface WhoFieldProps {\n value: SearchValue;\n onChange: (next: SearchValue) => void;\n loadObjects: PatientSearchProps['loadObjects'];\n onObjectSelect?: PatientSearchProps['onObjectSelect'];\n inputRef?: React.Ref<HTMLInputElement>;\n}\n\nfunction WhoField({\n value,\n onChange,\n loadObjects,\n onObjectSelect,\n inputRef,\n}: WhoFieldProps) {\n const { t } = useTranslation();\n\n // The Autocomplete owns its own visible text; the kit only intercepts\n // selection events to update `value.object`. We mirror external resets\n // (consumer clears `value.object` programmatically) into the visible text\n // via the controlled `value` prop, but otherwise let typed input flow\n // through Autocomplete's internal state. The local `query` state breaks\n // the controlled-input cycle: typing fires onChange(next), we store\n // `next` locally, the next render passes that exact string back as\n // `value` — no flicker, no truncation.\n const [query, setQuery] = useState<string>(value.object?.label ?? '');\n const selectedValueRef = useRef<string | undefined>(value.object?.value);\n\n useEffect(() => {\n // Sync external selection changes (e.g. consumer-driven reset) into the\n // visible text. We watch `value.object?.value` rather than `label` so an\n // identity update with the same label doesn't clobber a fresh query.\n if (value.object?.value !== selectedValueRef.current) {\n selectedValueRef.current = value.object?.value;\n setQuery(value.object?.label ?? '');\n }\n }, [value.object?.value, value.object?.label]);\n\n // Adapt consumer's `loadObjects` to Autocomplete's `loadOptions` signature.\n // PatientSearchObject is structurally a superset of OptionShape, so the\n // returned array is safe to pass through as `OptionShape<string>[]`.\n const loadOptions = useCallback(\n async (q: string, { signal }: { signal: AbortSignal; locale: string }) =>\n (await loadObjects(q, { signal })) as OptionShape<string>[],\n [loadObjects],\n );\n\n const handleSelect = (option: OptionShape<string>) => {\n const next: PatientSearchObject = {\n value: option.value,\n label: option.label,\n description: option.description,\n __payload: (option as PatientSearchObject).__payload,\n };\n setQuery(next.label);\n selectedValueRef.current = next.value;\n onChange({ ...value, object: next });\n track('patient_search_object_selected', {\n value: next.value,\n type:\n next.__payload && typeof next.__payload === 'object'\n ? (next.__payload as { type?: string }).type\n : undefined,\n });\n onObjectSelect?.(next);\n };\n\n // Autocomplete's public props don't include `onFocus`, so we capture focus\n // on a wrapping <div> via the bubbling event from the embedded input.\n // `data-component=\"patient-search-field-who\"` lets agents target the\n // wrapper for instrumentation hooks without depending on Autocomplete's\n // internal structure.\n const focusTrackedRef = useRef(false);\n return (\n <div\n data-component=\"patient-search-field\"\n data-field=\"who\"\n onFocus={() => {\n if (focusTrackedRef.current) return;\n focusTrackedRef.current = true;\n track('patient_search_object_focused');\n }}\n onBlur={(e) => {\n // Reset the once-per-mount focus flag only when focus leaves the\n // wrapper entirely (no relatedTarget inside our subtree).\n if (\n e.currentTarget &&\n !e.currentTarget.contains(e.relatedTarget as Node | null)\n ) {\n focusTrackedRef.current = false;\n }\n }}\n >\n <Autocomplete\n ref={inputRef}\n value={query}\n onChange={(next) => {\n setQuery(next);\n // User edited the input after selection — drop the structured\n // selection so submit can't reuse stale data.\n if (value.object && next !== value.object.label) {\n selectedValueRef.current = undefined;\n onChange({ ...value, object: undefined });\n }\n }}\n onSelect={handleSelect}\n loadOptions={loadOptions}\n debounceMs={AUTOCOMPLETE_DEBOUNCE_MS}\n placeholder={t('patientSearch.who.placeholder')}\n aria-label={t('patientSearch.who.label')}\n startAdornment={<SearchIcon aria-hidden=\"true\" />}\n />\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* WhereField — Autocomplete over the consumer's places provider. */\n/* -------------------------------------------------------------------- */\n\ninterface WhereFieldProps {\n value: SearchValue;\n onChange: (next: SearchValue) => void;\n loadPlaces: PatientSearchProps['loadPlaces'];\n onPlaceSelect?: PatientSearchProps['onPlaceSelect'];\n inputRef?: React.Ref<HTMLInputElement>;\n /**\n * Default radius (km) applied on a successful geolocate. Snaps to the\n * nearest preset (1 / 5 / 10 / 25 / 50). Defaults to 5 km.\n */\n defaultRadiusKm?: number;\n /** Reverse-geocode lat/lng → human label. Optional. */\n reverseGeocode?: PatientSearchProps['reverseGeocode'];\n}\n\ntype GeoStatus = 'pristine' | 'requesting' | 'granted' | 'denied';\n\nconst geoTriggerClasses = [\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:size-8 ds:rounded-[var(--radius-sm)]',\n 'ds:text-[color:var(--muted-foreground)] ds:hover:text-[color:var(--primary)]',\n 'ds:hover:bg-[color-mix(in_srgb,var(--primary)_8%,transparent)]',\n 'ds:aria-pressed:text-[color:var(--primary)]',\n 'ds:aria-pressed:bg-[color-mix(in_srgb,var(--primary)_12%,transparent)]',\n 'ds:disabled:opacity-50 ds:disabled:cursor-not-allowed',\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-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n].join(' ');\n\n/**\n * Unified WHERE input — every variant uses it.\n *\n * Layout per state:\n * - pristine: `[MapPin] City... [LocateFixed]`\n * - granted : `[MapPin] Milano, Italia [X] [LocateFixed]` + radius slider beneath\n * - denied : `[MapPin] City... ` + Alert beneath\n *\n * The MapPin on the inline-start is the conventional \"this is a location\n * field\" marker. The LocateFixed crosshair on the inline-end is the\n * geolocate trigger — these are deliberately different glyphs (Doctolib /\n * Google Maps / Booking pattern) so users don't conflate the field marker\n * with the action.\n */\nfunction WhereField({\n value,\n onChange,\n loadPlaces,\n onPlaceSelect,\n inputRef,\n defaultRadiusKm = DEFAULT_RADIUS_KM,\n reverseGeocode,\n}: WhereFieldProps) {\n const { t } = useTranslation();\n const [query, setQuery] = useState<string>(value.place?.label ?? '');\n const selectedValueRef = useRef<string | undefined>(value.place?.value);\n const initialStatus: GeoStatus = value.geo ? 'granted' : 'pristine';\n const [status, setStatus] = useState<GeoStatus>(initialStatus);\n\n useEffect(() => {\n if (value.place?.value !== selectedValueRef.current) {\n selectedValueRef.current = value.place?.value;\n setQuery(value.place?.label ?? '');\n }\n }, [value.place?.value, value.place?.label]);\n\n const loadOptions = useCallback(\n async (q: string, { signal }: { signal: AbortSignal; locale: string }) =>\n (await loadPlaces(q, { signal })) as OptionShape<string>[],\n [loadPlaces],\n );\n\n const handleSelect = (option: OptionShape<string>) => {\n const place: PatientSearchPlace = {\n value: option.value,\n label: option.label,\n lat: (option as PatientSearchPlace).lat,\n lng: (option as PatientSearchPlace).lng,\n };\n setQuery(place.label);\n selectedValueRef.current = place.value;\n // Typing or selecting a city through the autocomplete cancels any\n // active geolocate — the consumer's search API should now run plain\n // text search rather than lat/lng-radius.\n onChange({ ...value, place, geo: undefined });\n if (status === 'granted') setStatus('pristine');\n track('patient_search_place_selected', { value: place.value });\n onPlaceSelect?.(place);\n };\n\n const requestLocation = () => {\n if (typeof window === 'undefined' || !navigator?.geolocation) {\n setStatus('denied');\n track('patient_search_geo_denied', { reason: 'unsupported' });\n return;\n }\n setStatus('requesting');\n track('patient_search_place_focused');\n navigator.geolocation.getCurrentPosition(\n async (position) => {\n const { latitude, longitude } = position.coords;\n let label: string | undefined;\n try {\n label = await reverseGeocode?.(latitude, longitude);\n } catch {\n label = undefined;\n }\n const km = defaultRadiusKm;\n const resolvedLabel = label ?? t('patientSearch.geo.fallbackLabel');\n // Mirror the geo label into `value.place` so the visible input\n // shows the reverse-geocoded text. `value.geo` carries the\n // structured lat/lng/radius the consumer's search API actually\n // needs. The synthetic place carries a `geo:` value prefix so\n // the consumer can distinguish a geo-derived place from a\n // user-selected Google Places entry.\n const syntheticPlace: PatientSearchPlace = {\n value: `geo:${latitude.toFixed(4)},${longitude.toFixed(4)}`,\n label: resolvedLabel,\n lat: latitude,\n lng: longitude,\n };\n onChange({\n ...value,\n place: syntheticPlace,\n geo: {\n lat: latitude,\n lng: longitude,\n radiusKm: km,\n label: resolvedLabel,\n },\n });\n setStatus('granted');\n track('patient_search_geo_granted', { radiusKm: km });\n },\n () => {\n setStatus('denied');\n track('patient_search_geo_denied', { reason: 'user' });\n },\n { enableHighAccuracy: false, maximumAge: 60_000, timeout: 10_000 },\n );\n };\n\n const clearGeo = () => {\n onChange({ ...value, geo: undefined, place: undefined });\n setQuery('');\n selectedValueRef.current = undefined;\n setStatus('pristine');\n };\n\n const isGranted = status === 'granted' && Boolean(value.geo);\n const isRequesting = status === 'requesting';\n const isDenied = status === 'denied';\n\n const focusTrackedRef = useRef(false);\n // Overlay button(s) that sit visually inside the inline-end of the\n // input. Rendered as siblings of the Autocomplete (not inside the\n // `endAdornment` slot, which the kit wraps in `aria-hidden` and so\n // cannot host an interactive trigger). The decorative spacer in\n // `endAdornment` carves out room so typed text doesn't run under\n // the overlay.\n // Gate the geo-assist UI on the consumer wiring `reverseGeocode`.\n // Without it, clicking \"Vicino a me\" would set value.geo to lat/lng\n // coordinates the kit can't translate into a human-readable label —\n // the patient sees coordinates or nothing. Presence of the dep\n // implies intent.\n const geoEnabled = Boolean(reverseGeocode);\n const overlay =\n isDenied || !geoEnabled ? null : (\n <span className=\"ds:absolute ds:end-[var(--spacing-xs)] ds:top-1/2 ds:-translate-y-1/2 ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n {isGranted ? (\n <button\n type=\"button\"\n aria-label={t('patientSearch.geo.clear')}\n onClick={clearGeo}\n className={geoTriggerClasses}\n >\n <X aria-hidden=\"true\" className=\"ds:size-4\" />\n </button>\n ) : null}\n <button\n type=\"button\"\n aria-label={t('patientSearch.geo.cta')}\n aria-pressed={isGranted || undefined}\n disabled={isRequesting}\n onClick={isGranted ? clearGeo : requestLocation}\n className={geoTriggerClasses}\n >\n {/*\n The geo trigger uses `LocateFixed` (target-with-dot crosshair)\n rather than `MapPin`. The WHERE input's start-adornment is the\n `MapPin` (\"this is a location field\"); reusing the pin glyph\n here would visually conflate the field marker with the action.\n Crosshair = \"centre on me\" — Doctolib / Google Maps / Booking\n all use this distinction.\n */}\n <LocateFixed aria-hidden=\"true\" className=\"ds:size-4\" />\n </button>\n </span>\n );\n\n return (\n <div\n data-component=\"patient-search-field\"\n data-field=\"where\"\n onFocus={() => {\n if (focusTrackedRef.current) return;\n focusTrackedRef.current = true;\n track('patient_search_place_focused');\n }}\n onBlur={(e) => {\n if (\n e.currentTarget &&\n !e.currentTarget.contains(e.relatedTarget as Node | null)\n ) {\n focusTrackedRef.current = false;\n }\n }}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n <div className=\"ds:relative\">\n <Autocomplete\n ref={inputRef}\n value={query}\n onChange={(next) => {\n setQuery(next);\n if (value.place && next !== value.place.label) {\n selectedValueRef.current = undefined;\n // Manual edit cancels any active geolocate too — the typed\n // text owns the field now.\n onChange({ ...value, place: undefined, geo: undefined });\n if (status === 'granted') setStatus('pristine');\n }\n }}\n onSelect={handleSelect}\n loadOptions={loadOptions}\n debounceMs={AUTOCOMPLETE_DEBOUNCE_MS}\n placeholder={t('patientSearch.where.placeholder')}\n aria-label={t('patientSearch.where.label')}\n startAdornment={<MapPin aria-hidden=\"true\" />}\n endAdornment={\n isDenied ? undefined : (\n // Decorative spacer — reserves inline-end room so the typed\n // text doesn't run under the absolutely-positioned overlay.\n // Autocomplete wraps endAdornment in `aria-hidden`, which is\n // correct for this empty span. Granted-state shows an extra\n // clear-X chip, so the spacer doubles in width when granted.\n <span\n aria-hidden=\"true\"\n className={\n isGranted\n ? 'ds:inline-block ds:size-[4.5rem]'\n : 'ds:inline-block ds:size-8'\n }\n />\n )\n }\n />\n {overlay}\n </div>\n {isDenied ? (\n // Alert defaults to `role=\"status\"` + `aria-live=\"polite\"` for the\n // `info` variant — the user hears why the field reverted to plain\n // text when their browser blocked the geolocation prompt.\n <Alert variant=\"info\">\n <Alert.Description>{t('patientSearch.geo.denied')}</Alert.Description>\n </Alert>\n ) : null}\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* GeoRadiusBar — full-width radius slider rendered beneath the top */\n/* row when geo-assist is active. Lifted out of `WhereField` so the */\n/* WHO/WHERE/Submit row stays at single-row height when the slider is */\n/* visible (release notes 0.30.2 — see `ds-kit-followup-from-0.30.0`). */\n/* -------------------------------------------------------------------- */\n\ninterface GeoRadiusBarProps {\n value: SearchValue;\n onChange: (next: SearchValue) => void;\n defaultRadiusKm?: number;\n}\n\nfunction GeoRadiusBar({\n value,\n onChange,\n defaultRadiusKm = DEFAULT_RADIUS_KM,\n}: GeoRadiusBarProps) {\n const { t } = useTranslation();\n const [radiusIndex, setRadiusIndex] = useState<number>(() =>\n radiusToStepIndex(value.geo?.radiusKm ?? defaultRadiusKm),\n );\n if (!value.geo) return null;\n const commitRadius = (index: number) => {\n const km = RADIUS_PRESETS[index] ?? defaultRadiusKm;\n onChange({ ...value, geo: { ...value.geo!, radiusKm: km } });\n track('patient_search_radius_changed', { radiusKm: km });\n };\n const formatRadius = (n: number) => {\n const km = RADIUS_PRESETS[Math.round(n)] ?? defaultRadiusKm;\n return t('patientSearch.geo.radiusFormat', { km });\n };\n return (\n <div\n data-component=\"patient-search-geo-radius-bar\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pb-[var(--spacing-md)]\"\n >\n <span\n id=\"patient-search-radius-label\"\n className=\"type-meta ds:text-[color:var(--muted-foreground)]\"\n >\n {t('patientSearch.geo.radius')}\n </span>\n <Slider\n value={[radiusIndex]}\n min={0}\n max={RADIUS_PRESETS.length - 1}\n step={1}\n marks={RADIUS_PRESETS.map((km, index) => ({\n value: index,\n label: `${km}`,\n }))}\n formatValue={formatRadius}\n alwaysShowValue\n aria-labelledby=\"patient-search-radius-label\"\n onValueChange={(next) => setRadiusIndex(next[0] ?? 0)}\n onValueCommit={(next) => commitRadius(next[0] ?? 0)}\n />\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* WhenChipStrip — radiogroup of {today, tomorrow, this_week, anytime} */\n/* */\n/* Hand-rolled radiogroup: `role=\"radiogroup\"` on the container, button */\n/* children carry `role=\"radio\"` + `aria-checked`, arrow-key navigation */\n/* moves focus + selection. Visually presents as suggestion-style chips. */\n/* -------------------------------------------------------------------- */\n\nconst whenChipVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n 'ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]',\n 'ds:rounded-[var(--radius-full)]',\n 'type-label ds:font-[var(--font-weight-medium)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-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-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' '),\n {\n variants: {\n state: {\n idle: 'ds:bg-[var(--secondary)] ds:text-[var(--foreground)] ds:border ds:border-[color:var(--border)] ds:hover:bg-[color-mix(in_srgb,var(--primary)_8%,var(--secondary))]',\n selected:\n 'ds:bg-[var(--primary)] ds:text-[var(--primary-foreground)] ds:border ds:border-transparent ds:font-[var(--font-weight-semibold)]',\n },\n },\n defaultVariants: { state: 'idle' },\n },\n);\n\ninterface WhenChipStripProps {\n value?: WhenFilter;\n onChange: (next: WhenFilter) => void;\n}\n\nfunction WhenChipStrip({ value, onChange }: WhenChipStripProps) {\n const { t, i18n } = useTranslation();\n const groupId = useId();\n const isRTL = i18n.dir() === 'rtl';\n\n const buttonRefs = useRef<Map<WhenFilter, HTMLButtonElement>>(new Map());\n\n const focusAt = useCallback((opt: WhenFilter) => {\n buttonRefs.current.get(opt)?.focus();\n }, []);\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLButtonElement>, current: WhenFilter) => {\n const idx = WHEN_OPTIONS.indexOf(current);\n const total = WHEN_OPTIONS.length;\n const forwardKey = isRTL ? 'ArrowLeft' : 'ArrowRight';\n const backwardKey = isRTL ? 'ArrowRight' : 'ArrowLeft';\n switch (event.key) {\n case forwardKey:\n case 'ArrowDown': {\n event.preventDefault();\n const next = WHEN_OPTIONS[(idx + 1) % total];\n onChange(next);\n track('patient_search_when_selected', { when: next });\n focusAt(next);\n break;\n }\n case backwardKey:\n case 'ArrowUp': {\n event.preventDefault();\n const next = WHEN_OPTIONS[(idx - 1 + total) % total];\n onChange(next);\n track('patient_search_when_selected', { when: next });\n focusAt(next);\n break;\n }\n case 'Home': {\n event.preventDefault();\n const next = WHEN_OPTIONS[0];\n onChange(next);\n track('patient_search_when_selected', { when: next });\n focusAt(next);\n break;\n }\n case 'End': {\n event.preventDefault();\n const next = WHEN_OPTIONS[total - 1];\n onChange(next);\n track('patient_search_when_selected', { when: next });\n focusAt(next);\n break;\n }\n default:\n break;\n }\n },\n [focusAt, isRTL, onChange],\n );\n\n // Roving tabindex — the selected option (or `anytime` fallback) is the only\n // tab stop. Matches the WAI-ARIA radiogroup pattern.\n const rovingOption: WhenFilter = value ?? 'anytime';\n\n const labelKeyFor: Record<WhenFilter, string> = {\n today: 'patientSearch.when.today',\n tomorrow: 'patientSearch.when.tomorrow',\n this_week: 'patientSearch.when.thisWeek',\n anytime: 'patientSearch.when.anytime',\n };\n\n return (\n <div\n role=\"radiogroup\"\n aria-label={t('patientSearch.when.label')}\n id={groupId}\n className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-xs)]\"\n >\n {WHEN_OPTIONS.map((opt) => {\n const isSelected = value === opt;\n const isRoving = rovingOption === opt;\n const label = t(labelKeyFor[opt]);\n return (\n <button\n key={opt}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n aria-label={label}\n tabIndex={isRoving ? 0 : -1}\n ref={(node) => {\n if (node) buttonRefs.current.set(opt, node);\n else buttonRefs.current.delete(opt);\n }}\n onClick={() => {\n onChange(opt);\n track('patient_search_when_selected', { when: opt });\n }}\n onKeyDown={(e) => handleKeyDown(e, opt)}\n className={whenChipVariants({\n state: isSelected ? 'selected' : 'idle',\n })}\n >\n {label}\n </button>\n );\n })}\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* FacetChipRow — faceted variant. One chip per FacetGroup. */\n/* -------------------------------------------------------------------- */\n\nconst facetChipVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n 'ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]',\n 'ds:rounded-[var(--radius-full)]',\n 'type-label',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-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-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' '),\n {\n variants: {\n // Matches the WHEN chip strip's `selected` look — solid primary fill,\n // primary-foreground text, transparent border. The earlier\n // `color-mix(primary 12% / secondary)` tint read as a washed-out\n // colour over the grey input row and didn't visually signal \"this\n // filter is active\" with enough weight. Solid fill matches the kit's\n // toggle-button precedent (SuggestionChip's `suggestion` intent + the\n // ServiceGrid radio's selected state in Booking).\n state: {\n idle: 'ds:bg-[var(--secondary)] ds:text-[var(--foreground)] ds:border ds:border-[color:var(--border)] ds:hover:bg-[color-mix(in_srgb,var(--primary)_8%,var(--secondary))]',\n active:\n 'ds:bg-[var(--primary)] ds:text-[var(--primary-foreground)] ds:border ds:border-transparent ds:font-[var(--font-weight-semibold)]',\n },\n },\n defaultVariants: { state: 'idle' },\n },\n);\n\ninterface FacetChipRowProps {\n facets: FacetGroup[];\n value: SearchValue['facets'];\n onChange: (next: Record<string, string[]>) => void;\n}\n\nfunction FacetChipRow({ facets, value, onChange }: FacetChipRowProps) {\n const { t } = useTranslation();\n // `selections` is memoised because `value ?? {}` allocates a fresh `{}`\n // whenever the consumer passes `undefined`, which would otherwise invalidate\n // every downstream useMemo / useCallback that depends on it.\n const selections = useMemo(() => value ?? {}, [value]);\n\n const hasAnyActive = useMemo(\n () => Object.values(selections).some((arr) => arr && arr.length > 0),\n [selections],\n );\n\n const updateFacet = (key: string, values: string[]) => {\n const next = { ...selections };\n if (values.length === 0) delete next[key];\n else next[key] = values;\n onChange(next);\n track('patient_search_facet_changed', { key, values });\n };\n\n const toggleBoolean = (key: string) => {\n const isActive = (selections[key]?.length ?? 0) > 0;\n updateFacet(key, isActive ? [] : ['true']);\n };\n\n const toggleOption = (group: FacetGroup, option: string) => {\n const current = selections[group.key] ?? [];\n const active = current.includes(option);\n let next: string[];\n if (group.multi) {\n next = active\n ? current.filter((v) => v !== option)\n : [...current, option];\n } else {\n next = active ? [] : [option];\n }\n updateFacet(group.key, next);\n };\n\n return (\n <div\n role=\"group\"\n aria-label={t('patientSearch.facets.label')}\n className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-xs)]\"\n >\n {facets.map((group) => {\n const isBoolean = group.options.length === 0;\n const current = selections[group.key] ?? [];\n const isActive = current.length > 0;\n\n if (isBoolean) {\n return (\n <button\n key={group.key}\n type=\"button\"\n aria-pressed={isActive}\n aria-label={group.label}\n onClick={() => toggleBoolean(group.key)}\n className={facetChipVariants({\n state: isActive ? 'active' : 'idle',\n })}\n >\n <span>{group.label}</span>\n </button>\n );\n }\n\n const countSuffix = isActive ? ` (${current.length})` : '';\n\n return (\n <DropdownMenu.Root key={group.key}>\n <DropdownMenu.Trigger asChild>\n <button\n type=\"button\"\n aria-label={`${group.label}${countSuffix}`}\n className={facetChipVariants({\n state: isActive ? 'active' : 'idle',\n })}\n >\n <span>\n {group.label}\n {countSuffix}\n </span>\n <ChevronDown\n aria-hidden=\"true\"\n className=\"ds:size-3.5 ds:shrink-0\"\n />\n </button>\n </DropdownMenu.Trigger>\n <DropdownMenu.Content sideOffset={8} align=\"start\">\n {group.options.map((option) => (\n <DropdownMenu.CheckboxItem\n key={option.value}\n checked={current.includes(option.value)}\n onSelect={(event) => {\n // Keep the menu open for multi-select facets so the user\n // can tick multiple boxes without re-opening.\n if (group.multi) event.preventDefault();\n toggleOption(group, option.value);\n }}\n >\n {option.label}\n </DropdownMenu.CheckboxItem>\n ))}\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n })}\n {hasAnyActive ? (\n <button\n type=\"button\"\n onClick={() => {\n onChange({});\n track('patient_search_facet_changed', { key: '*', values: [] });\n }}\n className={[\n 'ds:ms-[var(--spacing-xs)] ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:min-h-[var(--min-target-size)] ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)]',\n 'type-label ds:text-[color:var(--muted-foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:hover:text-[color:var(--foreground)]',\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-[length:var(--focus-ring-offset)]',\n ].join(' ')}\n >\n <X aria-hidden=\"true\" className=\"ds:size-3.5\" />\n {t('patientSearch.facets.clear')}\n </button>\n ) : null}\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* DiscoveryGrid — responsive grid of curated cards. */\n/* -------------------------------------------------------------------- */\n\nconst WHEEL_ACCENTS = ['violet', 'purple', 'magenta', 'blue'] as const;\ntype WheelAccent = (typeof WHEEL_ACCENTS)[number];\n\nconst discoveryCardVariants = cva(\n [\n 'ds:relative ds:flex ds:h-full ds:flex-col ds:gap-[var(--spacing-sm)]',\n 'ds:rounded-[var(--radius-lg)] ds:overflow-hidden',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n 'ds:pt-[var(--spacing-md)] ds:pb-[var(--spacing-md)]',\n 'ds:bg-[var(--card)] ds:text-[var(--card-foreground)]',\n 'ds:border ds:border-[color:var(--card-border)]',\n 'ds:shadow-[var(--shadow-sm)]',\n 'ds:transition-[transform,box-shadow] ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:motion-safe:hover:-translate-y-0.5',\n 'ds:hover:shadow-[var(--shadow-md)]',\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-[length:var(--focus-ring-offset)]',\n 'ds:no-underline',\n ].join(' '),\n);\n\nconst accentStripVariants = cva(\n 'ds:absolute ds:inset-inline-0 ds:top-0 ds:h-[3px] ds:pointer-events-none',\n {\n variants: {\n accent: {\n violet: 'ds:bg-[var(--color-violet-500)]',\n purple: 'ds:bg-[var(--color-purple-500)]',\n magenta: 'ds:bg-[var(--color-magenta-500)]',\n blue: 'ds:bg-[var(--color-blue-500)]',\n },\n },\n defaultVariants: { accent: 'violet' },\n },\n);\n\nconst iconChipVariants = cva(\n 'ds:inline-flex ds:size-9 ds:items-center ds:justify-center ds:rounded-[var(--radius-md)]',\n {\n variants: {\n accent: {\n violet:\n 'ds:bg-[color-mix(in_srgb,var(--color-violet-500)_12%,var(--background))] ds:text-[var(--color-violet-700)]',\n purple:\n 'ds:bg-[color-mix(in_srgb,var(--color-purple-500)_12%,var(--background))] ds:text-[var(--color-purple-700)]',\n magenta:\n 'ds:bg-[color-mix(in_srgb,var(--color-magenta-500)_12%,var(--background))] ds:text-[var(--color-magenta-700)]',\n blue: 'ds:bg-[color-mix(in_srgb,var(--color-blue-500)_12%,var(--background))] ds:text-[var(--color-blue-700)]',\n },\n },\n defaultVariants: { accent: 'violet' },\n },\n);\n\nfunction resolveAccent(\n cardAccent: DiscoveryCard['accent'] | undefined,\n index: number,\n): WheelAccent {\n if (cardAccent && cardAccent !== 'auto') return cardAccent;\n return WHEEL_ACCENTS[index % WHEEL_ACCENTS.length];\n}\n\ninterface DiscoveryGridProps {\n cards: DiscoveryCard[];\n}\n\nfunction DiscoveryGrid({ cards }: DiscoveryGridProps) {\n const { t } = useTranslation();\n\n if (cards.length === 0) {\n return (\n <p className=\"type-body-sm ds:text-[color:var(--muted-foreground)] ds:m-0\">\n {t('patientSearch.discovery.empty')}\n </p>\n );\n }\n\n return (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <h3 className=\"type-title-card ds:text-[var(--foreground)] ds:m-0\">\n {t('patientSearch.discovery.heading')}\n </h3>\n <ul\n className={[\n 'ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)]',\n 'ds:md:grid-cols-2 ds:lg:grid-cols-3',\n 'ds:m-0 ds:p-0 ds:list-none',\n ].join(' ')}\n >\n {cards.map((card, index) => {\n const accent = resolveAccent(card.accent, index);\n return (\n <li key={card.id}>\n <a\n href={card.href}\n data-component=\"patient-search-card\"\n data-card-id={card.id}\n aria-label={\n card.description\n ? `${card.label} — ${card.description}`\n : card.label\n }\n onClick={() =>\n track('patient_search_discovery_card_clicked', {\n cardId: card.id,\n })\n }\n className={discoveryCardVariants()}\n >\n <span\n aria-hidden=\"true\"\n className={accentStripVariants({ accent })}\n />\n <span className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <span\n aria-hidden=\"true\"\n className={iconChipVariants({ accent })}\n >\n {card.icon ?? (\n <Stethoscope className=\"ds:size-4\" aria-hidden=\"true\" />\n )}\n </span>\n {card.badge ? (\n // The badge sits inside a softly-tinted accent bubble\n // (15% accent + 85% background). The text colour must\n // contrast with that mix, not with the accent's canonical\n // foreground — `--accent-foreground` is calibrated against\n // the 100% accent surface and fails AA on the 15% tint.\n // Use `--foreground` which contrasts with the\n // mostly-background mix in both light and dark themes.\n <span className=\"type-meta ds:ms-auto ds:inline-flex ds:items-center ds:rounded-[var(--radius-full)] ds:bg-[color-mix(in_srgb,var(--accent)_15%,var(--background))] ds:text-[color:var(--foreground)] ds:font-[var(--font-weight-semibold)] ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]\">\n {card.badge}\n </span>\n ) : null}\n </span>\n <span className=\"type-label ds:font-[var(--font-weight-semibold)] ds:text-[var(--foreground)]\">\n {card.label}\n </span>\n {card.description ? (\n <span className=\"type-body-sm ds:text-[color:var(--muted-foreground)]\">\n {card.description}\n </span>\n ) : null}\n </a>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* SubmitButton — shared CTA. Disabled only when every dimension is unset. */\n/* -------------------------------------------------------------------- */\n\ninterface SubmitButtonProps {\n value: SearchValue;\n variant: PatientSearchVariant;\n submitting?: boolean;\n submitLabel?: string;\n allowEmptySubmit?: boolean;\n onSubmit: (value: SearchValue) => void | Promise<void>;\n}\n\nfunction hasAnyDimension(value: SearchValue): boolean {\n if (value.object) return true;\n if (value.place) return true;\n if (value.geo) return true;\n if (value.when && value.when !== 'anytime') return true;\n if (value.facets) {\n for (const arr of Object.values(value.facets)) {\n if (arr && arr.length > 0) return true;\n }\n }\n return false;\n}\n\nfunction SubmitButton({\n value,\n variant,\n submitting,\n submitLabel,\n allowEmptySubmit,\n onSubmit,\n}: SubmitButtonProps) {\n const { t } = useTranslation();\n const disabled = submitting || (!allowEmptySubmit && !hasAnyDimension(value));\n const label = submitLabel ?? t('patientSearch.submit.cta');\n return (\n <Button\n type=\"button\"\n intent=\"primary\"\n size=\"lg\"\n startIcon={<SearchIcon aria-hidden=\"true\" />}\n loading={submitting}\n disabled={disabled}\n onClick={() => {\n track('patient_search_submitted', {\n variant,\n hasObject: Boolean(value.object),\n hasPlace: Boolean(value.place),\n hasGeo: Boolean(value.geo),\n when: value.when,\n facetKeys: value.facets ? Object.keys(value.facets) : [],\n });\n void onSubmit(value);\n }}\n >\n {label}\n </Button>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* Variant body props */\n/* -------------------------------------------------------------------- */\n\ninterface VariantBodyProps {\n variant: PatientSearchVariant;\n value: SearchValue;\n onChange: (next: SearchValue) => void;\n onSubmit: PatientSearchProps['onSubmit'];\n loadObjects: PatientSearchProps['loadObjects'];\n loadPlaces: PatientSearchProps['loadPlaces'];\n onObjectSelect?: PatientSearchProps['onObjectSelect'];\n onPlaceSelect?: PatientSearchProps['onPlaceSelect'];\n facets?: FacetGroup[];\n defaultRadiusKm: number;\n reverseGeocode?: PatientSearchProps['reverseGeocode'];\n discoveryCards: DiscoveryCard[];\n submitting?: boolean;\n submitLabel?: string;\n allowEmptySubmit?: boolean;\n whoRef: React.RefObject<HTMLInputElement>;\n whereRef: React.RefObject<HTMLInputElement>;\n /**\n * Accessible name applied to the inner `<Card>` so its underlying\n * `<article>` landmark isn't unnamed when nested inside the\n * `role=\"region\"` root. Without this every variant produces an\n * unnamed article — a `landmark-unique` axe hazard once a consumer\n * page renders adjacent articles. Resolved once at the root with\n * `t('patientSearch.regionLabel')` and threaded through so the\n * variant bodies don't each have to call `useTranslation()`.\n */\n cardLabel: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* WhoWhereBody — baseline variant. WHO + WHERE + submit. */\n/* -------------------------------------------------------------------- */\n\nfunction WhoWhereBody(props: VariantBodyProps) {\n const {\n variant,\n value,\n onChange,\n onSubmit,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n submitting,\n submitLabel,\n allowEmptySubmit,\n whoRef,\n whereRef,\n defaultRadiusKm,\n reverseGeocode,\n cardLabel,\n } = props;\n return (\n <Card variant=\"elevated\" aria-label={cardLabel}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:md:flex-row ds:md:items-start\">\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhoField\n value={value}\n onChange={onChange}\n loadObjects={loadObjects}\n onObjectSelect={onObjectSelect}\n inputRef={whoRef}\n />\n </div>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhereField\n value={value}\n onChange={onChange}\n loadPlaces={loadPlaces}\n onPlaceSelect={onPlaceSelect}\n defaultRadiusKm={defaultRadiusKm}\n reverseGeocode={reverseGeocode}\n inputRef={whereRef}\n />\n </div>\n <SubmitButton\n value={value}\n variant={variant}\n submitting={submitting}\n submitLabel={submitLabel}\n allowEmptySubmit={allowEmptySubmit}\n onSubmit={onSubmit}\n />\n </div>\n <GeoRadiusBar\n value={value}\n onChange={onChange}\n defaultRadiusKm={defaultRadiusKm}\n />\n </div>\n </Card>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* WhoWhereWhenBody — WHO + WHERE + submit + WHEN chip strip below. */\n/* -------------------------------------------------------------------- */\n\nfunction WhoWhereWhenBody(props: VariantBodyProps) {\n const {\n variant,\n value,\n onChange,\n onSubmit,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n submitting,\n submitLabel,\n allowEmptySubmit,\n whoRef,\n whereRef,\n defaultRadiusKm,\n reverseGeocode,\n cardLabel,\n } = props;\n return (\n <Card variant=\"elevated\" aria-label={cardLabel}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:p-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:md:flex-row ds:md:items-start\">\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhoField\n value={value}\n onChange={onChange}\n loadObjects={loadObjects}\n onObjectSelect={onObjectSelect}\n inputRef={whoRef}\n />\n </div>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhereField\n value={value}\n onChange={onChange}\n loadPlaces={loadPlaces}\n onPlaceSelect={onPlaceSelect}\n defaultRadiusKm={defaultRadiusKm}\n reverseGeocode={reverseGeocode}\n inputRef={whereRef}\n />\n </div>\n <SubmitButton\n value={value}\n variant={variant}\n submitting={submitting}\n submitLabel={submitLabel}\n allowEmptySubmit={allowEmptySubmit}\n onSubmit={onSubmit}\n />\n </div>\n <GeoRadiusBar\n value={value}\n onChange={onChange}\n defaultRadiusKm={defaultRadiusKm}\n />\n <WhenChipStrip\n value={value.when}\n onChange={(next) => onChange({ ...value, when: next })}\n />\n </div>\n </Card>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* FacetedBody — WHO + WHERE + submit + facet chip row below. */\n/* -------------------------------------------------------------------- */\n\nfunction FacetedBody(props: VariantBodyProps) {\n const {\n variant,\n value,\n onChange,\n onSubmit,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n facets,\n submitting,\n submitLabel,\n allowEmptySubmit,\n whoRef,\n whereRef,\n defaultRadiusKm,\n reverseGeocode,\n cardLabel,\n } = props;\n return (\n <Card variant=\"elevated\" aria-label={cardLabel}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:p-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:md:flex-row ds:md:items-start\">\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhoField\n value={value}\n onChange={onChange}\n loadObjects={loadObjects}\n onObjectSelect={onObjectSelect}\n inputRef={whoRef}\n />\n </div>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhereField\n value={value}\n onChange={onChange}\n loadPlaces={loadPlaces}\n onPlaceSelect={onPlaceSelect}\n defaultRadiusKm={defaultRadiusKm}\n reverseGeocode={reverseGeocode}\n inputRef={whereRef}\n />\n </div>\n <SubmitButton\n value={value}\n variant={variant}\n submitting={submitting}\n submitLabel={submitLabel}\n allowEmptySubmit={allowEmptySubmit}\n onSubmit={onSubmit}\n />\n </div>\n <GeoRadiusBar\n value={value}\n onChange={onChange}\n defaultRadiusKm={defaultRadiusKm}\n />\n {facets && facets.length > 0 ? (\n <FacetChipRow\n facets={facets}\n value={value.facets}\n onChange={(next) => onChange({ ...value, facets: next })}\n />\n ) : null}\n </div>\n </Card>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* DiscoveryBody — hero search + card grid. */\n/* -------------------------------------------------------------------- */\n\nfunction DiscoveryBody(props: VariantBodyProps) {\n const {\n variant,\n value,\n onChange,\n onSubmit,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n discoveryCards,\n submitting,\n submitLabel,\n allowEmptySubmit,\n whoRef,\n whereRef,\n defaultRadiusKm,\n reverseGeocode,\n cardLabel,\n } = props;\n return (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]\">\n <Card variant=\"elevated\" aria-label={cardLabel}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-[var(--spacing-md)] ds:md:flex-row ds:md:items-stretch\">\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhoField\n value={value}\n onChange={onChange}\n loadObjects={loadObjects}\n onObjectSelect={onObjectSelect}\n inputRef={whoRef}\n />\n </div>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhereField\n value={value}\n onChange={onChange}\n loadPlaces={loadPlaces}\n onPlaceSelect={onPlaceSelect}\n defaultRadiusKm={defaultRadiusKm}\n reverseGeocode={reverseGeocode}\n inputRef={whereRef}\n />\n </div>\n <SubmitButton\n value={value}\n variant={variant}\n submitting={submitting}\n submitLabel={submitLabel}\n allowEmptySubmit={allowEmptySubmit}\n onSubmit={onSubmit}\n />\n </div>\n <GeoRadiusBar\n value={value}\n onChange={onChange}\n defaultRadiusKm={defaultRadiusKm}\n />\n </Card>\n <DiscoveryGrid cards={discoveryCards} />\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* PatientSearch */\n/* -------------------------------------------------------------------- */\n\nexport const PatientSearch = forwardRef<\n PatientSearchHandle,\n PatientSearchProps\n>(function PatientSearch(\n {\n variant,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n facets,\n defaultRadiusKm = DEFAULT_RADIUS_KM,\n reverseGeocode,\n discoveryCards,\n countryCode,\n value,\n onChange,\n onSubmit,\n submitting,\n submitLabel,\n allowEmptySubmit,\n 'aria-label': ariaLabel,\n id,\n className,\n ...rest\n },\n ref,\n) {\n const { t } = useTranslation();\n\n const rootRef = useRef<HTMLDivElement>(null);\n\n // Primary-field refs — `focusPrimary` jumps to whichever input the variant\n // emphasises (WHO in most variants; no auto-focus on `discovery`).\n const whoRef = useRef<HTMLInputElement>(null);\n const whereRef = useRef<HTMLInputElement>(null);\n\n // Apply `when: anytime` default once on mount, but only for the variant\n // that actually renders the WHEN chip strip (`who-where-when`). The\n // `faceted` variant exposes facets, not WHEN — seeding `when: 'anytime'`\n // for it would silently pollute the consumer's value with a field the\n // form never displays.\n const seededWhenRef = useRef(false);\n useEffect(() => {\n if (seededWhenRef.current) return;\n if (variant !== 'who-where-when') return;\n if (value.when !== undefined) {\n seededWhenRef.current = true;\n return;\n }\n seededWhenRef.current = true;\n onChange({ ...value, when: 'anytime' });\n // We intentionally seed only on first render — the consumer can clear\n // back to `undefined` if they want, and we won't re-seed.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [variant]);\n\n // Apply `facets: {}` default once on mount for the faceted variant so\n // the chip row treats undefined and empty consistently.\n const seededFacetsRef = useRef(false);\n useEffect(() => {\n if (seededFacetsRef.current) return;\n if (variant !== 'faceted') return;\n if (value.facets !== undefined) {\n seededFacetsRef.current = true;\n return;\n }\n seededFacetsRef.current = true;\n onChange({ ...value, facets: {} });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [variant]);\n\n // Exposure event — fired once per variant assignment so PostHog can\n // record A/B assignment even for users who bounce before submitting.\n const exposedRef = useRef<PatientSearchVariant | null>(null);\n useEffect(() => {\n if (exposedRef.current === variant) return;\n exposedRef.current = variant;\n track('patient_search_variant_exposed', { variant, country: countryCode });\n }, [variant, countryCode]);\n\n // Imperative handle — minimal surface (`getVariant`, `reset`,\n // `focusPrimary`). The `value` reset goes through the consumer-owned\n // `onChange` so the parent's state stays the source of truth.\n //\n // The same handle is surfaced two ways: through the React `ref` (for\n // consumers driving the kit directly) and through `useAgentRegistration`\n // (for the future agent / MCP runtime). Both reach the same object so a\n // direct `ref.current?.reset()` and an `agentRegistry.invoke('reset')`\n // do the same thing.\n const handle = useMemo<PatientSearchHandle>(\n () => ({\n getVariant: () => variant,\n reset: () => {\n onChange({});\n },\n focusPrimary: () => {\n // Discovery deliberately doesn't auto-focus the search field on\n // mount; agents hitting `focus_primary` still get pulled into WHO\n // since that's the natural starting point for any keyboard-driven\n // flow once the user opts in.\n requestAnimationFrame(() => {\n whoRef.current?.focus();\n });\n },\n }),\n [variant, onChange],\n );\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(patientSearchAgent, handle, id);\n\n const resolvedAriaLabel = ariaLabel ?? t('patientSearch.regionLabel');\n\n const bodyProps: VariantBodyProps = {\n variant,\n value,\n onChange,\n onSubmit,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n facets,\n defaultRadiusKm,\n reverseGeocode,\n discoveryCards: discoveryCards ?? [],\n submitting,\n submitLabel,\n allowEmptySubmit,\n whoRef,\n whereRef,\n cardLabel: resolvedAriaLabel,\n };\n\n const body = (() => {\n switch (variant) {\n case 'who-where':\n return <WhoWhereBody {...bodyProps} />;\n case 'who-where-when':\n return <WhoWhereWhenBody {...bodyProps} />;\n case 'faceted':\n return <FacetedBody {...bodyProps} />;\n case 'discovery':\n return <DiscoveryBody {...bodyProps} />;\n /* c8 ignore next 2 */\n default:\n return null;\n }\n })();\n\n return (\n <div\n ref={rootRef}\n role=\"region\"\n aria-label={resolvedAriaLabel}\n id={id}\n data-component=\"patient-search\"\n data-component-id={id}\n data-variant={variant}\n className={rootVariants({ variant, className })}\n {...rest}\n >\n {body}\n </div>\n );\n});\n\nPatientSearch.displayName = 'PatientSearch';\n"],"names":["__iconNode","LocateFixed","createLucideIcon","patientSearchAgent","handle","track","event","payload","fn","WHEN_OPTIONS","RADIUS_PRESETS","DEFAULT_RADIUS_KM","AUTOCOMPLETE_DEBOUNCE_MS","radiusToStepIndex","km","best","bestDelta","preset","index","delta","rootVariants","cva","WhoField","value","onChange","loadObjects","onObjectSelect","inputRef","t","useTranslation","query","setQuery","useState","_a","selectedValueRef","useRef","_b","useEffect","_c","_d","loadOptions","useCallback","q","signal","handleSelect","option","next","focusTrackedRef","jsx","e","Autocomplete","SearchIcon","geoTriggerClasses","WhereField","loadPlaces","onPlaceSelect","defaultRadiusKm","reverseGeocode","initialStatus","status","setStatus","place","requestLocation","position","latitude","longitude","label","resolvedLabel","syntheticPlace","clearGeo","isGranted","isRequesting","isDenied","overlay","jsxs","X","MapPin","Alert","GeoRadiusBar","radiusIndex","setRadiusIndex","commitRadius","formatRadius","n","Slider","whenChipVariants","WhenChipStrip","i18n","groupId","useId","isRTL","buttonRefs","focusAt","opt","handleKeyDown","current","idx","total","forwardKey","backwardKey","rovingOption","labelKeyFor","isSelected","isRoving","node","facetChipVariants","FacetChipRow","facets","selections","useMemo","hasAnyActive","arr","updateFacet","key","values","toggleBoolean","isActive","toggleOption","group","active","v","isBoolean","countSuffix","DropdownMenu","ChevronDown","WHEEL_ACCENTS","discoveryCardVariants","accentStripVariants","iconChipVariants","resolveAccent","cardAccent","DiscoveryGrid","cards","card","accent","Stethoscope","hasAnyDimension","SubmitButton","variant","submitting","submitLabel","allowEmptySubmit","onSubmit","disabled","Button","WhoWhereBody","props","whoRef","whereRef","cardLabel","Card","WhoWhereWhenBody","FacetedBody","DiscoveryBody","discoveryCards","PatientSearch","forwardRef","countryCode","ariaLabel","id","className","rest","ref","rootRef","seededWhenRef","seededFacetsRef","exposedRef","useImperativeHandle","useAgentRegistration","resolvedAriaLabel","bodyProps","body"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"patient-search-CSDru7QW.js","sources":["../../node_modules/lucide-react/dist/esm/icons/locate-fixed.js","../../src/components/patient-search/patient-search.agent.ts","../../src/components/patient-search/patient-search.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"line\", { x1: \"2\", x2: \"5\", y1: \"12\", y2: \"12\", key: \"bvdh0s\" }],\n [\"line\", { x1: \"19\", x2: \"22\", y1: \"12\", y2: \"12\", key: \"1tbv5k\" }],\n [\"line\", { x1: \"12\", x2: \"12\", y1: \"2\", y2: \"5\", key: \"11lu5j\" }],\n [\"line\", { x1: \"12\", x2: \"12\", y1: \"19\", y2: \"22\", key: \"x3vr5v\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"7\", key: \"fim9np\" }],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst LocateFixed = createLucideIcon(\"locate-fixed\", __iconNode);\n\nexport { __iconNode, LocateFixed as default };\n//# sourceMappingURL=locate-fixed.js.map\n","/* -------------------------------------------------------------------- */\n/* Agent adapter — PatientSearch. */\n/* */\n/* PatientSearch is a top-of-funnel widget with four variants that vary */\n/* which search dimensions are exposed. The adapter surfaces the active */\n/* variant + lightweight actions (`reset`, `focus_primary`) so an agent */\n/* can drive the form across surfaces without knowing which variant is */\n/* in play. The selection state itself is owned by the consumer via the */\n/* `value` / `onChange` contract — never read here. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { PatientSearchHandle } from './patient-search';\n\nexport const patientSearchAgent: AgentAdapter<PatientSearchHandle> = {\n id: 'patient-search',\n capabilities: ['navigate', 'view_change'],\n state: {\n variant: {\n type: 'string',\n descriptionKey: 'ui.agent.patientSearch.state.variant',\n description:\n 'Active variant — one of `who-where`, `who-where-when`, `faceted`, `discovery`.',\n read: (handle) => handle.getVariant(),\n },\n },\n actions: {\n reset: {\n safety: 'write',\n descriptionKey: 'ui.agent.patientSearch.actions.reset',\n description:\n 'Clear the current selection (WHO, WHERE, WHEN, facets, geo) via the consumer-owned onChange.',\n invoke: (handle) => {\n handle.reset();\n },\n },\n focus_primary: {\n safety: 'read',\n descriptionKey: 'ui.agent.patientSearch.actions.focusPrimary',\n description:\n 'Move keyboard focus to the most-relevant field for the current variant.',\n invoke: (handle) => {\n handle.focusPrimary();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'patient-search',\n description: 'Marks the PatientSearch root region.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport {\n ChevronDown,\n LocateFixed,\n MapPin,\n Search as SearchIcon,\n Stethoscope,\n X,\n} from 'lucide-react';\nimport { Autocomplete } from '../autocomplete/autocomplete';\nimport { Button } from '../button/button';\nimport { Card } from '../card/card';\nimport { Slider } from '../slider/slider';\nimport { Alert } from '../alert/alert';\nimport { DropdownMenu } from '../dropdown-menu/dropdown-menu';\nimport type { OptionShape } from '../_shared/option';\nimport { useAgentRegistration } from '../../agent';\nimport { patientSearchAgent } from './patient-search.agent';\n\n/* -------------------------------------------------------------------- */\n/* PatientSearch */\n/* */\n/* Top-of-funnel search primitive with four A/B-testable variants. Each */\n/* variant exposes a different combination of patient decision-making */\n/* dimensions (WHO, WHERE, WHEN, INSURANCE/LANGUAGE, DISCOVERY) so */\n/* consumers can run PostHog-driven funnel experiments without having to */\n/* re-implement the form per surface. */\n/* */\n/* Every variant's WHERE input bakes in a \"use my location\" affordance — */\n/* a `LocateFixed` crosshair button on the inline-end of the field, with */\n/* a radius slider that appears beneath on grant. Geolocation isn't a */\n/* variant of its own; it's a universal WHERE behaviour the kit always */\n/* offers. */\n/* */\n/* Consumers own the data loaders (`loadObjects`, `loadPlaces`, */\n/* `reverseGeocode`, `discoveryCards`) + the routing logic in */\n/* `onSubmit` / `onObjectSelect` / `onPlaceSelect`. The kit owns layout, */\n/* variant gating, accessibility, telemetry, and the shared `SearchValue`*/\n/* contract that every variant feeds into. */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* -------------------------------------------------------------------- */\n\nexport type PatientSearchVariant =\n | 'who-where'\n | 'who-where-when'\n | 'faceted'\n | 'discovery';\n\nexport type WhenFilter = 'today' | 'tomorrow' | 'this_week' | 'anytime';\n\n/** Result from `loadObjects` — a profession, specialty, operator, or practice. */\nexport interface PatientSearchObject {\n /** Stable opaque key, e.g. `\"profession:dentista\"` or `\"operator:mario-rossi-9001\"`. */\n value: string;\n label: string;\n description?: string;\n /** Opaque consumer payload — used in `onObjectSelect` to route. */\n __payload?: unknown;\n}\n\n/** Result from `loadPlaces` — a Google-Places-style place prediction. */\nexport interface PatientSearchPlace {\n /** Place ID or stable text. */\n value: string;\n /** Display label, e.g. `\"Milano, Italia\"`. */\n label: string;\n lat?: number;\n lng?: number;\n}\n\n/** Curated card for the `discovery` variant. */\nexport interface DiscoveryCard {\n id: string;\n label: string;\n description?: string;\n href: string;\n icon?: ReactNode;\n /**\n * Accent token. `auto` rotates through the brand palette by row index.\n * Defaults to `auto`.\n */\n accent?: 'violet' | 'purple' | 'magenta' | 'blue' | 'auto';\n badge?: string;\n}\n\nexport interface FacetOption {\n value: string;\n label: string;\n}\n\nexport interface FacetGroup {\n /** Stable key — e.g. `\"insurance\"`, `\"language\"`. */\n key: string;\n /** Display label, e.g. `\"Convenzione\"`. */\n label: string;\n /** Empty array = boolean toggle chip. Non-empty = dropdown chip. */\n options: FacetOption[];\n /** Whether multiple options can be selected. Default false. */\n multi?: boolean;\n}\n\nexport interface SearchValue {\n object?: PatientSearchObject;\n place?: PatientSearchPlace;\n when?: WhenFilter;\n facets?: Record<string, string[]>;\n geo?: {\n lat: number;\n lng: number;\n radiusKm: number;\n label?: string;\n };\n}\n\n/** Curated imperative handle — used by the agent adapter. */\nexport interface PatientSearchHandle {\n getVariant: () => PatientSearchVariant;\n reset: () => void;\n /** Move focus to the most-relevant field for the current variant. */\n focusPrimary: () => void;\n}\n\n/* -------------------------------------------------------------------- */\n/* Telemetry */\n/* */\n/* Mirrors the booking-website convention (`window.track?.(event, p)`). */\n/* Soft-fails outside the browser and when no host has wired `track`. */\n/* -------------------------------------------------------------------- */\n\ninterface TrackFn {\n (event: string, payload?: Record<string, unknown>): void;\n}\n\nfunction track(event: string, payload?: Record<string, unknown>): void {\n if (typeof window === 'undefined') return;\n const fn = (window as unknown as { track?: TrackFn }).track;\n try {\n fn?.(event, payload);\n } catch {\n // Telemetry must never throw into the UI. Swallow.\n }\n}\n\n/* -------------------------------------------------------------------- */\n/* Constants */\n/* -------------------------------------------------------------------- */\n\nconst WHEN_OPTIONS: readonly WhenFilter[] = [\n 'today',\n 'tomorrow',\n 'this_week',\n 'anytime',\n] as const;\n\n/** Discrete radius presets per spec (1 / 5 / 10 / 25 / 50 km). */\nconst RADIUS_PRESETS: readonly number[] = [1, 5, 10, 25, 50] as const;\n\nconst DEFAULT_RADIUS_KM = 5;\n\nconst AUTOCOMPLETE_DEBOUNCE_MS = 200;\n\n/** Maps `defaultRadiusKm` to the nearest preset index. */\nfunction radiusToStepIndex(km: number): number {\n let best = 0;\n let bestDelta = Number.POSITIVE_INFINITY;\n RADIUS_PRESETS.forEach((preset, index) => {\n const delta = Math.abs(preset - km);\n if (delta < bestDelta) {\n bestDelta = delta;\n best = index;\n }\n });\n return best;\n}\n\n/* -------------------------------------------------------------------- */\n/* Root variants */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n 'ds:flex ds:w-full ds:flex-col ds:text-[var(--foreground)]',\n {\n variants: {\n variant: {\n 'who-where': 'ds:gap-[var(--spacing-md)]',\n 'who-where-when': 'ds:gap-[var(--spacing-md)]',\n faceted: 'ds:gap-[var(--spacing-md)]',\n discovery: 'ds:gap-[var(--spacing-lg)]',\n },\n },\n defaultVariants: { variant: 'who-where' },\n },\n);\n\n/* -------------------------------------------------------------------- */\n/* Public props */\n/* -------------------------------------------------------------------- */\n\nexport interface PatientSearchProps\n extends\n Omit<\n ComponentPropsWithoutRef<'div'>,\n 'aria-label' | 'onChange' | 'onSubmit'\n >,\n VariantProps<typeof rootVariants> {\n variant: PatientSearchVariant;\n\n // Data loaders — consumer-injected; kit calls them debounced.\n loadObjects: (\n query: string,\n ctx: { signal: AbortSignal },\n ) => Promise<PatientSearchObject[]>;\n loadPlaces: (\n query: string,\n ctx: { signal: AbortSignal },\n ) => Promise<PatientSearchPlace[]>;\n\n // Selection callbacks — the consumer decides whether to route.\n onObjectSelect?: (option: PatientSearchObject) => void;\n onPlaceSelect?: (place: PatientSearchPlace) => void;\n\n // Facet config — used by the `faceted` variant; ignored by others.\n facets?: FacetGroup[];\n\n // Geo config — applies to every variant's WHERE input. The crosshair\n // overlay on the inline-end of the input triggers geolocation on click;\n // on success the input fills with the reverse-geocoded label and a\n // radius slider appears beneath. Consumer-supplied `reverseGeocode` is\n // optional — when omitted, `value.geo.label` falls back to the localised\n // \"Vicino a me\" string.\n /** Default radius in km when geolocation is granted. Snaps to the nearest preset. */\n defaultRadiusKm?: number;\n /** Reverse-geocode lat/lng → human label. Optional. */\n reverseGeocode?: (lat: number, lng: number) => Promise<string>;\n\n // Discovery cards — used by the `discovery` variant.\n discoveryCards?: DiscoveryCard[];\n\n /** Forwarded to the consumer's loaders if they want to constrain by country. */\n countryCode?: 'it' | 'de' | string;\n\n // State — controlled.\n value: SearchValue;\n onChange: (next: SearchValue) => void;\n onSubmit: (value: SearchValue) => void | Promise<void>;\n\n // UX\n submitting?: boolean;\n /** Custom CTA label override; defaults to `ui.patientSearch.submit.cta`. */\n submitLabel?: string;\n /**\n * @default false\n * By default the kit disables the submit button until at least one\n * search dimension is set (object, place, geo, non-anytime when, or\n * a facet). Set to `true` to allow an empty submit — useful for\n * \"browse all\" patterns where the consumer wants to show all results\n * with no initial filter.\n */\n allowEmptySubmit?: boolean;\n\n // a11y\n 'aria-label'?: string;\n /** Agent-readiness instance id. */\n id?: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* WhoField — Autocomplete over union(profession, specialty, operator, */\n/* practice). */\n/* -------------------------------------------------------------------- */\n\ninterface WhoFieldProps {\n value: SearchValue;\n onChange: (next: SearchValue) => void;\n loadObjects: PatientSearchProps['loadObjects'];\n onObjectSelect?: PatientSearchProps['onObjectSelect'];\n inputRef?: React.Ref<HTMLInputElement>;\n}\n\nfunction WhoField({\n value,\n onChange,\n loadObjects,\n onObjectSelect,\n inputRef,\n}: WhoFieldProps) {\n const { t } = useTranslation();\n\n // The Autocomplete owns its own visible text; the kit only intercepts\n // selection events to update `value.object`. We mirror external resets\n // (consumer clears `value.object` programmatically) into the visible text\n // via the controlled `value` prop, but otherwise let typed input flow\n // through Autocomplete's internal state. The local `query` state breaks\n // the controlled-input cycle: typing fires onChange(next), we store\n // `next` locally, the next render passes that exact string back as\n // `value` — no flicker, no truncation.\n const [query, setQuery] = useState<string>(value.object?.label ?? '');\n const selectedValueRef = useRef<string | undefined>(value.object?.value);\n\n useEffect(() => {\n // Sync external selection changes (e.g. consumer-driven reset) into the\n // visible text. We watch `value.object?.value` rather than `label` so an\n // identity update with the same label doesn't clobber a fresh query.\n if (value.object?.value !== selectedValueRef.current) {\n selectedValueRef.current = value.object?.value;\n setQuery(value.object?.label ?? '');\n }\n }, [value.object?.value, value.object?.label]);\n\n // Adapt consumer's `loadObjects` to Autocomplete's `loadOptions` signature.\n // PatientSearchObject is structurally a superset of OptionShape, so the\n // returned array is safe to pass through as `OptionShape<string>[]`.\n const loadOptions = useCallback(\n async (q: string, { signal }: { signal: AbortSignal; locale: string }) =>\n (await loadObjects(q, { signal })) as OptionShape<string>[],\n [loadObjects],\n );\n\n const handleSelect = (option: OptionShape<string>) => {\n const next: PatientSearchObject = {\n value: option.value,\n label: option.label,\n description: option.description,\n __payload: (option as PatientSearchObject).__payload,\n };\n setQuery(next.label);\n selectedValueRef.current = next.value;\n onChange({ ...value, object: next });\n track('patient_search_object_selected', {\n value: next.value,\n type:\n next.__payload && typeof next.__payload === 'object'\n ? (next.__payload as { type?: string }).type\n : undefined,\n });\n onObjectSelect?.(next);\n };\n\n // Autocomplete's public props don't include `onFocus`, so we capture focus\n // on a wrapping <div> via the bubbling event from the embedded input.\n // `data-component=\"patient-search-field-who\"` lets agents target the\n // wrapper for instrumentation hooks without depending on Autocomplete's\n // internal structure.\n const focusTrackedRef = useRef(false);\n return (\n <div\n data-component=\"patient-search-field\"\n data-field=\"who\"\n onFocus={() => {\n if (focusTrackedRef.current) return;\n focusTrackedRef.current = true;\n track('patient_search_object_focused');\n }}\n onBlur={(e) => {\n // Reset the once-per-mount focus flag only when focus leaves the\n // wrapper entirely (no relatedTarget inside our subtree).\n if (\n e.currentTarget &&\n !e.currentTarget.contains(e.relatedTarget as Node | null)\n ) {\n focusTrackedRef.current = false;\n }\n }}\n >\n <Autocomplete\n ref={inputRef}\n value={query}\n onChange={(next) => {\n setQuery(next);\n // User edited the input after selection — drop the structured\n // selection so submit can't reuse stale data.\n if (value.object && next !== value.object.label) {\n selectedValueRef.current = undefined;\n onChange({ ...value, object: undefined });\n }\n }}\n onSelect={handleSelect}\n loadOptions={loadOptions}\n debounceMs={AUTOCOMPLETE_DEBOUNCE_MS}\n placeholder={t('patientSearch.who.placeholder')}\n aria-label={t('patientSearch.who.label')}\n startAdornment={<SearchIcon aria-hidden=\"true\" />}\n />\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* WhereField — Autocomplete over the consumer's places provider. */\n/* -------------------------------------------------------------------- */\n\ninterface WhereFieldProps {\n value: SearchValue;\n onChange: (next: SearchValue) => void;\n loadPlaces: PatientSearchProps['loadPlaces'];\n onPlaceSelect?: PatientSearchProps['onPlaceSelect'];\n inputRef?: React.Ref<HTMLInputElement>;\n /**\n * Default radius (km) applied on a successful geolocate. Snaps to the\n * nearest preset (1 / 5 / 10 / 25 / 50). Defaults to 5 km.\n */\n defaultRadiusKm?: number;\n /** Reverse-geocode lat/lng → human label. Optional. */\n reverseGeocode?: PatientSearchProps['reverseGeocode'];\n}\n\ntype GeoStatus = 'pristine' | 'requesting' | 'granted' | 'denied';\n\nconst geoTriggerClasses = [\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:size-8 ds:rounded-[var(--radius-sm)]',\n 'ds:text-[color:var(--muted-foreground)] ds:hover:text-[color:var(--primary)]',\n 'ds:hover:bg-[color-mix(in_srgb,var(--primary)_8%,transparent)]',\n 'ds:aria-pressed:text-[color:var(--primary)]',\n 'ds:aria-pressed:bg-[color-mix(in_srgb,var(--primary)_12%,transparent)]',\n 'ds:disabled:opacity-50 ds:disabled:cursor-not-allowed',\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-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n].join(' ');\n\n/**\n * Unified WHERE input — every variant uses it.\n *\n * Layout per state:\n * - pristine: `[MapPin] City... [LocateFixed]`\n * - granted : `[MapPin] Milano, Italia [X] [LocateFixed]` + radius slider beneath\n * - denied : `[MapPin] City... ` + Alert beneath\n *\n * The MapPin on the inline-start is the conventional \"this is a location\n * field\" marker. The LocateFixed crosshair on the inline-end is the\n * geolocate trigger — these are deliberately different glyphs (Doctolib /\n * Google Maps / Booking pattern) so users don't conflate the field marker\n * with the action.\n */\nfunction WhereField({\n value,\n onChange,\n loadPlaces,\n onPlaceSelect,\n inputRef,\n defaultRadiusKm = DEFAULT_RADIUS_KM,\n reverseGeocode,\n}: WhereFieldProps) {\n const { t } = useTranslation();\n const [query, setQuery] = useState<string>(value.place?.label ?? '');\n const selectedValueRef = useRef<string | undefined>(value.place?.value);\n const initialStatus: GeoStatus = value.geo ? 'granted' : 'pristine';\n const [status, setStatus] = useState<GeoStatus>(initialStatus);\n\n useEffect(() => {\n if (value.place?.value !== selectedValueRef.current) {\n selectedValueRef.current = value.place?.value;\n setQuery(value.place?.label ?? '');\n }\n }, [value.place?.value, value.place?.label]);\n\n const loadOptions = useCallback(\n async (q: string, { signal }: { signal: AbortSignal; locale: string }) =>\n (await loadPlaces(q, { signal })) as OptionShape<string>[],\n [loadPlaces],\n );\n\n const handleSelect = (option: OptionShape<string>) => {\n const place: PatientSearchPlace = {\n value: option.value,\n label: option.label,\n lat: (option as PatientSearchPlace).lat,\n lng: (option as PatientSearchPlace).lng,\n };\n setQuery(place.label);\n selectedValueRef.current = place.value;\n // Typing or selecting a city through the autocomplete cancels any\n // active geolocate — the consumer's search API should now run plain\n // text search rather than lat/lng-radius.\n onChange({ ...value, place, geo: undefined });\n if (status === 'granted') setStatus('pristine');\n track('patient_search_place_selected', { value: place.value });\n onPlaceSelect?.(place);\n };\n\n const requestLocation = () => {\n if (typeof window === 'undefined' || !navigator?.geolocation) {\n setStatus('denied');\n track('patient_search_geo_denied', { reason: 'unsupported' });\n return;\n }\n setStatus('requesting');\n track('patient_search_place_focused');\n navigator.geolocation.getCurrentPosition(\n async (position) => {\n const { latitude, longitude } = position.coords;\n let label: string | undefined;\n try {\n label = await reverseGeocode?.(latitude, longitude);\n } catch {\n label = undefined;\n }\n const km = defaultRadiusKm;\n const resolvedLabel = label ?? t('patientSearch.geo.fallbackLabel');\n // Mirror the geo label into `value.place` so the visible input\n // shows the reverse-geocoded text. `value.geo` carries the\n // structured lat/lng/radius the consumer's search API actually\n // needs. The synthetic place carries a `geo:` value prefix so\n // the consumer can distinguish a geo-derived place from a\n // user-selected Google Places entry.\n const syntheticPlace: PatientSearchPlace = {\n value: `geo:${latitude.toFixed(4)},${longitude.toFixed(4)}`,\n label: resolvedLabel,\n lat: latitude,\n lng: longitude,\n };\n onChange({\n ...value,\n place: syntheticPlace,\n geo: {\n lat: latitude,\n lng: longitude,\n radiusKm: km,\n label: resolvedLabel,\n },\n });\n setStatus('granted');\n track('patient_search_geo_granted', { radiusKm: km });\n },\n () => {\n setStatus('denied');\n track('patient_search_geo_denied', { reason: 'user' });\n },\n { enableHighAccuracy: false, maximumAge: 60_000, timeout: 10_000 },\n );\n };\n\n const clearGeo = () => {\n onChange({ ...value, geo: undefined, place: undefined });\n setQuery('');\n selectedValueRef.current = undefined;\n setStatus('pristine');\n };\n\n const isGranted = status === 'granted' && Boolean(value.geo);\n const isRequesting = status === 'requesting';\n const isDenied = status === 'denied';\n\n const focusTrackedRef = useRef(false);\n // Overlay button(s) that sit visually inside the inline-end of the\n // input. Rendered as siblings of the Autocomplete (not inside the\n // `endAdornment` slot, which the kit wraps in `aria-hidden` and so\n // cannot host an interactive trigger). The decorative spacer in\n // `endAdornment` carves out room so typed text doesn't run under\n // the overlay.\n // Gate the geo-assist UI on the consumer wiring `reverseGeocode`.\n // Without it, clicking \"Vicino a me\" would set value.geo to lat/lng\n // coordinates the kit can't translate into a human-readable label —\n // the patient sees coordinates or nothing. Presence of the dep\n // implies intent.\n const geoEnabled = Boolean(reverseGeocode);\n const overlay =\n isDenied || !geoEnabled ? null : (\n <span className=\"ds:absolute ds:end-[var(--spacing-xs)] ds:top-1/2 ds:-translate-y-1/2 ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n {isGranted ? (\n <button\n type=\"button\"\n aria-label={t('patientSearch.geo.clear')}\n onClick={clearGeo}\n className={geoTriggerClasses}\n >\n <X aria-hidden=\"true\" className=\"ds:size-4\" />\n </button>\n ) : null}\n <button\n type=\"button\"\n aria-label={t('patientSearch.geo.cta')}\n aria-pressed={isGranted || undefined}\n disabled={isRequesting}\n onClick={isGranted ? clearGeo : requestLocation}\n className={geoTriggerClasses}\n >\n {/*\n The geo trigger uses `LocateFixed` (target-with-dot crosshair)\n rather than `MapPin`. The WHERE input's start-adornment is the\n `MapPin` (\"this is a location field\"); reusing the pin glyph\n here would visually conflate the field marker with the action.\n Crosshair = \"centre on me\" — Doctolib / Google Maps / Booking\n all use this distinction.\n */}\n <LocateFixed aria-hidden=\"true\" className=\"ds:size-4\" />\n </button>\n </span>\n );\n\n return (\n <div\n data-component=\"patient-search-field\"\n data-field=\"where\"\n onFocus={() => {\n if (focusTrackedRef.current) return;\n focusTrackedRef.current = true;\n track('patient_search_place_focused');\n }}\n onBlur={(e) => {\n if (\n e.currentTarget &&\n !e.currentTarget.contains(e.relatedTarget as Node | null)\n ) {\n focusTrackedRef.current = false;\n }\n }}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n <div className=\"ds:relative\">\n <Autocomplete\n ref={inputRef}\n value={query}\n onChange={(next) => {\n setQuery(next);\n if (value.place && next !== value.place.label) {\n selectedValueRef.current = undefined;\n // Manual edit cancels any active geolocate too — the typed\n // text owns the field now.\n onChange({ ...value, place: undefined, geo: undefined });\n if (status === 'granted') setStatus('pristine');\n }\n }}\n onSelect={handleSelect}\n loadOptions={loadOptions}\n debounceMs={AUTOCOMPLETE_DEBOUNCE_MS}\n placeholder={t('patientSearch.where.placeholder')}\n aria-label={t('patientSearch.where.label')}\n startAdornment={<MapPin aria-hidden=\"true\" />}\n endAdornment={\n isDenied ? undefined : (\n // Decorative spacer — reserves inline-end room so the typed\n // text doesn't run under the absolutely-positioned overlay.\n // Autocomplete wraps endAdornment in `aria-hidden`, which is\n // correct for this empty span. Granted-state shows an extra\n // clear-X chip, so the spacer doubles in width when granted.\n <span\n aria-hidden=\"true\"\n className={\n isGranted\n ? 'ds:inline-block ds:size-[4.5rem]'\n : 'ds:inline-block ds:size-8'\n }\n />\n )\n }\n />\n {overlay}\n </div>\n {isDenied ? (\n // Alert defaults to `role=\"status\"` + `aria-live=\"polite\"` for the\n // `info` variant — the user hears why the field reverted to plain\n // text when their browser blocked the geolocation prompt.\n <Alert variant=\"info\">\n <Alert.Description>{t('patientSearch.geo.denied')}</Alert.Description>\n </Alert>\n ) : null}\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* GeoRadiusBar — full-width radius slider rendered beneath the top */\n/* row when geo-assist is active. Lifted out of `WhereField` so the */\n/* WHO/WHERE/Submit row stays at single-row height when the slider is */\n/* visible (release notes 0.30.2 — see `ds-kit-followup-from-0.30.0`). */\n/* -------------------------------------------------------------------- */\n\ninterface GeoRadiusBarProps {\n value: SearchValue;\n onChange: (next: SearchValue) => void;\n defaultRadiusKm?: number;\n}\n\nfunction GeoRadiusBar({\n value,\n onChange,\n defaultRadiusKm = DEFAULT_RADIUS_KM,\n}: GeoRadiusBarProps) {\n const { t } = useTranslation();\n const [radiusIndex, setRadiusIndex] = useState<number>(() =>\n radiusToStepIndex(value.geo?.radiusKm ?? defaultRadiusKm),\n );\n if (!value.geo) return null;\n const commitRadius = (index: number) => {\n const km = RADIUS_PRESETS[index] ?? defaultRadiusKm;\n onChange({ ...value, geo: { ...value.geo!, radiusKm: km } });\n track('patient_search_radius_changed', { radiusKm: km });\n };\n const formatRadius = (n: number) => {\n const km = RADIUS_PRESETS[Math.round(n)] ?? defaultRadiusKm;\n return t('patientSearch.geo.radiusFormat', { km });\n };\n return (\n <div\n data-component=\"patient-search-geo-radius-bar\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pb-[var(--spacing-md)]\"\n >\n <span\n id=\"patient-search-radius-label\"\n className=\"type-meta ds:text-[color:var(--muted-foreground)]\"\n >\n {t('patientSearch.geo.radius')}\n </span>\n <Slider\n value={[radiusIndex]}\n min={0}\n max={RADIUS_PRESETS.length - 1}\n step={1}\n marks={RADIUS_PRESETS.map((km, index) => ({\n value: index,\n label: `${km}`,\n }))}\n formatValue={formatRadius}\n alwaysShowValue\n aria-labelledby=\"patient-search-radius-label\"\n onValueChange={(next) => setRadiusIndex(next[0] ?? 0)}\n onValueCommit={(next) => commitRadius(next[0] ?? 0)}\n />\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* WhenChipStrip — radiogroup of {today, tomorrow, this_week, anytime} */\n/* */\n/* Hand-rolled radiogroup: `role=\"radiogroup\"` on the container, button */\n/* children carry `role=\"radio\"` + `aria-checked`, arrow-key navigation */\n/* moves focus + selection. Visually presents as suggestion-style chips. */\n/* -------------------------------------------------------------------- */\n\nconst whenChipVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n 'ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]',\n 'ds:rounded-[var(--radius-full)]',\n 'type-label ds:font-[var(--font-weight-medium)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-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-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' '),\n {\n variants: {\n state: {\n idle: 'ds:bg-[var(--secondary)] ds:text-[var(--foreground)] ds:border ds:border-[color:var(--border)] ds:hover:bg-[color-mix(in_srgb,var(--primary)_8%,var(--secondary))]',\n selected:\n 'ds:bg-[var(--primary)] ds:text-[var(--primary-foreground)] ds:border ds:border-transparent ds:font-[var(--font-weight-semibold)]',\n },\n },\n defaultVariants: { state: 'idle' },\n },\n);\n\ninterface WhenChipStripProps {\n value?: WhenFilter;\n onChange: (next: WhenFilter) => void;\n}\n\nfunction WhenChipStrip({ value, onChange }: WhenChipStripProps) {\n const { t, i18n } = useTranslation();\n const groupId = useId();\n const isRTL = i18n.dir() === 'rtl';\n\n const buttonRefs = useRef<Map<WhenFilter, HTMLButtonElement>>(new Map());\n\n const focusAt = useCallback((opt: WhenFilter) => {\n buttonRefs.current.get(opt)?.focus();\n }, []);\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLButtonElement>, current: WhenFilter) => {\n const idx = WHEN_OPTIONS.indexOf(current);\n const total = WHEN_OPTIONS.length;\n const forwardKey = isRTL ? 'ArrowLeft' : 'ArrowRight';\n const backwardKey = isRTL ? 'ArrowRight' : 'ArrowLeft';\n switch (event.key) {\n case forwardKey:\n case 'ArrowDown': {\n event.preventDefault();\n const next = WHEN_OPTIONS[(idx + 1) % total];\n onChange(next);\n track('patient_search_when_selected', { when: next });\n focusAt(next);\n break;\n }\n case backwardKey:\n case 'ArrowUp': {\n event.preventDefault();\n const next = WHEN_OPTIONS[(idx - 1 + total) % total];\n onChange(next);\n track('patient_search_when_selected', { when: next });\n focusAt(next);\n break;\n }\n case 'Home': {\n event.preventDefault();\n const next = WHEN_OPTIONS[0];\n onChange(next);\n track('patient_search_when_selected', { when: next });\n focusAt(next);\n break;\n }\n case 'End': {\n event.preventDefault();\n const next = WHEN_OPTIONS[total - 1];\n onChange(next);\n track('patient_search_when_selected', { when: next });\n focusAt(next);\n break;\n }\n default:\n break;\n }\n },\n [focusAt, isRTL, onChange],\n );\n\n // Roving tabindex — the selected option (or `anytime` fallback) is the only\n // tab stop. Matches the WAI-ARIA radiogroup pattern.\n const rovingOption: WhenFilter = value ?? 'anytime';\n\n const labelKeyFor: Record<WhenFilter, string> = {\n today: 'patientSearch.when.today',\n tomorrow: 'patientSearch.when.tomorrow',\n this_week: 'patientSearch.when.thisWeek',\n anytime: 'patientSearch.when.anytime',\n };\n\n return (\n <div\n role=\"radiogroup\"\n aria-label={t('patientSearch.when.label')}\n id={groupId}\n className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-xs)]\"\n >\n {WHEN_OPTIONS.map((opt) => {\n const isSelected = value === opt;\n const isRoving = rovingOption === opt;\n const label = t(labelKeyFor[opt]);\n return (\n <button\n key={opt}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n aria-label={label}\n tabIndex={isRoving ? 0 : -1}\n ref={(node) => {\n if (node) buttonRefs.current.set(opt, node);\n else buttonRefs.current.delete(opt);\n }}\n onClick={() => {\n onChange(opt);\n track('patient_search_when_selected', { when: opt });\n }}\n onKeyDown={(e) => handleKeyDown(e, opt)}\n className={whenChipVariants({\n state: isSelected ? 'selected' : 'idle',\n })}\n >\n {label}\n </button>\n );\n })}\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* FacetChipRow — faceted variant. One chip per FacetGroup. */\n/* -------------------------------------------------------------------- */\n\nconst facetChipVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n 'ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]',\n 'ds:rounded-[var(--radius-full)]',\n 'type-label',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-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-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' '),\n {\n variants: {\n // Matches the WHEN chip strip's `selected` look — solid primary fill,\n // primary-foreground text, transparent border. The earlier\n // `color-mix(primary 12% / secondary)` tint read as a washed-out\n // colour over the grey input row and didn't visually signal \"this\n // filter is active\" with enough weight. Solid fill matches the kit's\n // toggle-button precedent (SuggestionChip's `suggestion` intent + the\n // ServiceGrid radio's selected state in Booking).\n state: {\n idle: 'ds:bg-[var(--secondary)] ds:text-[var(--foreground)] ds:border ds:border-[color:var(--border)] ds:hover:bg-[color-mix(in_srgb,var(--primary)_8%,var(--secondary))]',\n active:\n 'ds:bg-[var(--primary)] ds:text-[var(--primary-foreground)] ds:border ds:border-transparent ds:font-[var(--font-weight-semibold)]',\n },\n },\n defaultVariants: { state: 'idle' },\n },\n);\n\ninterface FacetChipRowProps {\n facets: FacetGroup[];\n value: SearchValue['facets'];\n onChange: (next: Record<string, string[]>) => void;\n}\n\nfunction FacetChipRow({ facets, value, onChange }: FacetChipRowProps) {\n const { t } = useTranslation();\n // `selections` is memoised because `value ?? {}` allocates a fresh `{}`\n // whenever the consumer passes `undefined`, which would otherwise invalidate\n // every downstream useMemo / useCallback that depends on it.\n const selections = useMemo(() => value ?? {}, [value]);\n\n const hasAnyActive = useMemo(\n () => Object.values(selections).some((arr) => arr && arr.length > 0),\n [selections],\n );\n\n const updateFacet = (key: string, values: string[]) => {\n const next = { ...selections };\n if (values.length === 0) delete next[key];\n else next[key] = values;\n onChange(next);\n track('patient_search_facet_changed', { key, values });\n };\n\n const toggleBoolean = (key: string) => {\n const isActive = (selections[key]?.length ?? 0) > 0;\n updateFacet(key, isActive ? [] : ['true']);\n };\n\n const toggleOption = (group: FacetGroup, option: string) => {\n const current = selections[group.key] ?? [];\n const active = current.includes(option);\n let next: string[];\n if (group.multi) {\n next = active\n ? current.filter((v) => v !== option)\n : [...current, option];\n } else {\n next = active ? [] : [option];\n }\n updateFacet(group.key, next);\n };\n\n return (\n <div\n role=\"group\"\n aria-label={t('patientSearch.facets.label')}\n className=\"ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-xs)]\"\n >\n {facets.map((group) => {\n const isBoolean = group.options.length === 0;\n const current = selections[group.key] ?? [];\n const isActive = current.length > 0;\n\n if (isBoolean) {\n return (\n <button\n key={group.key}\n type=\"button\"\n aria-pressed={isActive}\n aria-label={group.label}\n onClick={() => toggleBoolean(group.key)}\n className={facetChipVariants({\n state: isActive ? 'active' : 'idle',\n })}\n >\n <span>{group.label}</span>\n </button>\n );\n }\n\n const countSuffix = isActive ? ` (${current.length})` : '';\n\n return (\n <DropdownMenu.Root key={group.key}>\n <DropdownMenu.Trigger asChild>\n <button\n type=\"button\"\n aria-label={`${group.label}${countSuffix}`}\n className={facetChipVariants({\n state: isActive ? 'active' : 'idle',\n })}\n >\n <span>\n {group.label}\n {countSuffix}\n </span>\n <ChevronDown\n aria-hidden=\"true\"\n className=\"ds:size-3.5 ds:shrink-0\"\n />\n </button>\n </DropdownMenu.Trigger>\n <DropdownMenu.Content sideOffset={8} align=\"start\">\n {group.options.map((option) => (\n <DropdownMenu.CheckboxItem\n key={option.value}\n checked={current.includes(option.value)}\n onSelect={(event) => {\n // Keep the menu open for multi-select facets so the user\n // can tick multiple boxes without re-opening.\n if (group.multi) event.preventDefault();\n toggleOption(group, option.value);\n }}\n >\n {option.label}\n </DropdownMenu.CheckboxItem>\n ))}\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n })}\n {hasAnyActive ? (\n <button\n type=\"button\"\n onClick={() => {\n onChange({});\n track('patient_search_facet_changed', { key: '*', values: [] });\n }}\n className={[\n 'ds:ms-[var(--spacing-xs)] ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:min-h-[var(--min-target-size)] ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)]',\n 'type-label ds:text-[color:var(--muted-foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:hover:text-[color:var(--foreground)]',\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-[length:var(--focus-ring-offset)]',\n ].join(' ')}\n >\n <X aria-hidden=\"true\" className=\"ds:size-3.5\" />\n {t('patientSearch.facets.clear')}\n </button>\n ) : null}\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* DiscoveryGrid — responsive grid of curated cards. */\n/* -------------------------------------------------------------------- */\n\nconst WHEEL_ACCENTS = ['violet', 'purple', 'magenta', 'blue'] as const;\ntype WheelAccent = (typeof WHEEL_ACCENTS)[number];\n\nconst discoveryCardVariants = cva(\n [\n 'ds:relative ds:flex ds:h-full ds:flex-col ds:gap-[var(--spacing-sm)]',\n 'ds:rounded-[var(--radius-lg)] ds:overflow-hidden',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n 'ds:pt-[var(--spacing-md)] ds:pb-[var(--spacing-md)]',\n 'ds:bg-[var(--card)] ds:text-[var(--card-foreground)]',\n 'ds:border ds:border-[color:var(--card-border)]',\n 'ds:shadow-[var(--shadow-sm)]',\n 'ds:transition-[transform,box-shadow] ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:motion-safe:hover:-translate-y-0.5',\n 'ds:hover:shadow-[var(--shadow-md)]',\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-[length:var(--focus-ring-offset)]',\n 'ds:no-underline',\n ].join(' '),\n);\n\nconst accentStripVariants = cva(\n 'ds:absolute ds:inset-inline-0 ds:top-0 ds:h-[3px] ds:pointer-events-none',\n {\n variants: {\n accent: {\n violet: 'ds:bg-[var(--color-violet-500)]',\n purple: 'ds:bg-[var(--color-purple-500)]',\n magenta: 'ds:bg-[var(--color-magenta-500)]',\n blue: 'ds:bg-[var(--color-blue-500)]',\n },\n },\n defaultVariants: { accent: 'violet' },\n },\n);\n\nconst iconChipVariants = cva(\n 'ds:inline-flex ds:size-9 ds:items-center ds:justify-center ds:rounded-[var(--radius-md)]',\n {\n variants: {\n accent: {\n violet:\n 'ds:bg-[color-mix(in_srgb,var(--color-violet-500)_12%,var(--background))] ds:text-[var(--color-violet-700)]',\n purple:\n 'ds:bg-[color-mix(in_srgb,var(--color-purple-500)_12%,var(--background))] ds:text-[var(--color-purple-700)]',\n magenta:\n 'ds:bg-[color-mix(in_srgb,var(--color-magenta-500)_12%,var(--background))] ds:text-[var(--color-magenta-700)]',\n blue: 'ds:bg-[color-mix(in_srgb,var(--color-blue-500)_12%,var(--background))] ds:text-[var(--color-blue-700)]',\n },\n },\n defaultVariants: { accent: 'violet' },\n },\n);\n\nfunction resolveAccent(\n cardAccent: DiscoveryCard['accent'] | undefined,\n index: number,\n): WheelAccent {\n if (cardAccent && cardAccent !== 'auto') return cardAccent;\n return WHEEL_ACCENTS[index % WHEEL_ACCENTS.length];\n}\n\ninterface DiscoveryGridProps {\n cards: DiscoveryCard[];\n}\n\nfunction DiscoveryGrid({ cards }: DiscoveryGridProps) {\n const { t } = useTranslation();\n\n if (cards.length === 0) {\n return (\n <p className=\"type-body-sm ds:text-[color:var(--muted-foreground)] ds:m-0\">\n {t('patientSearch.discovery.empty')}\n </p>\n );\n }\n\n return (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <h3 className=\"type-title-card ds:text-[var(--foreground)] ds:m-0\">\n {t('patientSearch.discovery.heading')}\n </h3>\n <ul\n className={[\n 'ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)]',\n 'ds:md:grid-cols-2 ds:lg:grid-cols-3',\n 'ds:m-0 ds:p-0 ds:list-none',\n ].join(' ')}\n >\n {cards.map((card, index) => {\n const accent = resolveAccent(card.accent, index);\n return (\n <li key={card.id}>\n <a\n href={card.href}\n data-component=\"patient-search-card\"\n data-card-id={card.id}\n aria-label={\n card.description\n ? `${card.label} — ${card.description}`\n : card.label\n }\n onClick={() =>\n track('patient_search_discovery_card_clicked', {\n cardId: card.id,\n })\n }\n className={discoveryCardVariants()}\n >\n <span\n aria-hidden=\"true\"\n className={accentStripVariants({ accent })}\n />\n <span className=\"ds:flex ds:items-center ds:gap-[var(--spacing-sm)]\">\n <span\n aria-hidden=\"true\"\n className={iconChipVariants({ accent })}\n >\n {card.icon ?? (\n <Stethoscope className=\"ds:size-4\" aria-hidden=\"true\" />\n )}\n </span>\n {card.badge ? (\n // The badge sits inside a softly-tinted accent bubble\n // (15% accent + 85% background). The text colour must\n // contrast with that mix, not with the accent's canonical\n // foreground — `--accent-foreground` is calibrated against\n // the 100% accent surface and fails AA on the 15% tint.\n // Use `--foreground` which contrasts with the\n // mostly-background mix in both light and dark themes.\n <span className=\"type-meta ds:ms-auto ds:inline-flex ds:items-center ds:rounded-[var(--radius-full)] ds:bg-[color-mix(in_srgb,var(--accent)_15%,var(--background))] ds:text-[color:var(--foreground)] ds:font-[var(--font-weight-semibold)] ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]\">\n {card.badge}\n </span>\n ) : null}\n </span>\n <span className=\"type-label ds:font-[var(--font-weight-semibold)] ds:text-[var(--foreground)]\">\n {card.label}\n </span>\n {card.description ? (\n <span className=\"type-body-sm ds:text-[color:var(--muted-foreground)]\">\n {card.description}\n </span>\n ) : null}\n </a>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* SubmitButton — shared CTA. Disabled only when every dimension is unset. */\n/* -------------------------------------------------------------------- */\n\ninterface SubmitButtonProps {\n value: SearchValue;\n variant: PatientSearchVariant;\n submitting?: boolean;\n submitLabel?: string;\n allowEmptySubmit?: boolean;\n onSubmit: (value: SearchValue) => void | Promise<void>;\n}\n\nfunction hasAnyDimension(value: SearchValue): boolean {\n if (value.object) return true;\n if (value.place) return true;\n if (value.geo) return true;\n if (value.when && value.when !== 'anytime') return true;\n if (value.facets) {\n for (const arr of Object.values(value.facets)) {\n if (arr && arr.length > 0) return true;\n }\n }\n return false;\n}\n\nfunction SubmitButton({\n value,\n variant,\n submitting,\n submitLabel,\n allowEmptySubmit,\n onSubmit,\n}: SubmitButtonProps) {\n const { t } = useTranslation();\n const disabled = submitting || (!allowEmptySubmit && !hasAnyDimension(value));\n const label = submitLabel ?? t('patientSearch.submit.cta');\n return (\n <Button\n type=\"button\"\n intent=\"primary\"\n size=\"lg\"\n startIcon={<SearchIcon aria-hidden=\"true\" />}\n loading={submitting}\n disabled={disabled}\n onClick={() => {\n track('patient_search_submitted', {\n variant,\n hasObject: Boolean(value.object),\n hasPlace: Boolean(value.place),\n hasGeo: Boolean(value.geo),\n when: value.when,\n facetKeys: value.facets ? Object.keys(value.facets) : [],\n });\n void onSubmit(value);\n }}\n >\n {label}\n </Button>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* Variant body props */\n/* -------------------------------------------------------------------- */\n\ninterface VariantBodyProps {\n variant: PatientSearchVariant;\n value: SearchValue;\n onChange: (next: SearchValue) => void;\n onSubmit: PatientSearchProps['onSubmit'];\n loadObjects: PatientSearchProps['loadObjects'];\n loadPlaces: PatientSearchProps['loadPlaces'];\n onObjectSelect?: PatientSearchProps['onObjectSelect'];\n onPlaceSelect?: PatientSearchProps['onPlaceSelect'];\n facets?: FacetGroup[];\n defaultRadiusKm: number;\n reverseGeocode?: PatientSearchProps['reverseGeocode'];\n discoveryCards: DiscoveryCard[];\n submitting?: boolean;\n submitLabel?: string;\n allowEmptySubmit?: boolean;\n whoRef: React.RefObject<HTMLInputElement>;\n whereRef: React.RefObject<HTMLInputElement>;\n /**\n * Accessible name applied to the inner `<Card>` so its underlying\n * `<article>` landmark isn't unnamed when nested inside the\n * `role=\"region\"` root. Without this every variant produces an\n * unnamed article — a `landmark-unique` axe hazard once a consumer\n * page renders adjacent articles. Resolved once at the root with\n * `t('patientSearch.regionLabel')` and threaded through so the\n * variant bodies don't each have to call `useTranslation()`.\n */\n cardLabel: string;\n}\n\n/* -------------------------------------------------------------------- */\n/* WhoWhereBody — baseline variant. WHO + WHERE + submit. */\n/* -------------------------------------------------------------------- */\n\nfunction WhoWhereBody(props: VariantBodyProps) {\n const {\n variant,\n value,\n onChange,\n onSubmit,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n submitting,\n submitLabel,\n allowEmptySubmit,\n whoRef,\n whereRef,\n defaultRadiusKm,\n reverseGeocode,\n cardLabel,\n } = props;\n return (\n <Card variant=\"elevated\" aria-label={cardLabel}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:md:flex-row ds:md:items-start\">\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhoField\n value={value}\n onChange={onChange}\n loadObjects={loadObjects}\n onObjectSelect={onObjectSelect}\n inputRef={whoRef}\n />\n </div>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhereField\n value={value}\n onChange={onChange}\n loadPlaces={loadPlaces}\n onPlaceSelect={onPlaceSelect}\n defaultRadiusKm={defaultRadiusKm}\n reverseGeocode={reverseGeocode}\n inputRef={whereRef}\n />\n </div>\n <SubmitButton\n value={value}\n variant={variant}\n submitting={submitting}\n submitLabel={submitLabel}\n allowEmptySubmit={allowEmptySubmit}\n onSubmit={onSubmit}\n />\n </div>\n <GeoRadiusBar\n value={value}\n onChange={onChange}\n defaultRadiusKm={defaultRadiusKm}\n />\n </div>\n </Card>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* WhoWhereWhenBody — WHO + WHERE + submit + WHEN chip strip below. */\n/* -------------------------------------------------------------------- */\n\nfunction WhoWhereWhenBody(props: VariantBodyProps) {\n const {\n variant,\n value,\n onChange,\n onSubmit,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n submitting,\n submitLabel,\n allowEmptySubmit,\n whoRef,\n whereRef,\n defaultRadiusKm,\n reverseGeocode,\n cardLabel,\n } = props;\n return (\n <Card variant=\"elevated\" aria-label={cardLabel}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:p-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:md:flex-row ds:md:items-start\">\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhoField\n value={value}\n onChange={onChange}\n loadObjects={loadObjects}\n onObjectSelect={onObjectSelect}\n inputRef={whoRef}\n />\n </div>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhereField\n value={value}\n onChange={onChange}\n loadPlaces={loadPlaces}\n onPlaceSelect={onPlaceSelect}\n defaultRadiusKm={defaultRadiusKm}\n reverseGeocode={reverseGeocode}\n inputRef={whereRef}\n />\n </div>\n <SubmitButton\n value={value}\n variant={variant}\n submitting={submitting}\n submitLabel={submitLabel}\n allowEmptySubmit={allowEmptySubmit}\n onSubmit={onSubmit}\n />\n </div>\n <GeoRadiusBar\n value={value}\n onChange={onChange}\n defaultRadiusKm={defaultRadiusKm}\n />\n <WhenChipStrip\n value={value.when}\n onChange={(next) => onChange({ ...value, when: next })}\n />\n </div>\n </Card>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* FacetedBody — WHO + WHERE + submit + facet chip row below. */\n/* -------------------------------------------------------------------- */\n\nfunction FacetedBody(props: VariantBodyProps) {\n const {\n variant,\n value,\n onChange,\n onSubmit,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n facets,\n submitting,\n submitLabel,\n allowEmptySubmit,\n whoRef,\n whereRef,\n defaultRadiusKm,\n reverseGeocode,\n cardLabel,\n } = props;\n return (\n <Card variant=\"elevated\" aria-label={cardLabel}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:p-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:md:flex-row ds:md:items-start\">\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhoField\n value={value}\n onChange={onChange}\n loadObjects={loadObjects}\n onObjectSelect={onObjectSelect}\n inputRef={whoRef}\n />\n </div>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhereField\n value={value}\n onChange={onChange}\n loadPlaces={loadPlaces}\n onPlaceSelect={onPlaceSelect}\n defaultRadiusKm={defaultRadiusKm}\n reverseGeocode={reverseGeocode}\n inputRef={whereRef}\n />\n </div>\n <SubmitButton\n value={value}\n variant={variant}\n submitting={submitting}\n submitLabel={submitLabel}\n allowEmptySubmit={allowEmptySubmit}\n onSubmit={onSubmit}\n />\n </div>\n <GeoRadiusBar\n value={value}\n onChange={onChange}\n defaultRadiusKm={defaultRadiusKm}\n />\n {facets && facets.length > 0 ? (\n <FacetChipRow\n facets={facets}\n value={value.facets}\n onChange={(next) => onChange({ ...value, facets: next })}\n />\n ) : null}\n </div>\n </Card>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* DiscoveryBody — hero search + card grid. */\n/* -------------------------------------------------------------------- */\n\nfunction DiscoveryBody(props: VariantBodyProps) {\n const {\n variant,\n value,\n onChange,\n onSubmit,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n discoveryCards,\n submitting,\n submitLabel,\n allowEmptySubmit,\n whoRef,\n whereRef,\n defaultRadiusKm,\n reverseGeocode,\n cardLabel,\n } = props;\n return (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]\">\n <Card variant=\"elevated\" aria-label={cardLabel}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:p-[var(--spacing-md)] ds:md:flex-row ds:md:items-stretch\">\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhoField\n value={value}\n onChange={onChange}\n loadObjects={loadObjects}\n onObjectSelect={onObjectSelect}\n inputRef={whoRef}\n />\n </div>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <WhereField\n value={value}\n onChange={onChange}\n loadPlaces={loadPlaces}\n onPlaceSelect={onPlaceSelect}\n defaultRadiusKm={defaultRadiusKm}\n reverseGeocode={reverseGeocode}\n inputRef={whereRef}\n />\n </div>\n <SubmitButton\n value={value}\n variant={variant}\n submitting={submitting}\n submitLabel={submitLabel}\n allowEmptySubmit={allowEmptySubmit}\n onSubmit={onSubmit}\n />\n </div>\n <GeoRadiusBar\n value={value}\n onChange={onChange}\n defaultRadiusKm={defaultRadiusKm}\n />\n </Card>\n <DiscoveryGrid cards={discoveryCards} />\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* PatientSearch */\n/* -------------------------------------------------------------------- */\n\nexport const PatientSearch = forwardRef<\n PatientSearchHandle,\n PatientSearchProps\n>(function PatientSearch(\n {\n variant,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n facets,\n defaultRadiusKm = DEFAULT_RADIUS_KM,\n reverseGeocode,\n discoveryCards,\n countryCode,\n value,\n onChange,\n onSubmit,\n submitting,\n submitLabel,\n allowEmptySubmit,\n 'aria-label': ariaLabel,\n id,\n className,\n ...rest\n },\n ref,\n) {\n const { t } = useTranslation();\n\n const rootRef = useRef<HTMLDivElement>(null);\n\n // Primary-field refs — `focusPrimary` jumps to whichever input the variant\n // emphasises (WHO in most variants; no auto-focus on `discovery`).\n const whoRef = useRef<HTMLInputElement>(null);\n const whereRef = useRef<HTMLInputElement>(null);\n\n // Apply `when: anytime` default once on mount, but only for the variant\n // that actually renders the WHEN chip strip (`who-where-when`). The\n // `faceted` variant exposes facets, not WHEN — seeding `when: 'anytime'`\n // for it would silently pollute the consumer's value with a field the\n // form never displays.\n const seededWhenRef = useRef(false);\n useEffect(() => {\n if (seededWhenRef.current) return;\n if (variant !== 'who-where-when') return;\n if (value.when !== undefined) {\n seededWhenRef.current = true;\n return;\n }\n seededWhenRef.current = true;\n onChange({ ...value, when: 'anytime' });\n // We intentionally seed only on first render — the consumer can clear\n // back to `undefined` if they want, and we won't re-seed.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [variant]);\n\n // Apply `facets: {}` default once on mount for the faceted variant so\n // the chip row treats undefined and empty consistently.\n const seededFacetsRef = useRef(false);\n useEffect(() => {\n if (seededFacetsRef.current) return;\n if (variant !== 'faceted') return;\n if (value.facets !== undefined) {\n seededFacetsRef.current = true;\n return;\n }\n seededFacetsRef.current = true;\n onChange({ ...value, facets: {} });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [variant]);\n\n // Exposure event — fired once per variant assignment so PostHog can\n // record A/B assignment even for users who bounce before submitting.\n const exposedRef = useRef<PatientSearchVariant | null>(null);\n useEffect(() => {\n if (exposedRef.current === variant) return;\n exposedRef.current = variant;\n track('patient_search_variant_exposed', { variant, country: countryCode });\n }, [variant, countryCode]);\n\n // Imperative handle — minimal surface (`getVariant`, `reset`,\n // `focusPrimary`). The `value` reset goes through the consumer-owned\n // `onChange` so the parent's state stays the source of truth.\n //\n // The same handle is surfaced two ways: through the React `ref` (for\n // consumers driving the kit directly) and through `useAgentRegistration`\n // (for the future agent / MCP runtime). Both reach the same object so a\n // direct `ref.current?.reset()` and an `agentRegistry.invoke('reset')`\n // do the same thing.\n const handle = useMemo<PatientSearchHandle>(\n () => ({\n getVariant: () => variant,\n reset: () => {\n onChange({});\n },\n focusPrimary: () => {\n // Discovery deliberately doesn't auto-focus the search field on\n // mount; agents hitting `focus_primary` still get pulled into WHO\n // since that's the natural starting point for any keyboard-driven\n // flow once the user opts in.\n requestAnimationFrame(() => {\n whoRef.current?.focus();\n });\n },\n }),\n [variant, onChange],\n );\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(patientSearchAgent, handle, id);\n\n const resolvedAriaLabel = ariaLabel ?? t('patientSearch.regionLabel');\n\n const bodyProps: VariantBodyProps = {\n variant,\n value,\n onChange,\n onSubmit,\n loadObjects,\n loadPlaces,\n onObjectSelect,\n onPlaceSelect,\n facets,\n defaultRadiusKm,\n reverseGeocode,\n discoveryCards: discoveryCards ?? [],\n submitting,\n submitLabel,\n allowEmptySubmit,\n whoRef,\n whereRef,\n cardLabel: resolvedAriaLabel,\n };\n\n const body = (() => {\n switch (variant) {\n case 'who-where':\n return <WhoWhereBody {...bodyProps} />;\n case 'who-where-when':\n return <WhoWhereWhenBody {...bodyProps} />;\n case 'faceted':\n return <FacetedBody {...bodyProps} />;\n case 'discovery':\n return <DiscoveryBody {...bodyProps} />;\n /* c8 ignore next 2 */\n default:\n return null;\n }\n })();\n\n return (\n <div\n ref={rootRef}\n role=\"region\"\n aria-label={resolvedAriaLabel}\n id={id}\n data-component=\"patient-search\"\n data-component-id={id}\n data-variant={variant}\n className={rootVariants({ variant, className })}\n {...rest}\n >\n {body}\n </div>\n );\n});\n\nPatientSearch.displayName = 'PatientSearch';\n"],"names":["__iconNode","LocateFixed","createLucideIcon","patientSearchAgent","handle","track","event","payload","fn","WHEN_OPTIONS","RADIUS_PRESETS","DEFAULT_RADIUS_KM","AUTOCOMPLETE_DEBOUNCE_MS","radiusToStepIndex","km","best","bestDelta","preset","index","delta","rootVariants","cva","WhoField","value","onChange","loadObjects","onObjectSelect","inputRef","t","useTranslation","query","setQuery","useState","_a","selectedValueRef","useRef","_b","useEffect","_c","_d","loadOptions","useCallback","q","signal","handleSelect","option","next","focusTrackedRef","jsx","e","Autocomplete","SearchIcon","geoTriggerClasses","WhereField","loadPlaces","onPlaceSelect","defaultRadiusKm","reverseGeocode","initialStatus","status","setStatus","place","requestLocation","position","latitude","longitude","label","resolvedLabel","syntheticPlace","clearGeo","isGranted","isRequesting","isDenied","overlay","jsxs","X","MapPin","Alert","GeoRadiusBar","radiusIndex","setRadiusIndex","commitRadius","formatRadius","n","Slider","whenChipVariants","WhenChipStrip","i18n","groupId","useId","isRTL","buttonRefs","focusAt","opt","handleKeyDown","current","idx","total","forwardKey","backwardKey","rovingOption","labelKeyFor","isSelected","isRoving","node","facetChipVariants","FacetChipRow","facets","selections","useMemo","hasAnyActive","arr","updateFacet","key","values","toggleBoolean","isActive","toggleOption","group","active","v","isBoolean","countSuffix","DropdownMenu","ChevronDown","WHEEL_ACCENTS","discoveryCardVariants","accentStripVariants","iconChipVariants","resolveAccent","cardAccent","DiscoveryGrid","cards","card","accent","Stethoscope","hasAnyDimension","SubmitButton","variant","submitting","submitLabel","allowEmptySubmit","onSubmit","disabled","Button","WhoWhereBody","props","whoRef","whereRef","cardLabel","Card","WhoWhereWhenBody","FacetedBody","DiscoveryBody","discoveryCards","PatientSearch","forwardRef","countryCode","ariaLabel","id","className","rest","ref","rootRef","seededWhenRef","seededFacetsRef","exposedRef","useImperativeHandle","useAgentRegistration","resolvedAriaLabel","bodyProps","body"],"mappings":"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","x_google_ignoreList":[0]}
|