@alfadocs/ui-kit-debug 1.105.0 → 1.106.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,19 @@
1
- import { jsx as t, jsxs as n, Fragment as K } from "react/jsx-runtime";
2
- import { forwardRef as J, useState as H, useCallback as L, useMemo as C, useRef as F, useImperativeHandle as Q, useId as B, useEffect as W } from "react";
3
- import { c as _ } from "./index-D2ZczOXr.js";
4
- import { useTranslation as E } from "react-i18next";
5
- import { u as X } from "./registry-DvAUVLHh.js";
6
- import { R as k } from "./rating-CGYpg5dj.js";
1
+ import { jsx as t, jsxs as r, Fragment as _ } from "react/jsx-runtime";
2
+ import { forwardRef as Q, useState as E, useCallback as B, useMemo as C, useRef as F, useImperativeHandle as W, useId as H, useEffect as X } from "react";
3
+ import { c as $ } from "./index-D2ZczOXr.js";
4
+ import { useTranslation as M } from "react-i18next";
5
+ import { u as Y } from "./registry-DvAUVLHh.js";
6
+ import { R as P } from "./rating-CGYpg5dj.js";
7
7
  import { C as A } from "./card-C85_Ndhb.js";
8
- import { C as Y, b as Z, a as ee } from "./collapsible-DVPM46bq.js";
9
- import { D as l } from "./description-list-BxzDN9o2.js";
10
- import { E as te } from "./empty-state-DwJ5YNzz.js";
8
+ import { C as Z, b as ee, a as te } from "./collapsible-DVPM46bq.js";
9
+ import { D as o } from "./description-list-BxzDN9o2.js";
10
+ import { E as se } from "./empty-state-DwJ5YNzz.js";
11
11
  import { S as T } from "./skeleton-tTWJpvAq.js";
12
- import { B as se } from "./button-DohK_Ak_.js";
12
+ import { B as ae } from "./button-DohK_Ak_.js";
13
13
  import { P as re } from "./panel-header-CbtlTe85.js";
14
- import { S as ae } from "./star-Bf9L2Uot.js";
15
- import { C as ne } from "./chevron-down-DWE7GSq9.js";
16
- const ie = {
14
+ import { S as ne } from "./star-Bf9L2Uot.js";
15
+ import { C as ie } from "./chevron-down-DWE7GSq9.js";
16
+ const le = {
17
17
  id: "reviews-panel",
18
18
  capabilities: ["filter"],
19
19
  state: {
@@ -55,8 +55,8 @@ const ie = {
55
55
  { args: { filter: 5 }, note: "Show only 5-star reviews." },
56
56
  { args: { filter: "all" }, note: "Clear the star filter." }
57
57
  ],
58
- invoke: (e, i) => {
59
- e.setFilter(i.filter);
58
+ invoke: (e, l) => {
59
+ e.setFilter(l.filter);
60
60
  }
61
61
  }
62
62
  },
@@ -73,21 +73,21 @@ const ie = {
73
73
  }
74
74
  }
75
75
  }, w = ["all", 5, 4, 3, 2, 1];
