@alfadocs/ui-kit-debug 0.30.1 → 0.30.2
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/{booking-Bw9eOU4k.js → booking-CBaSujVP.js} +496 -464
- package/dist/_chunks/booking-CBaSujVP.js.map +1 -0
- package/dist/_chunks/{patient-search-DuSoGG2t.js → patient-search-ZpHN-pgJ.js} +520 -475
- package/dist/_chunks/patient-search-ZpHN-pgJ.js.map +1 -0
- package/dist/_chunks/{reviews-panel-CFttsfuC.js → reviews-panel-D-zjPKFL.js} +198 -178
- package/dist/_chunks/reviews-panel-D-zjPKFL.js.map +1 -0
- package/dist/agent-catalog.json +1 -1
- package/dist/components/booking/booking-types.d.ts +16 -0
- package/dist/components/booking/booking-types.d.ts.map +1 -1
- package/dist/components/booking/booking.d.ts.map +1 -1
- package/dist/components/booking/index.js +1 -1
- package/dist/components/patient-search/index.js +1 -1
- package/dist/components/patient-search/patient-search.d.ts.map +1 -1
- package/dist/components/reviews-panel/index.js +1 -1
- package/dist/components/reviews-panel/reviews-panel.d.ts +8 -0
- package/dist/components/reviews-panel/reviews-panel.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/package.json +1 -1
- package/dist/_chunks/booking-Bw9eOU4k.js.map +0 -1
- package/dist/_chunks/patient-search-DuSoGG2t.js.map +0 -1
- package/dist/_chunks/reviews-panel-CFttsfuC.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"patient-search-DuSoGG2t.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 const [radiusIndex, setRadiusIndex] = useState<number>(() =>\n radiusToStepIndex(value.geo?.radiusKm ?? defaultRadiusKm),\n );\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 = RADIUS_PRESETS[radiusIndex] ?? 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 commitRadius = (index: number) => {\n const km = RADIUS_PRESETS[index] ?? defaultRadiusKm;\n if (value.geo) {\n onChange({ ...value, geo: { ...value.geo, radiusKm: km } });\n }\n track('patient_search_radius_changed', { radiusKm: km });\n };\n\n const formatRadius = (n: number) => {\n const km = RADIUS_PRESETS[Math.round(n)] ?? defaultRadiusKm;\n return t('patientSearch.geo.radiusFormat', { km });\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 const overlay = isDenied ? 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 {isGranted ? (\n // Slider marks render with `translate(-50%)` so half of each\n // end-mark (\"1\" and \"50\" labels) sits outside the slider's\n // bounding box. The parent Card uses `overflow: hidden`, which\n // would clip them — the inline padding pulls the rail inset by\n // half a label width so the overhang lands inside the card.\n <div 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 <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 ) : null}\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/* 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)] ds:md:flex-row ds:md:items-center\">\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 </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-center\">\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 <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-center\">\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 {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 </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","radiusIndex","setRadiusIndex","place","requestLocation","position","latitude","longitude","label","resolvedLabel","syntheticPlace","clearGeo","commitRadius","formatRadius","n","isGranted","isRequesting","isDenied","overlay","jsxs","X","MapPin","Slider","Alert","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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"reviews-panel-CFttsfuC.js","sources":["../../src/components/reviews-panel/reviews-panel.agent.ts","../../src/components/reviews-panel/reviews-panel.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — ReviewsPanel. */\n/* */\n/* Replaces the legacy patient-reviews Carousel with a single panel that */\n/* surfaces an aggregate score, a star-bucket filter, and a vertical list */\n/* of compact review cards. The adapter exposes the active filter and the */\n/* visible review count so a host agent can drive the panel and observe */\n/* what the user actually sees after the filter is applied. The reviews */\n/* themselves remain owned by the consumer — they never flow through the */\n/* adapter (PHI: pseudonyms + free-text comments stay on the page). */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { ReviewFilter, ReviewsPanelHandle } from './reviews-panel';\n\nexport const reviewsPanelAgent: AgentAdapter<ReviewsPanelHandle> = {\n id: 'reviews-panel',\n capabilities: ['filter'],\n state: {\n filter: {\n type: 'string',\n descriptionKey: 'ui.agent.reviewsPanel.state.filter',\n description:\n 'Active star-bucket filter — `\"all\"` or an integer 1..5. Reads the controlled `filter` prop.',\n read: (handle) => handle.getFilter(),\n },\n visibleCount: {\n type: 'number',\n descriptionKey: 'ui.agent.reviewsPanel.state.visibleCount',\n description:\n 'Number of reviews currently rendered after the filter is applied.',\n read: (handle) => handle.getVisibleCount(),\n },\n },\n actions: {\n set_filter: {\n safety: 'read',\n argsType: '{ filter: \"all\" | 1 | 2 | 3 | 4 | 5 }',\n descriptionKey: 'ui.agent.reviewsPanel.actions.setFilter',\n description:\n 'Narrow the visible review list to a star bucket, or `\"all\"` to clear the filter.',\n invoke: (handle, args: { filter: ReviewFilter }) => {\n handle.setFilter(args.filter);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'reviews-panel',\n description: 'Marks the ReviewsPanel 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 } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { ChevronDown } from 'lucide-react';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { reviewsPanelAgent } from './reviews-panel.agent';\nimport { Rating } from '../rating/rating';\nimport { Card } from '../card/card';\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from '../collapsible/collapsible';\nimport { DescriptionList } from '../description-list/description-list';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { Button } from '../button/button';\n\n/* -------------------------------------------------------------------- */\n/* ReviewsPanel */\n/* */\n/* Replaces the legacy patient-reviews Carousel with a two-column panel */\n/* — a star-bucket filter sidebar + a vertical list of compact review */\n/* cards — that surfaces the aggregate score, the volume, and (via a */\n/* per-card Collapsible) the rich per-axis breakdown alfaDocs already */\n/* collects but the carousel buried. */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* -------------------------------------------------------------------- */\n\nexport interface ReviewItem {\n id: string;\n /** When the review was authored (ISO 8601). */\n authoredAt: string;\n /** Anonymised author display (\"Marco T.\", \"Anna_M\"). Falls back to a localised \"Anonymous\". */\n pseudonym?: string;\n /** 0..max — drives the visible star summary on the compact card. */\n overallScore: number;\n /** Short free-text comment shown on the compact card. */\n comment?: string;\n /** Practice reply (revealed in the expanded panel only). */\n reply?: string;\n /** Optional per-axis breakdown — when present, the card grows an\n * \"Expand\" affordance that reveals these in a DescriptionList. */\n breakdown?: {\n appointmentScore?: number;\n communicationScore?: number;\n staffScore?: number;\n overallScore?: number;\n };\n /** Optional categorical follow-ups, surfaced inside the breakdown. */\n futureVisit?: 'yes' | 'no' | 'unsure';\n recommendation?: 'yes' | 'no' | 'unsure';\n}\n\nexport interface ReviewsAggregate {\n /** Total review count across all star buckets (may exceed the loaded set when paginated). */\n count: number;\n /** Average score across the full population (drives the header `Rating`). */\n averageScore: number;\n /**\n * Optional histogram — per-bucket count keyed by integer star value 1..5.\n * When omitted, the panel derives buckets from the loaded `reviews` array\n * (best-effort: paginated consumers SHOULD provide this explicitly).\n */\n histogram?: Record<1 | 2 | 3 | 4 | 5, number>;\n}\n\nexport type ReviewFilter = 'all' | 1 | 2 | 3 | 4 | 5;\n\nexport interface ReviewsPanelHandle {\n getFilter: () => ReviewFilter;\n setFilter: (next: ReviewFilter) => void;\n /** Total visible after filter is applied. */\n getVisibleCount: () => number;\n}\n\nexport interface ReviewsPanelProps extends Omit<\n ComponentPropsWithoutRef<'section'>,\n 'aria-label' | 'children'\n> {\n reviews: ReviewItem[];\n aggregate: ReviewsAggregate;\n\n /**\n * Active star filter. Controlled — consumer owns it for shareability via\n * URL query strings. Omit for uncontrolled (panel-managed) filtering.\n */\n filter?: ReviewFilter;\n onFilterChange?: (next: ReviewFilter) => void;\n\n /**\n * Optional intro callout shown above the list (e.g. \"Riepilogo delle\n * opinioni — i pazienti raccontano…\"). Render-prop slot so the consumer\n * can drop in `<Alert>` / `<Callout>` / plain `<p>`.\n */\n introSlot?: ReactNode;\n\n /** Optional header right-end action (e.g. a \"Leave a review\" button). */\n headerActionSlot?: ReactNode;\n\n /** Override the default region aria-label. */\n 'aria-label'?: string;\n\n /** Agent-readiness id. */\n id?: string;\n\n className?: string;\n\n /**\n * Hide the filter sidebar — useful when the panel mounts under ~640px\n * width or inside a narrow column. Defaults to false.\n */\n hideFilter?: boolean;\n\n /** Render skeletons for the header and the list. */\n loading?: boolean;\n}\n\n/* -------------------------------------------------------------------- */\n/* Constants & helpers */\n/* -------------------------------------------------------------------- */\n\nconst FILTER_VALUES: readonly ReviewFilter[] = ['all', 5, 4, 3, 2, 1] as const;\n\nfunction bucketOf(score: number): 1 | 2 | 3 | 4 | 5 {\n const n = Math.min(5, Math.max(1, Math.floor(score)));\n return n as 1 | 2 | 3 | 4 | 5;\n}\n\nfunction deriveHistogram(\n reviews: ReviewItem[],\n): Record<1 | 2 | 3 | 4 | 5, number> {\n const out: Record<1 | 2 | 3 | 4 | 5, number> = {\n 1: 0,\n 2: 0,\n 3: 0,\n 4: 0,\n 5: 0,\n };\n for (const r of reviews) out[bucketOf(r.overallScore)] += 1;\n return out;\n}\n\n/* -------------------------------------------------------------------- */\n/* FilterSidebar — hand-rolled radiogroup with roving tabindex */\n/* */\n/* Mirrors `ServiceGrid` in `booking.tsx`: the WAI-ARIA RadioGroup */\n/* pattern with full-row click target + icon column. NOT Radix */\n/* `RadioGroup` because we need a row layout with a non-text leading */\n/* glyph (the star ramp) and a trailing count. */\n/* */\n/* Activation is automatic — Arrow keys move BOTH focus and selection, */\n/* so a keyboard user feels the same responsiveness as a mouse user. */\n/* That's the right call here because filter activation is cheap (re- */\n/* renders the visible list); contrast with the booking ServiceGrid */\n/* where activation cascades through downstream selection state. */\n/* -------------------------------------------------------------------- */\n\nconst filterRowVariants = cva(\n [\n 'ds:relative ds:flex ds:w-full ds:items-center ds:justify-between',\n 'ds:gap-[var(--spacing-sm)]',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:rounded-[var(--radius-md)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)]',\n 'ds:pt-[var(--spacing-2xs)] ds:pb-[var(--spacing-2xs)]',\n 'ds:text-start',\n 'ds:transition-[background-color,color]',\n 'ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)]',\n 'ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[var(--ring)]',\n 'ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:cursor-pointer',\n ].join(' '),\n {\n variants: {\n state: {\n idle: 'ds:hover:bg-[var(--muted)]/30 ds:text-[var(--foreground)]',\n selected: [\n 'ds:bg-[var(--primary)]',\n 'ds:text-[var(--primary-foreground)]',\n 'ds:font-[var(--font-weight-semibold)]',\n ].join(' '),\n },\n },\n defaultVariants: { state: 'idle' },\n },\n);\n\ninterface FilterSidebarProps {\n filter: ReviewFilter;\n histogram: Record<1 | 2 | 3 | 4 | 5, number>;\n totalCount: number;\n onChange: (next: ReviewFilter) => void;\n}\n\nfunction FilterSidebar({\n filter,\n histogram,\n totalCount,\n onChange,\n}: FilterSidebarProps) {\n const { t } = useTranslation();\n const labelId = useId();\n\n // Roving tabindex tracks where keyboard focus is. We use AUTOMATIC\n // activation: arrow keys move focus AND fire onChange (see the rationale\n // above the component). To keep the visible aria-checked row and the\n // tab-stop aligned when the parent changes `filter` externally (e.g.\n // from a `?filter=4` URL parameter on page load), sync focusIndex to\n // the current filter via the effect below — without it, the tab stop\n // could land on `All` while the highlight shows `4`.\n const [focusIndex, setFocusIndex] = useState<number>(() =>\n Math.max(0, FILTER_VALUES.indexOf(filter)),\n );\n useEffect(() => {\n const i = FILTER_VALUES.indexOf(filter);\n if (i >= 0) setFocusIndex(i);\n }, [filter]);\n\n const rowRefs = useRef<Map<number, HTMLButtonElement>>(new Map());\n const focusAt = useCallback((index: number) => {\n rowRefs.current.get(index)?.focus();\n }, []);\n\n const total = FILTER_VALUES.length;\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLButtonElement>, index: number) => {\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowRight': {\n event.preventDefault();\n const next = (index + 1) % total;\n setFocusIndex(next);\n focusAt(next);\n onChange(FILTER_VALUES[next]);\n break;\n }\n case 'ArrowUp':\n case 'ArrowLeft': {\n event.preventDefault();\n const prev = (index - 1 + total) % total;\n setFocusIndex(prev);\n focusAt(prev);\n onChange(FILTER_VALUES[prev]);\n break;\n }\n case 'Home': {\n event.preventDefault();\n setFocusIndex(0);\n focusAt(0);\n onChange(FILTER_VALUES[0]);\n break;\n }\n case 'End': {\n event.preventDefault();\n const last = total - 1;\n setFocusIndex(last);\n focusAt(last);\n onChange(FILTER_VALUES[last]);\n break;\n }\n case 'Enter':\n case ' ': {\n event.preventDefault();\n onChange(FILTER_VALUES[index]);\n break;\n }\n default:\n break;\n }\n },\n [total, focusAt, onChange],\n );\n\n return (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n <h3\n id={labelId}\n className=\"type-label ds:font-[var(--font-weight-semibold)] ds:text-[var(--foreground)] ds:m-0\"\n >\n {t('reviewsPanel.filterTitle')}\n </h3>\n <div\n role=\"radiogroup\"\n aria-labelledby={labelId}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n {FILTER_VALUES.map((val, index) => {\n const isAll = val === 'all';\n const stars = isAll ? 0 : (val as 1 | 2 | 3 | 4 | 5);\n const count = isAll\n ? totalCount\n : histogram[stars as 1 | 2 | 3 | 4 | 5];\n const isSelected = val === filter;\n const isFocused = focusIndex === index;\n const ariaLabel = isAll\n ? t('reviewsPanel.filter.allLabel', { count })\n : t('reviewsPanel.filter.starsLabel', { stars, count });\n\n return (\n <button\n key={String(val)}\n ref={(node) => {\n if (node) rowRefs.current.set(index, node);\n else rowRefs.current.delete(index);\n }}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n aria-label={ariaLabel}\n tabIndex={isFocused ? 0 : -1}\n onClick={() => {\n setFocusIndex(index);\n onChange(val);\n }}\n onFocus={() => setFocusIndex(index)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n data-filter-bucket={String(val)}\n className={filterRowVariants({\n state: isSelected ? 'selected' : 'idle',\n })}\n >\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]\"\n >\n {isAll ? (\n <span className=\"type-label\">\n {t('reviewsPanel.filter.allShort')}\n </span>\n ) : (\n <Rating value={stars} size=\"sm\" decorative />\n )}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"type-meta ds:text-[var(--muted-foreground)]\"\n >\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* ReviewCard — compact card with optional Collapsible breakdown */\n/* -------------------------------------------------------------------- */\n\ninterface ReviewCardProps {\n review: ReviewItem;\n locale: string;\n}\n\nfunction ReviewCard({ review, locale }: ReviewCardProps) {\n const { t } = useTranslation();\n const triggerId = useId();\n const contentId = useId();\n const [open, setOpen] = useState(false);\n\n const breakdownHasValues = Boolean(\n review.breakdown && Object.values(review.breakdown).some((v) => v != null),\n );\n const hasDetails =\n Boolean(review.reply) ||\n breakdownHasValues ||\n Boolean(review.futureVisit) ||\n Boolean(review.recommendation);\n\n const formattedDate = useMemo(() => {\n if (!review.authoredAt) return '';\n try {\n const parsed = new Date(review.authoredAt);\n if (Number.isNaN(parsed.getTime())) return review.authoredAt;\n return new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(\n parsed,\n );\n } catch {\n return review.authoredAt;\n }\n }, [review.authoredAt, locale]);\n\n const pseudonym = review.pseudonym ?? t('reviewsPanel.anonymous');\n\n // Compose the full Card body. The Collapsible wraps the footer + the\n // hidden content so both share the same Radix Root and the Trigger's\n // `data-state` reaches the Content automatically (used for the height\n // animation). The Footer + Content are direct Card children so the\n // Card root's flex-col places them in order.\n const footerAndDetails = (\n <Collapsible\n open={hasDetails ? open : false}\n onOpenChange={hasDetails ? setOpen : undefined}\n >\n <Card.Footer>\n <span className=\"type-meta ds:text-[var(--muted-foreground)]\">\n {formattedDate}\n </span>\n {hasDetails ? (\n <CollapsibleTrigger asChild>\n <button\n id={triggerId}\n type=\"button\"\n data-review-trigger\n className=\"ds:ms-auto ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)] ds:rounded-[var(--radius-sm)] ds:ps-[var(--spacing-2xs)] ds:pe-[var(--spacing-2xs)] ds:pt-[var(--spacing-2xs)] ds:pb-[var(--spacing-2xs)] ds:bg-transparent ds:border-0 ds:text-[length:var(--font-size-sm)] ds:text-[var(--primary)] ds:hover:underline ds:cursor-pointer ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]\"\n >\n {open\n ? t('reviewsPanel.card.collapse')\n : t('reviewsPanel.card.expand')}\n <ChevronDown\n aria-hidden=\"true\"\n className={[\n 'ds:size-4 ds:transition-transform ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n open ? 'ds:rotate-180' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n />\n </button>\n </CollapsibleTrigger>\n ) : null}\n </Card.Footer>\n {hasDetails ? (\n <CollapsibleContent id={contentId} aria-labelledby={triggerId}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pb-[var(--spacing-md)]\">\n {breakdownHasValues ||\n review.futureVisit ||\n review.recommendation ? (\n <DescriptionList layout=\"inline\" density=\"compact\">\n {review.breakdown?.appointmentScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.appointment')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating\n value={review.breakdown.appointmentScore}\n size=\"sm\"\n />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.breakdown?.communicationScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.communication')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating\n value={review.breakdown.communicationScore}\n size=\"sm\"\n />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.breakdown?.staffScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.staff')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating value={review.breakdown.staffScore} size=\"sm\" />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.breakdown?.overallScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.overall')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating value={review.breakdown.overallScore} size=\"sm\" />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.futureVisit ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.futureVisit')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n {t(`reviewsPanel.answer.${review.futureVisit}`)}\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.recommendation ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.recommendation')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n {t(`reviewsPanel.answer.${review.recommendation}`)}\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n </DescriptionList>\n ) : null}\n {review.reply ? (\n <div className=\"ds:rounded-[var(--radius-sm)] ds:bg-[var(--muted)]/40 ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]\">\n <p className=\"type-label ds:m-0 ds:mb-[var(--spacing-2xs)] ds:text-[var(--muted-foreground)] ds:font-[var(--font-weight-semibold)]\">\n {t('reviewsPanel.replyTitle')}\n </p>\n <p className=\"type-body-sm ds:m-0 ds:text-[var(--foreground)] ds:[white-space:pre-line]\">\n {review.reply}\n </p>\n </div>\n ) : null}\n </div>\n </CollapsibleContent>\n ) : null}\n </Collapsible>\n );\n\n return (\n <Card variant=\"default\" className=\"ds:shadow-[var(--shadow-sm)]\">\n <Card.Header>\n <div className=\"ds:flex ds:items-start ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <h4 className=\"type-title-item ds:m-0 ds:text-[var(--foreground)]\">\n {pseudonym}\n </h4>\n <Rating value={review.overallScore} size=\"sm\" />\n </div>\n </Card.Header>\n {review.comment ? (\n <Card.Body>\n <p className=\"type-body ds:m-0 ds:text-[var(--foreground)] ds:line-clamp-2\">\n {review.comment}\n </p>\n </Card.Body>\n ) : null}\n {footerAndDetails}\n </Card>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* PanelSkeleton — loading state */\n/* -------------------------------------------------------------------- */\n\nfunction PanelSkeleton({ hideFilter }: { hideFilter: boolean }) {\n // The parent <section> already carries `aria-busy=\"true\"`, which is the\n // canonical SR signal for \"this region is loading\". Don't add `role=status`\n // + `aria-live=polite` here too — pairing both would double-announce on\n // mount. Skeleton itself is `aria-hidden`.\n return (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <Skeleton variant=\"text\" width=\"40%\" />\n <Skeleton variant=\"text\" width=\"20%\" />\n </div>\n <div\n className={\n hideFilter\n ? 'ds:flex ds:flex-col ds:gap-[var(--spacing-md)]'\n : 'ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:md:grid-cols-[minmax(0,30%)_minmax(0,1fr)]'\n }\n >\n {!hideFilter ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n {Array.from({ length: 6 }, (_, i) => (\n <Skeleton key={`rp-fs-${i}`} variant=\"rounded\" height=\"2.5rem\" />\n ))}\n </div>\n ) : null}\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {Array.from({ length: 3 }, (_, i) => (\n <Skeleton key={`rp-rs-${i}`} variant=\"rounded\" height=\"6rem\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* Root variants */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n 'ds:flex ds:w-full ds:flex-col ds:gap-[var(--spacing-md)] ds:text-[var(--foreground)]',\n);\n\n/* -------------------------------------------------------------------- */\n/* ReviewsPanel */\n/* -------------------------------------------------------------------- */\n\nexport const ReviewsPanel = forwardRef<HTMLElement, ReviewsPanelProps>(\n (\n {\n reviews,\n aggregate,\n filter: filterProp,\n onFilterChange,\n introSlot,\n headerActionSlot,\n 'aria-label': ariaLabel,\n id,\n className,\n hideFilter = false,\n loading = false,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const locale = i18n.language ?? 'en';\n\n // Controlled / uncontrolled filter handling. When `filterProp` is\n // undefined the panel manages the filter internally — the consumer can\n // mount the panel and never wire `filter` / `onFilterChange` for the\n // common case where the URL doesn't carry the filter state. When the\n // prop IS provided, the consumer owns the truth.\n const [internalFilter, setInternalFilter] = useState<ReviewFilter>('all');\n const filter = filterProp ?? internalFilter;\n\n const handleFilterChange = useCallback(\n (next: ReviewFilter) => {\n if (filterProp === undefined) setInternalFilter(next);\n onFilterChange?.(next);\n },\n [filterProp, onFilterChange],\n );\n\n // Prefer the consumer-provided histogram; fall back to deriving from the\n // loaded reviews. Pagination consumers SHOULD pass an explicit histogram\n // so the per-bucket counts reflect server-side totals rather than only\n // what's loaded on this page.\n const histogram = useMemo(\n () => aggregate.histogram ?? deriveHistogram(reviews),\n [aggregate.histogram, reviews],\n );\n\n const visibleReviews = useMemo(() => {\n if (filter === 'all') return reviews;\n return reviews.filter((r) => bucketOf(r.overallScore) === filter);\n }, [reviews, filter]);\n\n /* Agent registration — exposes filter + visible count via refs so the\n imperative handle returns the latest values without re-creating the\n handle on every render. */\n const latestFilterRef = useRef(filter);\n latestFilterRef.current = filter;\n const latestVisibleCountRef = useRef(visibleReviews.length);\n latestVisibleCountRef.current = visibleReviews.length;\n\n const agentHandle = useMemo<ReviewsPanelHandle>(\n () => ({\n getFilter: () => latestFilterRef.current,\n setFilter: (next) => handleFilterChange(next),\n getVisibleCount: () => latestVisibleCountRef.current,\n }),\n [handleFilterChange],\n );\n\n const rootRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => rootRef.current as HTMLElement, []);\n useAgentRegistration(reviewsPanelAgent, agentHandle, id);\n\n const resolvedAriaLabel = ariaLabel ?? t('reviewsPanel.regionLabel');\n const dataFilter = String(filter);\n\n /* ---------------------------- Loading ---------------------------- */\n if (loading) {\n return (\n <section\n ref={rootRef as React.Ref<HTMLElement>}\n aria-label={resolvedAriaLabel}\n aria-busy=\"true\"\n id={id}\n data-component=\"reviews-panel\"\n data-component-id={id}\n data-filter={dataFilter}\n data-state=\"loading\"\n className={rootVariants({ className })}\n {...rest}\n >\n <PanelSkeleton hideFilter={hideFilter} />\n </section>\n );\n }\n\n /* ----------------------- Zero-total empty ------------------------ */\n // No header, no filter — just the EmptyState. The region landmark is\n // still emitted so an agent can locate the empty panel by its\n // data-component marker.\n if (aggregate.count === 0 && reviews.length === 0) {\n return (\n <section\n ref={rootRef as React.Ref<HTMLElement>}\n aria-label={resolvedAriaLabel}\n id={id}\n data-component=\"reviews-panel\"\n data-component-id={id}\n data-filter={dataFilter}\n data-state=\"empty\"\n className={rootVariants({ className })}\n {...rest}\n >\n <EmptyState\n variant=\"no-results\"\n title={t('reviewsPanel.empty.title')}\n description={t('reviewsPanel.empty.description')}\n />\n </section>\n );\n }\n\n const showFilterSidebar = !hideFilter;\n\n return (\n <section\n ref={rootRef as React.Ref<HTMLElement>}\n aria-label={resolvedAriaLabel}\n id={id}\n data-component=\"reviews-panel\"\n data-component-id={id}\n data-filter={dataFilter}\n className={rootVariants({ className })}\n {...rest}\n >\n {/* Header row — plain <div>, not <header>: the panel root is a\n region landmark with an accessible name, so a nested <header>\n would emit a banner-inside-region pair that axe's\n landmark-banner-is-top-level rule flags. */}\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <h2 className=\"type-title-section ds:m-0 ds:text-[var(--foreground)]\">\n {t('reviewsPanel.title')}\n </h2>\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-md)]\">\n {headerActionSlot}\n <Rating\n value={aggregate.averageScore}\n reviews={{ count: aggregate.count }}\n size=\"md\"\n />\n </div>\n </div>\n\n <div\n className={\n showFilterSidebar\n ? 'ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:md:grid-cols-[minmax(0,30%)_minmax(0,1fr)]'\n : 'ds:flex ds:flex-col ds:gap-[var(--spacing-md)]'\n }\n >\n {showFilterSidebar ? (\n <FilterSidebar\n filter={filter}\n histogram={histogram}\n totalCount={aggregate.count}\n onChange={handleFilterChange}\n />\n ) : null}\n\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:min-w-0\">\n {introSlot ? (\n // Plain div, not <aside>: axe's\n // `landmark-complementary-is-top-level` rule fires when an\n // `<aside>` lands inside another landmark (this panel's\n // <section role=\"region\">), and the consumer's introSlot\n // already supplies its own semantics (e.g. an Alert with\n // role=\"status\").\n <div className=\"ds:rounded-[var(--radius-md)] ds:bg-[color-mix(in_srgb,var(--info)_8%,transparent)] ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pt-[var(--spacing-md)] ds:pb-[var(--spacing-md)]\">\n {introSlot}\n </div>\n ) : null}\n\n {visibleReviews.length === 0 ? (\n // `role=\"status\"` already implies `aria-live=\"polite\"` —\n // declaring both would queue two announcements when the\n // filter changes (the radio's aria-checked flip + this\n // container's text). Keeping `role` only.\n <div\n role=\"status\"\n data-state=\"no-match\"\n className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-sm)] ds:rounded-[var(--radius-md)] ds:border ds:border-dashed ds:border-[var(--border)] ds:p-[var(--spacing-lg)] ds:text-center\"\n >\n <p className=\"type-body ds:m-0 ds:text-[var(--muted-foreground)]\">\n {filter === 'all'\n ? t('reviewsPanel.empty.title')\n : t('reviewsPanel.empty.filterDescription', {\n stars: filter,\n })}\n </p>\n {filter !== 'all' ? (\n <Button\n intent=\"secondary\"\n size=\"sm\"\n onClick={() => handleFilterChange('all')}\n >\n {t('reviewsPanel.empty.resetFilter')}\n </Button>\n ) : null}\n </div>\n ) : (\n <ol\n aria-label={t('reviewsPanel.listLabel')}\n className=\"ds:list-none ds:m-0 ds:p-0 ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n {visibleReviews.map((review) => (\n <li key={review.id} data-review-id={review.id}>\n <ReviewCard review={review} locale={locale} />\n </li>\n ))}\n </ol>\n )}\n </div>\n </div>\n </section>\n );\n },\n);\n\nReviewsPanel.displayName = 'ReviewsPanel';\n"],"names":["reviewsPanelAgent","handle","args","FILTER_VALUES","bucketOf","score","deriveHistogram","reviews","out","filterRowVariants","cva","FilterSidebar","filter","histogram","totalCount","onChange","t","useTranslation","labelId","useId","focusIndex","setFocusIndex","useState","useEffect","i","rowRefs","useRef","focusAt","useCallback","index","_a","total","handleKeyDown","event","next","prev","last","jsxs","jsx","val","isAll","stars","count","isSelected","isFocused","ariaLabel","node","e","Rating","ReviewCard","review","locale","triggerId","contentId","open","setOpen","breakdownHasValues","v","hasDetails","formattedDate","useMemo","parsed","pseudonym","footerAndDetails","Collapsible","Card","CollapsibleTrigger","ChevronDown","CollapsibleContent","DescriptionList","_b","_c","_d","PanelSkeleton","hideFilter","Skeleton","_","rootVariants","ReviewsPanel","forwardRef","aggregate","filterProp","onFilterChange","introSlot","headerActionSlot","id","className","loading","rest","ref","i18n","internalFilter","setInternalFilter","handleFilterChange","visibleReviews","r","latestFilterRef","latestVisibleCountRef","agentHandle","rootRef","useImperativeHandle","useAgentRegistration","resolvedAriaLabel","dataFilter","EmptyState","showFilterSidebar","Button"],"mappings":"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"}
|