76
- function $(e) {
76
+ function U(e) {
77
77
  return Math.min(5, Math.max(1, Math.floor(e)));
78
78
  }
79
- function le(e) {
80
- const i = {
79
+ function oe(e) {
80
+ const l = {
81
81
  1: 0,
82
82
  2: 0,
83
83
  3: 0,
84
84
  4: 0,
85
85
  5: 0
86
86
  };
87
- for (const a of e) i[$(a.overallScore)] += 1;
88
- return i;
87
+ for (const p of e) l[U(p.overallScore)] += 1;
88
+ return l;
89
89
  }
90
- const oe = _(
90
+ const de = $(
91
91
  [
92
92
  "ds:relative ds:inline-flex ds:items-center ds:justify-center",
93
93
  "ds:gap-[var(--spacing-xs)]",
@@ -120,59 +120,59 @@ const oe = _(
120
120
  defaultVariants: { state: "idle" }
121
121
  }
122
122
  );
123
- function de({
123
+ function ce({
124
124
  filter: e,
125
- histogram: i,
126
- totalCount: a,
127
- onChange: c
125
+ histogram: l,
126
+ totalCount: p,
127
+ onChange: s
128
128
  }) {
129
- const { t: f } = E("ui"), y = B(), [N, o] = H(
129
+ const { t: f } = M("ui"), y = H(), [k, v] = E(
130
130
  () => Math.max(0, w.indexOf(e))
131
131
  );
132
- W(() => {
133
- const r = w.indexOf(e);
134
- r >= 0 && o(r);
132
+ X(() => {
133
+ const a = w.indexOf(e);
134
+ a >= 0 && v(a);
135
135
  }, [e]);
136
- const u = F(/* @__PURE__ */ new Map()), v = L((r) => {
137
- var d;
138
- (d = u.current.get(r)) == null || d.focus();
139
- }, []), p = w.length, P = L(
140
- (r, d) => {
141
- switch (r.key) {
136
+ const u = F(/* @__PURE__ */ new Map()), m = B((a) => {
137
+ var i;
138
+ (i = u.current.get(a)) == null || i.focus();
139
+ }, []), g = w.length, N = B(
140
+ (a, i) => {
141
+ switch (a.key) {
142
142
  case "ArrowDown":
143
143
  case "ArrowRight": {
144
- r.preventDefault();
145
- const s = (d + 1) % p;
146
- o(s), v(s), c(w[s]);
144
+ a.preventDefault();
145
+ const n = (i + 1) % g;
146
+ v(n), m(n), s(w[n]);
147
147
  break;
148
148
  }
149
149
  case "ArrowUp":
150
150
  case "ArrowLeft": {
151
- r.preventDefault();
152
- const s = (d - 1 + p) % p;
153
- o(s), v(s), c(w[s]);
151
+ a.preventDefault();
152
+ const n = (i - 1 + g) % g;
153
+ v(n), m(n), s(w[n]);
154
154
  break;
155
155
  }
156
156
  case "Home": {
157
- r.preventDefault(), o(0), v(0), c(w[0]);
157
+ a.preventDefault(), v(0), m(0), s(w[0]);
158
158
  break;
159
159
  }
160
160
  case "End": {
161
- r.preventDefault();
162
- const s = p - 1;
163
- o(s), v(s), c(w[s]);
161
+ a.preventDefault();
162
+ const n = g - 1;
163
+ v(n), m(n), s(w[n]);
164
164
  break;
165
165
  }
166
166
  case "Enter":
167
167
  case " ": {
168
- r.preventDefault(), c(w[d]);
168
+ a.preventDefault(), s(w[i]);
169
169
  break;
170
170
  }
171
171
  }
172
172
  },
173
- [p, v, c]
173
+ [g, m, s]
174
174
  );
175
- return /* @__PURE__ */ n(K, { children: [
175
+ return /* @__PURE__ */ r(_, { children: [
176
176
  /* @__PURE__ */ t(
177
177
  "h3",
178
178
  {
@@ -187,27 +187,27 @@ function de({
187
187
  role: "radiogroup",
188
188
  "aria-labelledby": y,
189
189
  className: "ds:flex ds:flex-wrap ds:items-center ds:gap-[var(--spacing-2xs)]",
190
- children: w.map((r, d) => {
191
- const s = r === "all", x = s ? 0 : r, h = s ? a : i[x], S = r === e, V = N === d, m = s ? f("reviewsPanel.filter.allLabel", { count: h }) : f("reviewsPanel.filter.starsLabel", { stars: x, count: h });
192
- return /* @__PURE__ */ n(
190
+ children: w.map((a, i) => {
191
+ const n = a === "all", d = n ? 0 : a, x = n ? p : l[d], b = a === e, V = k === i, z = n ? f("reviewsPanel.filter.allLabel", { count: x }) : f("reviewsPanel.filter.starsLabel", { stars: d, count: x });
192
+ return /* @__PURE__ */ r(
193
193
  "button",
194
194
  {
195
- ref: (b) => {
196
- b ? u.current.set(d, b) : u.current.delete(d);
195
+ ref: (c) => {
196
+ c ? u.current.set(i, c) : u.current.delete(i);
197
197
  },
198
198
  type: "button",
199
199
  role: "radio",
200
- "aria-checked": S,
201
- "aria-label": m,
200
+ "aria-checked": b,
201
+ "aria-label": z,
202
202
  tabIndex: V ? 0 : -1,
203
203
  onClick: () => {
204
- o(d), c(r);
204
+ v(i), s(a);
205
205
  },
206
- onFocus: () => o(d),
207
- onKeyDown: (b) => P(b, d),
208
- "data-filter-bucket": String(r),
209
- className: oe({
210
- state: S ? "selected" : "idle"
206
+ onFocus: () => v(i),
207
+ onKeyDown: (c) => N(c, i),
208
+ "data-filter-bucket": String(a),
209
+ className: de({
210
+ state: b ? "selected" : "idle"
211
211
  }),
212
212
  children: [
213
213
  /* @__PURE__ */ t(
@@ -215,7 +215,7 @@ function de({
215
215
  {
216
216
  "aria-hidden": "true",
217
217
  className: "ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]",
218
- children: s ? /* @__PURE__ */ t("span", { className: "type-label", children: f("reviewsPanel.filter.allShort") }) : /* @__PURE__ */ t(k, { value: x, size: "sm", decorative: !0 })
218
+ children: n ? /* @__PURE__ */ t("span", { className: "type-label", children: f("reviewsPanel.filter.allShort") }) : /* @__PURE__ */ t(P, { value: d, size: "sm", decorative: !0 })
219
219
  }
220
220
  ),
221
221
  /* @__PURE__ */ t(
@@ -232,57 +232,57 @@ function de({
232
232
  // text on the violet pill (~3:1, AA-fail). Match the
233
233
  // row's foreground when selected; slight opacity
234
234
  // keeps the count secondary to the label.
235
- S ? "ds:text-[color:var(--primary-foreground)]" : "ds:text-[var(--muted-foreground)]"
235
+ b ? "ds:text-[color:var(--primary-foreground)]" : "ds:text-[var(--muted-foreground)]"
236
236
  ].join(" "),
237
- children: h
237
+ children: x
238
238
  }
239
239
  )
240
240
  ]
241
241
  },
242
- String(r)
242
+ String(a)
243
243
  );
244
244
  })
245
245
  }
246
246
  )
247
247
  ] });
248
248
  }
249
- function ce({ review: e, locale: i }) {
250
- var r, d, s, x;
251
- const { t: a } = E("ui"), c = B(), f = B(), [y, N] = H(!1), o = !!(e.breakdown && Object.values(e.breakdown).some((h) => h != null)), u = !!e.reply || o || !!e.futureVisit || !!e.recommendation, v = C(() => {
249
+ function ue({ review: e, locale: l, textLang: p }) {
250
+ var i, n, d, x;
251
+ const { t: s } = M("ui"), f = H(), y = H(), [k, v] = E(!1), u = !!(e.breakdown && Object.values(e.breakdown).some((b) => b != null)), m = !!e.reply || u || !!e.futureVisit || !!e.recommendation, g = C(() => {
252
252
  if (!e.authoredAt) return "";
253
253
  try {
254
- const h = new Date(e.authoredAt);
255
- return Number.isNaN(h.getTime()) ? e.authoredAt : new Intl.DateTimeFormat(i, { dateStyle: "medium" }).format(
256
- h
254
+ const b = new Date(e.authoredAt);
255
+ return Number.isNaN(b.getTime()) ? e.authoredAt : new Intl.DateTimeFormat(l, { dateStyle: "medium" }).format(
256
+ b
257
257
  );
258
258
  } catch {
259
259
  return e.authoredAt;
260
260
  }
261
- }, [e.authoredAt, i]), p = e.pseudonym ?? a("reviewsPanel.anonymous"), P = /* @__PURE__ */ n(
262
- Y,
261
+ }, [e.authoredAt, l]), N = e.pseudonym ?? s("reviewsPanel.anonymous"), a = /* @__PURE__ */ r(
262
+ Z,
263
263
  {
264
- open: u ? y : !1,
265
- onOpenChange: u ? N : void 0,
264
+ open: m ? k : !1,
265
+ onOpenChange: m ? v : void 0,
266
266
  children: [
267
- /* @__PURE__ */ n(A.Footer, { children: [
268
- /* @__PURE__ */ t("span", { className: "type-meta ds:text-[var(--muted-foreground)]", children: v }),
269
- u ? /* @__PURE__ */ t(Z, { asChild: !0, children: /* @__PURE__ */ n(
267
+ /* @__PURE__ */ r(A.Footer, { children: [
268
+ /* @__PURE__ */ t("span", { className: "type-meta ds:text-[var(--muted-foreground)]", children: g }),
269
+ m ? /* @__PURE__ */ t(ee, { asChild: !0, children: /* @__PURE__ */ r(
270
270
  "button",
271
271
  {
272
- id: c,
272
+ id: f,
273
273
  type: "button",
274
274
  "data-review-trigger": !0,
275
- "aria-controls": f,
275
+ "aria-controls": y,
276
276
  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(--review-details-foreground)] ds:min-w-[var(--min-target-size)] ds:min-h-[var(--min-target-size)] 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]",
277
277
  children: [
278
- a(y ? "reviewsPanel.card.collapse" : "reviewsPanel.card.expand"),
278
+ s(k ? "reviewsPanel.card.collapse" : "reviewsPanel.card.expand"),
279
279
  /* @__PURE__ */ t(
280
- ne,
280
+ ie,
281
281
  {
282
282
  "aria-hidden": "true",
283
283
  className: [
284
284
  "ds:size-4 ds:transition-transform ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none",
285
- y ? "ds:rotate-180" : ""
285
+ k ? "ds:rotate-180" : ""
286
286
  ].filter(Boolean).join(" ")
287
287
  }
288
288
  )
@@ -290,50 +290,51 @@ function ce({ review: e, locale: i }) {
290
290
  }
291
291
  ) }) : null
292
292
  ] }),
293
- u ? /* @__PURE__ */ t(ee, { id: f, "aria-labelledby": c, children: /* @__PURE__ */ 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)]", children: [
294
- o || e.futureVisit || e.recommendation ? /* @__PURE__ */ n(l, { layout: "inline", density: "compact", children: [
295
- ((r = e.breakdown) == null ? void 0 : r.appointmentScore) != null ? /* @__PURE__ */ n(l.Row, { children: [
296
- /* @__PURE__ */ t(l.Term, { children: a("reviewsPanel.breakdown.appointment") }),
297
- /* @__PURE__ */ t(l.Detail, { children: /* @__PURE__ */ t(
298
- k,
293
+ m ? /* @__PURE__ */ t(te, { id: y, "aria-labelledby": f, children: /* @__PURE__ */ r("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)]", children: [
294
+ u || e.futureVisit || e.recommendation ? /* @__PURE__ */ r(o, { layout: "inline", density: "compact", children: [
295
+ ((i = e.breakdown) == null ? void 0 : i.appointmentScore) != null ? /* @__PURE__ */ r(o.Row, { children: [
296
+ /* @__PURE__ */ t(o.Term, { children: s("reviewsPanel.breakdown.appointment") }),
297
+ /* @__PURE__ */ t(o.Detail, { children: /* @__PURE__ */ t(
298
+ P,
299
299
  {
300
300
  value: e.breakdown.appointmentScore,
301
301
  size: "sm"
302
302
  }
303
303
  ) })
304
304
  ] }) : null,
305
- ((d = e.breakdown) == null ? void 0 : d.communicationScore) != null ? /* @__PURE__ */ n(l.Row, { children: [
306
- /* @__PURE__ */ t(l.Term, { children: a("reviewsPanel.breakdown.communication") }),
307
- /* @__PURE__ */ t(l.Detail, { children: /* @__PURE__ */ t(
308
- k,
305
+ ((n = e.breakdown) == null ? void 0 : n.communicationScore) != null ? /* @__PURE__ */ r(o.Row, { children: [
306
+ /* @__PURE__ */ t(o.Term, { children: s("reviewsPanel.breakdown.communication") }),
307
+ /* @__PURE__ */ t(o.Detail, { children: /* @__PURE__ */ t(
308
+ P,
309
309
  {
310
310
  value: e.breakdown.communicationScore,
311
311
  size: "sm"
312
312
  }
313
313
  ) })
314
314
  ] }) : null,
315
- ((s = e.breakdown) == null ? void 0 : s.staffScore) != null ? /* @__PURE__ */ n(l.Row, { children: [
316
- /* @__PURE__ */ t(l.Term, { children: a("reviewsPanel.breakdown.staff") }),
317
- /* @__PURE__ */ t(l.Detail, { children: /* @__PURE__ */ t(k, { value: e.breakdown.staffScore, size: "sm" }) })
315
+ ((d = e.breakdown) == null ? void 0 : d.staffScore) != null ? /* @__PURE__ */ r(o.Row, { children: [
316
+ /* @__PURE__ */ t(o.Term, { children: s("reviewsPanel.breakdown.staff") }),
317
+ /* @__PURE__ */ t(o.Detail, { children: /* @__PURE__ */ t(P, { value: e.breakdown.staffScore, size: "sm" }) })
318
318
  ] }) : null,
319
- ((x = e.breakdown) == null ? void 0 : x.overallScore) != null ? /* @__PURE__ */ n(l.Row, { children: [
320
- /* @__PURE__ */ t(l.Term, { children: a("reviewsPanel.breakdown.overall") }),
321
- /* @__PURE__ */ t(l.Detail, { children: /* @__PURE__ */ t(k, { value: e.breakdown.overallScore, size: "sm" }) })
319
+ ((x = e.breakdown) == null ? void 0 : x.overallScore) != null ? /* @__PURE__ */ r(o.Row, { children: [
320
+ /* @__PURE__ */ t(o.Term, { children: s("reviewsPanel.breakdown.overall") }),
321
+ /* @__PURE__ */ t(o.Detail, { children: /* @__PURE__ */ t(P, { value: e.breakdown.overallScore, size: "sm" }) })
322
322
  ] }) : null,
323
- e.futureVisit ? /* @__PURE__ */ n(l.Row, { children: [
324
- /* @__PURE__ */ t(l.Term, { children: a("reviewsPanel.breakdown.futureVisit") }),
325
- /* @__PURE__ */ t(l.Detail, { children: a(`reviewsPanel.answer.${e.futureVisit}`) })
323
+ e.futureVisit ? /* @__PURE__ */ r(o.Row, { children: [
324
+ /* @__PURE__ */ t(o.Term, { children: s("reviewsPanel.breakdown.futureVisit") }),
325
+ /* @__PURE__ */ t(o.Detail, { children: s(`reviewsPanel.answer.${e.futureVisit}`) })
326
326
  ] }) : null,
327
- e.recommendation ? /* @__PURE__ */ n(l.Row, { children: [
328
- /* @__PURE__ */ t(l.Term, { children: a("reviewsPanel.breakdown.recommendation") }),
329
- /* @__PURE__ */ t(l.Detail, { children: a(`reviewsPanel.answer.${e.recommendation}`) })
327
+ e.recommendation ? /* @__PURE__ */ r(o.Row, { children: [
328
+ /* @__PURE__ */ t(o.Term, { children: s("reviewsPanel.breakdown.recommendation") }),
329
+ /* @__PURE__ */ t(o.Detail, { children: s(`reviewsPanel.answer.${e.recommendation}`) })
330
330
  ] }) : null
331
331
  ] }) : null,
332
- e.reply ? /* @__PURE__ */ 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)]", children: [
333
- /* @__PURE__ */ t("p", { className: "type-label ds:m-0 ds:mb-[var(--spacing-2xs)] ds:text-[var(--muted-foreground)] ds:font-[var(--font-weight-semibold)]", children: a("reviewsPanel.replyTitle") }),
332
+ e.reply ? /* @__PURE__ */ r("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)]", children: [
333
+ /* @__PURE__ */ t("p", { className: "type-label ds:m-0 ds:mb-[var(--spacing-2xs)] ds:text-[var(--muted-foreground)] ds:font-[var(--font-weight-semibold)]", children: s("reviewsPanel.replyTitle") }),
334
334
  /* @__PURE__ */ t(
335
335
  "p",
336
336
  {
337
+ lang: p,
337
338
  dir: "auto",
338
339
  className: "type-body-sm ds:m-0 ds:text-[var(--foreground)] ds:[white-space:pre-line]",
339
340
  children: e.reply
@@ -344,48 +345,49 @@ function ce({ review: e, locale: i }) {
344
345
  ]
345
346
  }
346
347
  );
347
- return /* @__PURE__ */ n(A, { variant: "default", className: "ds:shadow-[var(--shadow-sm)]", children: [
348
- /* @__PURE__ */ t(A.Header, { children: /* @__PURE__ */ n("div", { className: "ds:flex ds:items-start ds:justify-between ds:gap-[var(--spacing-sm)]", children: [
348
+ return /* @__PURE__ */ r(A, { variant: "default", className: "ds:shadow-[var(--shadow-sm)]", children: [
349
+ /* @__PURE__ */ t(A.Header, { children: /* @__PURE__ */ r("div", { className: "ds:flex ds:items-start ds:justify-between ds:gap-[var(--spacing-sm)]", children: [
349
350
  /* @__PURE__ */ t(
350
351
  "h4",
351
352
  {
352
353
  dir: e.pseudonym ? "auto" : void 0,
353
354
  className: "type-title-item ds:m-0 ds:text-[var(--foreground)]",
354
- children: p
355
+ children: N
355
356
  }
356
357
  ),
357
- /* @__PURE__ */ t(k, { value: e.overallScore, size: "sm" })
358
+ /* @__PURE__ */ t(P, { value: e.overallScore, size: "sm" })
358
359
  ] }) }),
359
360
  e.comment ? /* @__PURE__ */ t(A.Body, { children: /* @__PURE__ */ t(
360
361
  "p",
361
362
  {
363
+ lang: p,
362
364
  dir: "auto",
363
365
  className: "type-body ds:m-0 ds:text-[var(--foreground)] ds:line-clamp-2",
364
366
  children: e.comment
365
367
  }
366
368
  ) }) : null,
367
- P
369
+ a
368
370
  ] });
369
371
  }
370
- function ue({ hideFilter: e }) {
371
- return /* @__PURE__ */ n("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-md)]", children: [
372
- /* @__PURE__ */ n("div", { className: "ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]", children: [
372
+ function me({ hideFilter: e }) {
373
+ return /* @__PURE__ */ r("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-md)]", children: [
374
+ /* @__PURE__ */ r("div", { className: "ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]", children: [
373
375
  /* @__PURE__ */ t(T, { variant: "text", width: "40%" }),
374
376
  /* @__PURE__ */ t(T, { variant: "text", width: "20%" })
375
377
  ] }),
376
- e ? null : /* @__PURE__ */ t("div", { className: "ds:flex ds:flex-wrap ds:gap-[var(--spacing-xs)]", children: Array.from({ length: 6 }, (i, a) => /* @__PURE__ */ t(
378
+ e ? null : /* @__PURE__ */ t("div", { className: "ds:flex ds:flex-wrap ds:gap-[var(--spacing-xs)]", children: Array.from({ length: 6 }, (l, p) => /* @__PURE__ */ t(
377
379
  T,
378
380
  {
379
381
  variant: "rounded",
380
382
  width: "4rem",
381
383
  height: "2.5rem"
382
384
  },
383
- `rp-fs-${a}`
385
+ `rp-fs-${p}`
384
386
  )) }),
385
- /* @__PURE__ */ t("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]", children: Array.from({ length: 3 }, (i, a) => /* @__PURE__ */ t(T, { variant: "rounded", height: "6rem" }, `rp-rs-${a}`)) })
387
+ /* @__PURE__ */ t("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]", children: Array.from({ length: 3 }, (l, p) => /* @__PURE__ */ t(T, { variant: "rounded", height: "6rem" }, `rp-rs-${p}`)) })
386
388
  ] });
387
389
  }
388
- const I = _(
390
+ const L = $(
389
391
  // No root padding: the shared `PanelHeader` owns the header's edge
390
392
  // padding (px/pt/pb) and the body wrapper below owns the content
391
393
  // padding, so the header divider can span flush to the panel edge.
@@ -410,151 +412,152 @@ const I = _(
410
412
  },
411
413
  defaultVariants: { surface: "flat" }
412
414
  }
413
- ), me = J(
415
+ ), pe = Q(
414
416
  ({
415
417
  reviews: e,
416
- aggregate: i,
417
- filter: a,
418
- onFilterChange: c,
419
- introSlot: f,
420
- headerActionSlot: y,
421
- "aria-label": N,
422
- id: o,
423
- className: u,
424
- hideFilter: v = !1,
425
- surface: p = "flat",
426
- loading: P = !1,
427
- ...r
428
- }, d) => {
429
- const { t: s, i18n: x } = E("ui"), h = x.language ?? "en", [S, V] = H("all"), m = a ?? S, b = L(
430
- (g) => {
431
- a === void 0 && V(g), c == null || c(g);
418
+ aggregate: l,
419
+ contentLang: p,
420
+ filter: s,
421
+ onFilterChange: f,
422
+ introSlot: y,
423
+ headerActionSlot: k,
424
+ "aria-label": v,
425
+ id: u,
426
+ className: m,
427
+ hideFilter: g = !1,
428
+ surface: N = "flat",
429
+ loading: a = !1,
430
+ ...i
431
+ }, n) => {
432
+ const { t: d, i18n: x } = M("ui"), b = x.language ?? "en", [V, z] = E("all"), c = s ?? V, S = B(
433
+ (h) => {
434
+ s === void 0 && z(h), f == null || f(h);
432
435
  },
433
- [a, c]
434
- ), U = C(
435
- () => i.histogram ?? le(e),
436
- [i.histogram, e]
437
- ), R = C(() => m === "all" ? e : e.filter((g) => $(g.overallScore) === m), [e, m]), M = F(m);
438
- M.current = m;
439
- const O = F(R.length);
440
- O.current = R.length;
441
- const q = C(
436
+ [s, f]
437
+ ), q = C(
438
+ () => l.histogram ?? oe(e),
439
+ [l.histogram, e]
440
+ ), R = C(() => c === "all" ? e : e.filter((h) => U(h.overallScore) === c), [e, c]), O = F(c);
441
+ O.current = c;
442
+ const K = F(R.length);
443
+ K.current = R.length;
444
+ const G = C(
442
445
  () => ({
443
- getFilter: () => M.current,
444
- setFilter: (g) => b(g),
445
- getVisibleCount: () => O.current
446
+ getFilter: () => O.current,
447
+ setFilter: (h) => S(h),
448
+ getVisibleCount: () => K.current
446
449
  }),
447
- [b]
450
+ [S]
448
451
  ), D = F(null);
449
- Q(d, () => D.current, []), X(ie, q, o);
450
- const z = N ?? s("reviewsPanel.regionLabel"), j = String(m);
451
- if (P)
452
+ W(n, () => D.current, []), Y(le, G, u);
453
+ const j = v ?? d("reviewsPanel.regionLabel"), I = String(c);
454
+ if (a)
452
455
  return /* @__PURE__ */ t(
453
456
  "section",
454
457
  {
455
458
  ref: D,
456
- "aria-label": z,
459
+ "aria-label": j,
457
460
  "aria-busy": "true",
458
- id: o,
461
+ id: u,
459
462
  "data-component": "reviews-panel",
460
- "data-component-id": o,
461
- "data-filter": j,
463
+ "data-component-id": u,
464
+ "data-filter": I,
462
465
  "data-state": "loading",
463
- className: I({ surface: p, className: u }),
464
- ...r,
465
- children: /* @__PURE__ */ t("div", { className: "ds:p-[var(--spacing-md)]", children: /* @__PURE__ */ t(ue, { hideFilter: v }) })
466
+ className: L({ surface: N, className: m }),
467
+ ...i,
468
+ children: /* @__PURE__ */ t("div", { className: "ds:p-[var(--spacing-md)]", children: /* @__PURE__ */ t(me, { hideFilter: g }) })
466
469
  }
467
470
  );
468
- if (i.count === 0 && e.length === 0)
471
+ if (l.count === 0 && e.length === 0)
469
472
  return /* @__PURE__ */ t(
470
473
  "section",
471
474
  {
472
475
  ref: D,
473
- "aria-label": z,
474
- id: o,
476
+ "aria-label": j,
477
+ id: u,
475
478
  "data-component": "reviews-panel",
476
- "data-component-id": o,
477
- "data-filter": j,
479
+ "data-component-id": u,
480
+ "data-filter": I,
478
481
  "data-state": "empty",
479
- className: I({ surface: p, className: u }),
480
- ...r,
482
+ className: L({ surface: N, className: m }),
483
+ ...i,
481
484
  children: /* @__PURE__ */ t("div", { className: "ds:p-[var(--spacing-md)]", children: /* @__PURE__ */ t(
482
- te,
485
+ se,
483
486
  {
484
487
  variant: "no-results",
485
- title: s("reviewsPanel.empty.title"),
486
- description: s("reviewsPanel.empty.description")
488
+ title: d("reviewsPanel.empty.title"),
489
+ description: d("reviewsPanel.empty.description")
487
490
  }
488
491
  ) })
489
492
  }
490
493
  );
491
- const G = !v;
492
- return /* @__PURE__ */ n(
494
+ const J = !g;
495
+ return /* @__PURE__ */ r(
493
496
  "section",
494
497
  {
495
498
  ref: D,
496
- "aria-label": z,
497
- id: o,
499
+ "aria-label": j,
500
+ id: u,
498
501
  "data-component": "reviews-panel",
499
- "data-component-id": o,
500
- "data-filter": j,
501
- className: I({ surface: p, className: u }),
502
- ...r,
502
+ "data-component-id": u,
503
+ "data-filter": I,
504
+ className: L({ surface: N, className: m }),
505
+ ...i,
503
506
  children: [
504
507
  /* @__PURE__ */ t(
505
508
  re,
506
509
  {
507
- title: s("reviewsPanel.title"),
508
- icon: /* @__PURE__ */ t(ae, { "aria-hidden": "true" }),
510
+ title: d("reviewsPanel.title"),
511
+ icon: /* @__PURE__ */ t(ne, { "aria-hidden": "true" }),
509
512
  titleScale: "section",
510
513
  sticky: !0,
511
514
  wrapActions: !0,
512
- actions: /* @__PURE__ */ n(K, { children: [
513
- y,
515
+ actions: /* @__PURE__ */ r(_, { children: [
516
+ k,
514
517
  /* @__PURE__ */ t(
515
- k,
518
+ P,
516
519
  {
517
- value: i.averageScore,
518
- reviews: { count: i.count },
520
+ value: l.averageScore,
521
+ reviews: { count: l.count },
519
522
  size: "md"
520
523
  }
521
524
  )
522
525
  ] }),
523
- filters: G ? /* @__PURE__ */ t(
524
- de,
526
+ filters: J ? /* @__PURE__ */ t(
527
+ ce,
525
528
  {
526
- filter: m,
527
- histogram: U,
528
- totalCount: i.count,
529
- onChange: b
529
+ filter: c,
530
+ histogram: q,
531
+ totalCount: l.count,
532
+ onChange: S
530
533
  }
531
534
  ) : void 0
532
535
  }
533
536
  ),
534
- /* @__PURE__ */ n("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:min-w-0 ds:p-[var(--spacing-md)]", children: [
535
- f || null,
537
+ /* @__PURE__ */ r("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:min-w-0 ds:p-[var(--spacing-md)]", children: [
538
+ y || null,
536
539
  R.length === 0 ? (
537
540
  // `role="status"` already implies `aria-live="polite"` —
538
541
  // declaring both would queue two announcements when the
539
542
  // filter changes (the radio's aria-checked flip + this
540
543
  // container's text). Keeping `role` only.
541
- /* @__PURE__ */ n(
544
+ /* @__PURE__ */ r(
542
545
  "div",
543
546
  {
544
547
  role: "status",
545
548
  "data-state": "no-match",
546
549
  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",
547
550
  children: [
548
- /* @__PURE__ */ t("p", { className: "type-body ds:m-0 ds:text-[var(--muted-foreground)]", children: m === "all" ? s("reviewsPanel.empty.title") : s("reviewsPanel.empty.filterDescription", {
549
- stars: m
551
+ /* @__PURE__ */ t("p", { className: "type-body ds:m-0 ds:text-[var(--muted-foreground)]", children: c === "all" ? d("reviewsPanel.empty.title") : d("reviewsPanel.empty.filterDescription", {
552
+ stars: c
550
553
  }) }),
551
- m !== "all" ? /* @__PURE__ */ t(
552
- se,
554
+ c !== "all" ? /* @__PURE__ */ t(
555
+ ae,
553
556
  {
554
557
  intent: "secondary",
555
558
  size: "sm",
556
- onClick: () => b("all"),
557
- children: s("reviewsPanel.empty.resetFilter")
559
+ onClick: () => S("all"),
560
+ children: d("reviewsPanel.empty.resetFilter")
558
561
  }
559
562
  ) : null
560
563
  ]
@@ -563,9 +566,16 @@ const I = _(
563
566
  ) : /* @__PURE__ */ t(
564
567
  "ol",
565
568
  {
566
- "aria-label": s("reviewsPanel.listLabel"),
569
+ "aria-label": d("reviewsPanel.listLabel"),
567
570
  className: "ds:list-none ds:m-0 ds:p-0 ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]",
568
- children: R.map((g) => /* @__PURE__ */ t("li", { "data-review-id": g.id, children: /* @__PURE__ */ t(ce, { review: g, locale: h }) }, g.id))
571
+ children: R.map((h) => /* @__PURE__ */ t("li", { "data-review-id": h.id, children: /* @__PURE__ */ t(
572
+ ue,
573
+ {
574
+ review: h,
575
+ locale: b,
576
+ textLang: h.contentLang ?? p
577
+ }
578
+ ) }, h.id))
569
579
  }
570
580
  )
571
581
  ] })
@@ -574,9 +584,9 @@ const I = _(
574
584
  );
575
585
  }
576
586
  );
577
- me.displayName = "ReviewsPanel";
587
+ pe.displayName = "ReviewsPanel";
578
588
  export {
579
- me as R,
580
- ie as r
589
+ pe as R,
590
+ le as r
581
591
  };
582
- //# sourceMappingURL=reviews-panel-jPYgYvxG.js.map
592
+ //# sourceMappingURL=reviews-panel-CtrwZOUS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviews-panel-CtrwZOUS.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 // Pure ephemeral narrowing of the visible list — no data loss.\n safety: 'read',\n argsType: '{ filter: \"all\" | 1 | 2 | 3 | 4 | 5 }',\n argsSchema: {\n type: 'object',\n properties: {\n filter: {\n description: 'Star bucket to show, or \"all\" to clear the filter.',\n oneOf: [\n { type: 'string', enum: ['all'] },\n { type: 'integer', enum: [1, 2, 3, 4, 5] },\n ],\n },\n },\n required: ['filter'],\n },\n idempotent: true,\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 examples: [\n { args: { filter: 5 }, note: 'Show only 5-star reviews.' },\n { args: { filter: 'all' }, note: 'Clear the star filter.' },\n ],\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, Star } 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';\nimport { PanelHeader } from '../panel-header';\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 /**\n * Language of this review's own text (comment and practice reply), as a\n * BCP 47 tag such as `\"it\"`. Sets `lang` on that text so a screen reader on\n * a page in another language pronounces it correctly. Overrides the\n * panel-level `contentLang`. Leave it out and no `lang` is written.\n * The pseudonym, date, rating and the kit's labels keep the page language.\n */\n contentLang?: 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 * Default language of every review's own text, as a BCP 47 tag. A review's\n * own `contentLang` wins. Leave both out and no `lang` is written.\n */\n contentLang?: string;\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 /**\n * @default 'flat'\n * `'elevated'` wraps the panel in the equivalent of `Card variant=\"elevated\"`\n * — same tokens (`--card`, `--card-border`, `--shadow-card`,\n * `--radius-lg`), same padding. Use to drop the consumer-side\n * `<Card><Card.Body>` wrap.\n */\n surface?: 'flat' | 'elevated';\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/* FilterBar — hand-rolled radiogroup with roving tabindex */\n/* */\n/* Lives in the shared `PanelHeader` filters row, so it lays out */\n/* HORIZONTALLY (a wrapping bar of star-bucket chips) rather than the */\n/* legacy vertical sidebar column. Still the WAI-ARIA RadioGroup pattern */\n/* — each chip is a full click target with a non-text leading glyph (the */\n/* star ramp) and a trailing count. NOT Radix `RadioGroup` because we */\n/* need that glyph-plus-count chip shape. */\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:inline-flex ds:items-center ds:justify-center',\n 'ds:gap-[var(--spacing-xs)]',\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 FilterBarProps {\n filter: ReviewFilter;\n histogram: Record<1 | 2 | 3 | 4 | 5, number>;\n totalCount: number;\n onChange: (next: ReviewFilter) => void;\n}\n\nfunction FilterBar({\n filter,\n histogram,\n totalCount,\n onChange,\n}: FilterBarProps) {\n const { t } = useTranslation('ui');\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 <>\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-wrap ds:items-center 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={[\n 'type-meta',\n // Idle row: count is dimmed (--muted-foreground over\n // --background ≈ 4.5:1, AA passes). Selected row\n // paints violet bg + white text on the BUTTON, but\n // the count's own explicit color override would\n // otherwise win the cascade and leave dark navy-grey\n // text on the violet pill (~3:1, AA-fail). Match the\n // row's foreground when selected; slight opacity\n // keeps the count secondary to the label.\n isSelected\n ? 'ds:text-[color:var(--primary-foreground)]'\n : 'ds:text-[var(--muted-foreground)]',\n ].join(' ')}\n >\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* ReviewCard — compact card with optional Collapsible breakdown */\n/* -------------------------------------------------------------------- */\n\ninterface ReviewCardProps {\n review: ReviewItem;\n locale: string;\n /** Resolved language of the review's text (item override, else panel default). */\n textLang?: string;\n}\n\nfunction ReviewCard({ review, locale, textLang }: ReviewCardProps) {\n const { t } = useTranslation('ui');\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 aria-controls={contentId}\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(--review-details-foreground)] ds:min-w-[var(--min-target-size)] ds:min-h-[var(--min-target-size)] 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\n lang={textLang}\n dir=\"auto\"\n className=\"type-body-sm ds:m-0 ds:text-[var(--foreground)] ds:[white-space:pre-line]\"\n >\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 {/* The patient's pseudonym and comment, and the practice's reply,\n keep the direction they were typed in. The kit's own\n \"Anonymous\" follows the page. */}\n <h4\n dir={review.pseudonym ? 'auto' : undefined}\n className=\"type-title-item ds:m-0 ds:text-[var(--foreground)]\"\n >\n {pseudonym}\n </h4>\n <Rating value={review.overallScore} size=\"sm\" />\n </div>\n </Card.Header>\n {review.comment ? (\n <Card.Body>\n <p\n lang={textLang}\n dir=\"auto\"\n className=\"type-body ds:m-0 ds:text-[var(--foreground)] ds:line-clamp-2\"\n >\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 {!hideFilter ? (\n <div className=\"ds:flex ds:flex-wrap ds:gap-[var(--spacing-xs)]\">\n {Array.from({ length: 6 }, (_, i) => (\n <Skeleton\n key={`rp-fs-${i}`}\n variant=\"rounded\"\n width=\"4rem\"\n height=\"2.5rem\"\n />\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 );\n}\n\n/* -------------------------------------------------------------------- */\n/* Root variants */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n // No root padding: the shared `PanelHeader` owns the header's edge\n // padding (px/pt/pb) and the body wrapper below owns the content\n // padding, so the header divider can span flush to the panel edge.\n 'ds:flex ds:w-full ds:flex-col ds:text-[var(--foreground)]',\n {\n variants: {\n // `surface=\"elevated\"` matches `Card variant=\"elevated\"` — same\n // chrome tokens — so consumers can drop their own Card wraps\n // without producing a double-card look. Padding is supplied by the\n // header + body, not the root, so the header divider reaches the\n // card edge. Default `'flat'` adds no chrome.\n surface: {\n flat: '',\n elevated: [\n 'ds:overflow-hidden',\n 'ds:rounded-[var(--radius-lg)]',\n 'ds:bg-[var(--card)] ds:text-[var(--card-foreground)]',\n 'ds:border ds:border-[color:var(--card-border)]',\n 'ds:shadow-[var(--shadow-card)]',\n ].join(' '),\n },\n },\n defaultVariants: { surface: 'flat' },\n },\n);\n\n/* -------------------------------------------------------------------- */\n/* ReviewsPanel */\n/* -------------------------------------------------------------------- */\n\nexport const ReviewsPanel = forwardRef<HTMLElement, ReviewsPanelProps>(\n (\n {\n reviews,\n aggregate,\n contentLang,\n filter: filterProp,\n onFilterChange,\n introSlot,\n headerActionSlot,\n 'aria-label': ariaLabel,\n id,\n className,\n hideFilter = false,\n surface = 'flat',\n loading = false,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation('ui');\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({ surface, className })}\n {...rest}\n >\n <div className=\"ds:p-[var(--spacing-md)]\">\n <PanelSkeleton hideFilter={hideFilter} />\n </div>\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({ surface, className })}\n {...rest}\n >\n <div className=\"ds:p-[var(--spacing-md)]\">\n <EmptyState\n variant=\"no-results\"\n title={t('reviewsPanel.empty.title')}\n description={t('reviewsPanel.empty.description')}\n />\n </div>\n </section>\n );\n }\n\n const showFilter = !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({ surface, className })}\n {...rest}\n >\n {/* Shared rail header — the panel root is a region landmark with an\n accessible name, so this nested <header> is a generic element\n (not a banner) and stays within the region. The rail injects the\n favourite / close actions via PanelHeaderActionsProvider; the\n panel's own aggregate Rating + headerActionSlot lead the cluster.\n The rating summary is wide (about 260px), so on a phone it moves\n under the heading rather than clipping it to a letter or two. */}\n <PanelHeader\n title={t('reviewsPanel.title')}\n icon={<Star aria-hidden=\"true\" />}\n titleScale=\"section\"\n sticky\n wrapActions\n actions={\n <>\n {headerActionSlot}\n <Rating\n value={aggregate.averageScore}\n reviews={{ count: aggregate.count }}\n size=\"md\"\n />\n </>\n }\n filters={\n showFilter ? (\n <FilterBar\n filter={filter}\n histogram={histogram}\n totalCount={aggregate.count}\n onChange={handleFilterChange}\n />\n ) : undefined\n }\n />\n\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:min-w-0 ds:p-[var(--spacing-md)]\">\n {introSlot\n ? // Render the slot raw — the consumer's introSlot owns\n // its own visual chrome (Alert, Infotip, plain <p>, …).\n // 0.31.x wrapped the slot in a `bg-info/8` tinted box,\n // but when the consumer passed an Alert with its own\n // tinted surface the visual stack rendered TWO concentric\n // tinted boxes (\"double effect\"). The wrapper added no\n // semantic value either (no role, no aria), so dropping\n // it is purely visual.\n introSlot\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\n review={review}\n locale={locale}\n textLang={review.contentLang ?? contentLang}\n />\n </li>\n ))}\n </ol>\n )}\n </div>\n </section>\n );\n },\n);\n\nReviewsPanel.displayName = 'ReviewsPanel';\n"],"names":["reviewsPanelAgent","handle","args","FILTER_VALUES","bucketOf","score","deriveHistogram","reviews","out","r","filterRowVariants","cva","FilterBar","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","Fragment","jsx","val","isAll","stars","count","isSelected","isFocused","ariaLabel","node","e","Rating","ReviewCard","review","locale","textLang","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","contentLang","filterProp","onFilterChange","introSlot","headerActionSlot","id","className","surface","loading","rest","ref","i18n","internalFilter","setInternalFilter","handleFilterChange","visibleReviews","latestFilterRef","latestVisibleCountRef","agentHandle","rootRef","useImperativeHandle","useAgentRegistration","resolvedAriaLabel","dataFilter","EmptyState","showFilter","PanelHeader","Star","Button"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "packageVersion": "1.105.0",
3
+ "packageVersion": "1.106.0",
4
4
  "components": [
5
5
  {
6
6
  "kind": "component",
@@ -1,4 +1,4 @@
1
- import { R as r, r as s } from "../../_chunks/reviews-panel-jPYgYvxG.js";
1
+ import { R as r, r as s } from "../../_chunks/reviews-panel-CtrwZOUS.js";
2
2
  export {
3
3
  r as ReviewsPanel,
4
4
  s as reviewsPanelAgent
@@ -9,6 +9,14 @@ export interface ReviewItem {
9
9
  overallScore: number;
10
10
  /** Short free-text comment shown on the compact card. */
11
11
  comment?: string;
12
+ /**
13
+ * Language of this review's own text (comment and practice reply), as a
14
+ * BCP 47 tag such as `"it"`. Sets `lang` on that text so a screen reader on
15
+ * a page in another language pronounces it correctly. Overrides the
16
+ * panel-level `contentLang`. Leave it out and no `lang` is written.
17
+ * The pseudonym, date, rating and the kit's labels keep the page language.
18
+ */
19
+ contentLang?: string;
12
20
  /** Practice reply (revealed in the expanded panel only). */
13
21
  reply?: string;
14
22
  /** Optional per-axis breakdown — when present, the card grows an
@@ -45,6 +53,11 @@ export interface ReviewsPanelHandle {
45
53
  export interface ReviewsPanelProps extends Omit<ComponentPropsWithoutRef<'section'>, 'aria-label' | 'children'> {
46
54
  reviews: ReviewItem[];
47
55
  aggregate: ReviewsAggregate;
56
+ /**
57
+ * Default language of every review's own text, as a BCP 47 tag. A review's
58
+ * own `contentLang` wins. Leave both out and no `lang` is written.
59
+ */
60
+ contentLang?: string;
48
61
  /**
49
62
  * Active star filter. Controlled — consumer owns it for shareability via
50
63
  * URL query strings. Omit for uncontrolled (panel-managed) filtering.
@@ -1 +1 @@
1
- {"version":3,"file":"reviews-panel.d.ts","sourceRoot":"","sources":["../../../src/components/reviews-panel/reviews-panel.tsx"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,wBAAwB,EAE7B,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAiCf,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;uEACmE;IACnE,SAAS,CAAC,EAAE;QACV,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,sEAAsE;IACtE,WAAW,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC;IACtC,cAAc,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAgB;IAC/B,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,YAAY,CAAC;IAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,6CAA6C;IAC7C,eAAe,EAAE,MAAM,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,wBAAwB,CAAC,SAAS,CAAC,EACnC,YAAY,GAAG,UAAU,CAC1B;IACC,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,EAAE,gBAAgB,CAAC;IAE5B;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IAE9C;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAE7B,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,0BAA0B;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAE9B,oDAAoD;IACpD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA6gBD,eAAO,MAAM,YAAY,2GAsOxB,CAAC"}
1
+ {"version":3,"file":"reviews-panel.d.ts","sourceRoot":"","sources":["../../../src/components/reviews-panel/reviews-panel.tsx"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,wBAAwB,EAE7B,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAiCf,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;uEACmE;IACnE,SAAS,CAAC,EAAE;QACV,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,sEAAsE;IACtE,WAAW,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC;IACtC,cAAc,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,QAAQ,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAgB;IAC/B,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,YAAY,CAAC;IAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,6CAA6C;IAC7C,eAAe,EAAE,MAAM,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,wBAAwB,CAAC,SAAS,CAAC,EACnC,YAAY,GAAG,UAAU,CAC1B;IACC,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,EAAE,gBAAgB,CAAC;IAC5B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IAE9C;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAE7B,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,0BAA0B;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAE9B,oDAAoD;IACpD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAihBD,eAAO,MAAM,YAAY,2GA2OxB,CAAC"}
package/dist/index.js CHANGED
@@ -245,7 +245,7 @@ import { P as bg, p as Ng } from "./_chunks/practice-results-BXz01sti.js";
245
245
  import { P as Dg, a as Fg, p as Lg, b as hg } from "./_chunks/payment-request-card-IR83TJax.js";
246
246
  import { P as yg, f as kg, p as Bg, a as vg, s as Gg, b as Hg, c as Wg } from "./_chunks/payment-form-C-s1x-bU.js";
247
247
  import { P as Ug, p as Vg } from "./_chunks/pdf-viewer-BnBKdx0y.js";
248
- import { R as Yg, r as Xg } from "./_chunks/reviews-panel-jPYgYvxG.js";
248
+ import { R as Yg, r as Xg } from "./_chunks/reviews-panel-CtrwZOUS.js";
249
249
  import { R as zg, r as jg, w as Qg } from "./_chunks/rich-text-editor-C3nANKZt.js";
250
250
  import { k as Jg } from "./_chunks/kit-extensions-BOnTM4Yf.js";
251
251
  import { E as aC } from "./_chunks/editor-overlay-Dc8uVxEp.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfadocs/ui-kit-debug",
3
- "version": "1.105.0",
3
+ "version": "1.106.0",
4
4
  "type": "module",
5
5
  "description": "AlfaDocs shared design system — tokens, components, patterns, and translations for platform, booking, and alfascribe. (debug build — identical runtime to @alfadocs/ui-kit, ships source maps for symbolication).",
6
6
  "license": "BUSL-1.1",
@@ -1 +0,0 @@
1
- {"version":3,"file":"reviews-panel-jPYgYvxG.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 // Pure ephemeral narrowing of the visible list — no data loss.\n safety: 'read',\n argsType: '{ filter: \"all\" | 1 | 2 | 3 | 4 | 5 }',\n argsSchema: {\n type: 'object',\n properties: {\n filter: {\n description: 'Star bucket to show, or \"all\" to clear the filter.',\n oneOf: [\n { type: 'string', enum: ['all'] },\n { type: 'integer', enum: [1, 2, 3, 4, 5] },\n ],\n },\n },\n required: ['filter'],\n },\n idempotent: true,\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 examples: [\n { args: { filter: 5 }, note: 'Show only 5-star reviews.' },\n { args: { filter: 'all' }, note: 'Clear the star filter.' },\n ],\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, Star } 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';\nimport { PanelHeader } from '../panel-header';\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 /**\n * @default 'flat'\n * `'elevated'` wraps the panel in the equivalent of `Card variant=\"elevated\"`\n * — same tokens (`--card`, `--card-border`, `--shadow-card`,\n * `--radius-lg`), same padding. Use to drop the consumer-side\n * `<Card><Card.Body>` wrap.\n */\n surface?: 'flat' | 'elevated';\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/* FilterBar — hand-rolled radiogroup with roving tabindex */\n/* */\n/* Lives in the shared `PanelHeader` filters row, so it lays out */\n/* HORIZONTALLY (a wrapping bar of star-bucket chips) rather than the */\n/* legacy vertical sidebar column. Still the WAI-ARIA RadioGroup pattern */\n/* — each chip is a full click target with a non-text leading glyph (the */\n/* star ramp) and a trailing count. NOT Radix `RadioGroup` because we */\n/* need that glyph-plus-count chip shape. */\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:inline-flex ds:items-center ds:justify-center',\n 'ds:gap-[var(--spacing-xs)]',\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 FilterBarProps {\n filter: ReviewFilter;\n histogram: Record<1 | 2 | 3 | 4 | 5, number>;\n totalCount: number;\n onChange: (next: ReviewFilter) => void;\n}\n\nfunction FilterBar({\n filter,\n histogram,\n totalCount,\n onChange,\n}: FilterBarProps) {\n const { t } = useTranslation('ui');\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 <>\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-wrap ds:items-center 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={[\n 'type-meta',\n // Idle row: count is dimmed (--muted-foreground over\n // --background ≈ 4.5:1, AA passes). Selected row\n // paints violet bg + white text on the BUTTON, but\n // the count's own explicit color override would\n // otherwise win the cascade and leave dark navy-grey\n // text on the violet pill (~3:1, AA-fail). Match the\n // row's foreground when selected; slight opacity\n // keeps the count secondary to the label.\n isSelected\n ? 'ds:text-[color:var(--primary-foreground)]'\n : 'ds:text-[var(--muted-foreground)]',\n ].join(' ')}\n >\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </>\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('ui');\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 aria-controls={contentId}\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(--review-details-foreground)] ds:min-w-[var(--min-target-size)] ds:min-h-[var(--min-target-size)] 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\n dir=\"auto\"\n className=\"type-body-sm ds:m-0 ds:text-[var(--foreground)] ds:[white-space:pre-line]\"\n >\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 {/* The patient's pseudonym and comment, and the practice's reply,\n keep the direction they were typed in. The kit's own\n \"Anonymous\" follows the page. */}\n <h4\n dir={review.pseudonym ? 'auto' : undefined}\n className=\"type-title-item ds:m-0 ds:text-[var(--foreground)]\"\n >\n {pseudonym}\n </h4>\n <Rating value={review.overallScore} size=\"sm\" />\n </div>\n </Card.Header>\n {review.comment ? (\n <Card.Body>\n <p\n dir=\"auto\"\n className=\"type-body ds:m-0 ds:text-[var(--foreground)] ds:line-clamp-2\"\n >\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 {!hideFilter ? (\n <div className=\"ds:flex ds:flex-wrap ds:gap-[var(--spacing-xs)]\">\n {Array.from({ length: 6 }, (_, i) => (\n <Skeleton\n key={`rp-fs-${i}`}\n variant=\"rounded\"\n width=\"4rem\"\n height=\"2.5rem\"\n />\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 );\n}\n\n/* -------------------------------------------------------------------- */\n/* Root variants */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n // No root padding: the shared `PanelHeader` owns the header's edge\n // padding (px/pt/pb) and the body wrapper below owns the content\n // padding, so the header divider can span flush to the panel edge.\n 'ds:flex ds:w-full ds:flex-col ds:text-[var(--foreground)]',\n {\n variants: {\n // `surface=\"elevated\"` matches `Card variant=\"elevated\"` — same\n // chrome tokens — so consumers can drop their own Card wraps\n // without producing a double-card look. Padding is supplied by the\n // header + body, not the root, so the header divider reaches the\n // card edge. Default `'flat'` adds no chrome.\n surface: {\n flat: '',\n elevated: [\n 'ds:overflow-hidden',\n 'ds:rounded-[var(--radius-lg)]',\n 'ds:bg-[var(--card)] ds:text-[var(--card-foreground)]',\n 'ds:border ds:border-[color:var(--card-border)]',\n 'ds:shadow-[var(--shadow-card)]',\n ].join(' '),\n },\n },\n defaultVariants: { surface: 'flat' },\n },\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 surface = 'flat',\n loading = false,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation('ui');\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({ surface, className })}\n {...rest}\n >\n <div className=\"ds:p-[var(--spacing-md)]\">\n <PanelSkeleton hideFilter={hideFilter} />\n </div>\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({ surface, className })}\n {...rest}\n >\n <div className=\"ds:p-[var(--spacing-md)]\">\n <EmptyState\n variant=\"no-results\"\n title={t('reviewsPanel.empty.title')}\n description={t('reviewsPanel.empty.description')}\n />\n </div>\n </section>\n );\n }\n\n const showFilter = !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({ surface, className })}\n {...rest}\n >\n {/* Shared rail header — the panel root is a region landmark with an\n accessible name, so this nested <header> is a generic element\n (not a banner) and stays within the region. The rail injects the\n favourite / close actions via PanelHeaderActionsProvider; the\n panel's own aggregate Rating + headerActionSlot lead the cluster.\n The rating summary is wide (about 260px), so on a phone it moves\n under the heading rather than clipping it to a letter or two. */}\n <PanelHeader\n title={t('reviewsPanel.title')}\n icon={<Star aria-hidden=\"true\" />}\n titleScale=\"section\"\n sticky\n wrapActions\n actions={\n <>\n {headerActionSlot}\n <Rating\n value={aggregate.averageScore}\n reviews={{ count: aggregate.count }}\n size=\"md\"\n />\n </>\n }\n filters={\n showFilter ? (\n <FilterBar\n filter={filter}\n histogram={histogram}\n totalCount={aggregate.count}\n onChange={handleFilterChange}\n />\n ) : undefined\n }\n />\n\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:min-w-0 ds:p-[var(--spacing-md)]\">\n {introSlot\n ? // Render the slot raw — the consumer's introSlot owns\n // its own visual chrome (Alert, Infotip, plain <p>, …).\n // 0.31.x wrapped the slot in a `bg-info/8` tinted box,\n // but when the consumer passed an Alert with its own\n // tinted surface the visual stack rendered TWO concentric\n // tinted boxes (\"double effect\"). The wrapper added no\n // semantic value either (no role, no aria), so dropping\n // it is purely visual.\n introSlot\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 </section>\n );\n },\n);\n\nReviewsPanel.displayName = 'ReviewsPanel';\n"],"names":["reviewsPanelAgent","handle","args","FILTER_VALUES","bucketOf","score","deriveHistogram","reviews","out","r","filterRowVariants","cva","FilterBar","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","Fragment","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","surface","loading","rest","ref","i18n","internalFilter","setInternalFilter","handleFilterChange","visibleReviews","latestFilterRef","latestVisibleCountRef","agentHandle","rootRef","useImperativeHandle","useAgentRegistration","resolvedAriaLabel","dataFilter","EmptyState","showFilter","PanelHeader","Star","Button"],"mappings":"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"}