@wealthx/shadcn 1.5.38 → 1.5.39

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.
Files changed (35) hide show
  1. package/.turbo/turbo-build.log +129 -126
  2. package/CHANGELOG.md +6 -0
  3. package/dist/{chunk-LSSIWLYU.mjs → chunk-6XNEHTII.mjs} +1 -1
  4. package/dist/{chunk-ULQ53FRJ.mjs → chunk-7NQKFPXE.mjs} +1 -1
  5. package/dist/{chunk-DSVKEVX6.mjs → chunk-CZOGJC76.mjs} +1 -1
  6. package/dist/{chunk-JPGL36WQ.mjs → chunk-FL7DEYUA.mjs} +6 -7
  7. package/dist/{chunk-2CHH5QOA.mjs → chunk-FQUT5XD6.mjs} +1 -1
  8. package/dist/chunk-MGIDYXOP.mjs +814 -0
  9. package/dist/{chunk-OG2VM34K.mjs → chunk-MHBQJVHE.mjs} +1 -1
  10. package/dist/components/ui/file-preview-dialog.js +6 -7
  11. package/dist/components/ui/file-preview-dialog.mjs +2 -2
  12. package/dist/components/ui/kanban-column.js +6 -7
  13. package/dist/components/ui/kanban-column.mjs +3 -3
  14. package/dist/components/ui/opportunity-card.js +6 -7
  15. package/dist/components/ui/opportunity-card.mjs +2 -2
  16. package/dist/components/ui/pipeline-board.js +6 -7
  17. package/dist/components/ui/pipeline-board.mjs +4 -4
  18. package/dist/components/ui/policy-ai/index.js +1636 -0
  19. package/dist/components/ui/policy-ai/index.mjs +36 -0
  20. package/dist/components/ui/progress.js +6 -7
  21. package/dist/components/ui/progress.mjs +1 -1
  22. package/dist/components/ui/stage-timeline.js +6 -7
  23. package/dist/components/ui/stage-timeline.mjs +2 -2
  24. package/dist/index.js +4034 -3258
  25. package/dist/index.mjs +24 -6
  26. package/dist/styles.css +1 -1
  27. package/package.json +10 -5
  28. package/src/components/index.tsx +30 -0
  29. package/src/components/ui/policy-ai/index.tsx +41 -0
  30. package/src/components/ui/policy-ai/policy-ai-panel.tsx +526 -0
  31. package/src/components/ui/policy-ai/policy-ai-primitives.tsx +332 -0
  32. package/src/components/ui/policy-ai/policy-ai-responses.tsx +543 -0
  33. package/src/components/ui/progress.tsx +15 -12
  34. package/src/styles/styles-css.ts +1 -1
  35. package/tsup.config.ts +2 -1
@@ -0,0 +1,814 @@
1
+ import {
2
+ Progress
3
+ } from "./chunk-FL7DEYUA.mjs";
4
+ import {
5
+ ChatWidgetMessage
6
+ } from "./chunk-FQYFPHDO.mjs";
7
+ import {
8
+ Tooltip,
9
+ TooltipContent,
10
+ TooltipProvider,
11
+ TooltipTrigger
12
+ } from "./chunk-3S6KVFF5.mjs";
13
+ import {
14
+ Tabs,
15
+ TabsList,
16
+ TabsTrigger
17
+ } from "./chunk-WE4YKBDE.mjs";
18
+ import {
19
+ ChatInputArea
20
+ } from "./chunk-EFHPSKVF.mjs";
21
+ import {
22
+ Badge
23
+ } from "./chunk-X6RC5UWB.mjs";
24
+ import {
25
+ Popover,
26
+ PopoverContent,
27
+ PopoverTrigger
28
+ } from "./chunk-F3CU6KEI.mjs";
29
+ import {
30
+ Input
31
+ } from "./chunk-LBTHZSBT.mjs";
32
+ import {
33
+ Button
34
+ } from "./chunk-NOOEKOWY.mjs";
35
+ import {
36
+ cn
37
+ } from "./chunk-AFML43VJ.mjs";
38
+
39
+ // src/components/ui/policy-ai/policy-ai-primitives.tsx
40
+ import * as React from "react";
41
+ import { ChevronDown, ChevronUp, FileText, Tag } from "lucide-react";
42
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
43
+ var POLICY_TYPE_COLORS = {
44
+ Income: "text-primary",
45
+ Security: "text-warning",
46
+ Serviceability: "text-info",
47
+ "Loan Type": "text-secondary",
48
+ Borrower: "text-success"
49
+ };
50
+ var QUERY_TYPE_LABELS = {
51
+ single_bank: "Single bank",
52
+ cross_bank_comparison: "Cross-bank",
53
+ threshold_filter: "Filter",
54
+ ranking: "Ranking",
55
+ scenario_match: "Scenario",
56
+ general: "General"
57
+ };
58
+ function PolicyQueryChip({ context, className }) {
59
+ const { policyType, categories, queryType, bankName } = context;
60
+ const typeColor = POLICY_TYPE_COLORS[policyType];
61
+ return /* @__PURE__ */ jsxs(
62
+ "div",
63
+ {
64
+ "data-slot": "policy-query-chip",
65
+ className: cn(
66
+ "inline-flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground",
67
+ className
68
+ ),
69
+ children: [
70
+ /* @__PURE__ */ jsx(Tag, { className: "size-3 shrink-0", "aria-hidden": "true" }),
71
+ /* @__PURE__ */ jsx("span", { className: cn("font-medium", typeColor), children: policyType }),
72
+ bankName && /* @__PURE__ */ jsxs(Fragment, { children: [
73
+ /* @__PURE__ */ jsx("span", { className: "text-border", children: "\xB7" }),
74
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: bankName })
75
+ ] }),
76
+ categories.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
77
+ /* @__PURE__ */ jsx("span", { className: "text-border", children: "\xB7" }),
78
+ /* @__PURE__ */ jsx("span", { children: categories.join(", ") })
79
+ ] }),
80
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "text-xs px-1.5 py-0 shrink-0", children: QUERY_TYPE_LABELS[queryType] })
81
+ ]
82
+ }
83
+ );
84
+ }
85
+ var VERDICT_CONFIG = {
86
+ yes: { variant: "success", label: "Yes" },
87
+ soft_no: { variant: "warning", label: "Soft No" },
88
+ no: { variant: "destructive", label: "No" },
89
+ unknown: { variant: "secondary", label: "\u2014" }
90
+ };
91
+ function PolicyVerdictBadge({
92
+ verdict,
93
+ className
94
+ }) {
95
+ const { variant, label } = VERDICT_CONFIG[verdict];
96
+ return /* @__PURE__ */ jsx(
97
+ Badge,
98
+ {
99
+ "data-slot": "policy-verdict-badge",
100
+ variant,
101
+ className: cn("shrink-0", className),
102
+ children: label
103
+ }
104
+ );
105
+ }
106
+ function PolicyCitationPanel({
107
+ citations,
108
+ className
109
+ }) {
110
+ const [open, setOpen] = React.useState(false);
111
+ if (citations.length === 0) return null;
112
+ return /* @__PURE__ */ jsxs(
113
+ "div",
114
+ {
115
+ "data-slot": "policy-citation-panel",
116
+ className: cn("border-t border-border", className),
117
+ children: [
118
+ /* @__PURE__ */ jsxs(
119
+ Button,
120
+ {
121
+ variant: "ghost",
122
+ size: "sm",
123
+ className: "w-full justify-between px-0 text-xs text-muted-foreground hover:text-foreground hover:bg-transparent gap-1",
124
+ onClick: () => setOpen((v) => !v),
125
+ "aria-expanded": open,
126
+ children: [
127
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
128
+ /* @__PURE__ */ jsx(FileText, { className: "size-3", "aria-hidden": "true" }),
129
+ "View sources (",
130
+ citations.length,
131
+ ")"
132
+ ] }),
133
+ open ? /* @__PURE__ */ jsx(ChevronUp, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "size-3", "aria-hidden": "true" })
134
+ ]
135
+ }
136
+ ),
137
+ open && /* @__PURE__ */ jsx("ol", { className: "mt-1 flex flex-col gap-2 pb-1", children: citations.map((cite) => /* @__PURE__ */ jsxs("li", { className: "flex gap-2 text-xs", children: [
138
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 flex items-center justify-center size-4 bg-muted text-muted-foreground font-mono font-semibold text-xs", children: cite.index }),
139
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [
140
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 flex-wrap", children: [
141
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: cite.bankName }),
142
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs px-1.5 py-0", children: cite.category })
143
+ ] }),
144
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground leading-relaxed line-clamp-3", children: cite.excerpt })
145
+ ] })
146
+ ] }, cite.index)) })
147
+ ]
148
+ }
149
+ );
150
+ }
151
+
152
+ // src/components/ui/policy-ai/policy-ai-responses.tsx
153
+ import * as React2 from "react";
154
+ import { Building2, Info } from "lucide-react";
155
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
156
+ function BankAvatar({ name }) {
157
+ return /* @__PURE__ */ jsx2(
158
+ "span",
159
+ {
160
+ "aria-hidden": "true",
161
+ className: "shrink-0 inline-flex items-center justify-center size-7 bg-muted text-muted-foreground text-xs font-semibold border border-border",
162
+ children: name.charAt(0).toUpperCase()
163
+ }
164
+ );
165
+ }
166
+ function QueryContextInfo({ label }) {
167
+ return /* @__PURE__ */ jsx2(TooltipProvider, { children: /* @__PURE__ */ jsxs2(Tooltip, { children: [
168
+ /* @__PURE__ */ jsx2(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx2(
169
+ "span",
170
+ {
171
+ className: "shrink-0 text-muted-foreground/60 hover:text-muted-foreground cursor-default transition-colors",
172
+ "aria-label": label,
173
+ children: /* @__PURE__ */ jsx2(Info, { className: "size-3.5", "aria-hidden": "true" })
174
+ }
175
+ ) }),
176
+ /* @__PURE__ */ jsx2(TooltipContent, { side: "top", children: label })
177
+ ] }) });
178
+ }
179
+ function queryContextLabel(context) {
180
+ return [
181
+ context.policyType,
182
+ ...context.categories,
183
+ context.queryType.replace(/_/g, " ")
184
+ ].join(" \xB7 ");
185
+ }
186
+ function CitationBadge({ citation }) {
187
+ return /* @__PURE__ */ jsxs2(Popover, { children: [
188
+ /* @__PURE__ */ jsx2(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `View source ${citation.index}`, children: /* @__PURE__ */ jsx2(
189
+ Badge,
190
+ {
191
+ variant: "secondary",
192
+ className: "cursor-pointer px-1.5 py-0 text-xs mx-0.5 align-baseline hover:bg-secondary/80 transition-colors",
193
+ children: citation.index
194
+ }
195
+ ) }) }),
196
+ /* @__PURE__ */ jsx2(PopoverContent, { className: "w-72 p-3", sideOffset: 6, children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2", children: [
197
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1.5 flex-wrap", children: [
198
+ /* @__PURE__ */ jsx2("span", { className: "text-xs font-semibold text-foreground", children: citation.bankName }),
199
+ /* @__PURE__ */ jsx2(Badge, { variant: "secondary", className: "text-xs px-1.5 py-0", children: citation.category })
200
+ ] }),
201
+ /* @__PURE__ */ jsx2("p", { className: "text-xs text-muted-foreground leading-relaxed", children: citation.excerpt })
202
+ ] }) })
203
+ ] });
204
+ }
205
+ function AnswerWithCitations({
206
+ answer,
207
+ citations
208
+ }) {
209
+ const citationMap = new Map(citations.map((c) => [c.index, c]));
210
+ const parts = answer.split(/(\[\d+\])/g);
211
+ return /* @__PURE__ */ jsx2("p", { className: "text-sm text-foreground leading-relaxed whitespace-pre-line", children: parts.map((part, i) => {
212
+ const match = part.match(/^\[(\d+)\]$/);
213
+ if (match) {
214
+ const citation = citationMap.get(parseInt(match[1], 10));
215
+ if (citation) return /* @__PURE__ */ jsx2(CitationBadge, { citation }, i);
216
+ return /* @__PURE__ */ jsx2("span", { className: "text-xs text-muted-foreground", children: part }, i);
217
+ }
218
+ return /* @__PURE__ */ jsx2(React2.Fragment, { children: part }, i);
219
+ }) });
220
+ }
221
+ function PolicySingleBankAnswer({
222
+ bankName,
223
+ verdict,
224
+ answer,
225
+ citations,
226
+ queryContext,
227
+ className
228
+ }) {
229
+ return /* @__PURE__ */ jsxs2(
230
+ "div",
231
+ {
232
+ "data-slot": "policy-single-bank-answer",
233
+ className: cn("border border-border bg-card flex flex-col", className),
234
+ children: [
235
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 px-4 py-3 border-b border-border", children: [
236
+ /* @__PURE__ */ jsx2(BankAvatar, { name: bankName }),
237
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex items-center gap-1.5 min-w-0", children: [
238
+ /* @__PURE__ */ jsx2("span", { className: "font-semibold text-sm text-foreground truncate", children: bankName }),
239
+ queryContext && /* @__PURE__ */ jsx2(QueryContextInfo, { label: queryContextLabel(queryContext) })
240
+ ] }),
241
+ /* @__PURE__ */ jsx2(PolicyVerdictBadge, { verdict })
242
+ ] }),
243
+ /* @__PURE__ */ jsx2("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx2(AnswerWithCitations, { answer, citations }) })
244
+ ]
245
+ }
246
+ );
247
+ }
248
+ function PolicyComparisonTable({
249
+ categories,
250
+ banks,
251
+ summaryCounts,
252
+ citations,
253
+ queryContext,
254
+ className
255
+ }) {
256
+ const [search, setSearch] = React2.useState("");
257
+ const [filter, setFilter] = React2.useState("all");
258
+ const filtered = React2.useMemo(() => {
259
+ let result = banks;
260
+ if (search.trim()) {
261
+ const q = search.toLowerCase();
262
+ result = result.filter((b) => b.bankName.toLowerCase().includes(q));
263
+ }
264
+ if (filter !== "all") {
265
+ result = result.filter((b) => b.verdict === filter);
266
+ }
267
+ return result;
268
+ }, [banks, search, filter]);
269
+ return /* @__PURE__ */ jsxs2(
270
+ "div",
271
+ {
272
+ "data-slot": "policy-comparison-table",
273
+ className: cn("border border-border bg-card flex flex-col", className),
274
+ children: [
275
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 px-4 py-2.5 border-b border-border bg-muted/30", children: [
276
+ /* @__PURE__ */ jsx2(
277
+ Building2,
278
+ {
279
+ className: "size-3.5 text-muted-foreground shrink-0",
280
+ "aria-hidden": "true"
281
+ }
282
+ ),
283
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 text-sm flex-wrap flex-1 min-w-0", children: [
284
+ /* @__PURE__ */ jsxs2("span", { className: "font-semibold text-foreground", children: [
285
+ summaryCounts.total,
286
+ " banks"
287
+ ] }),
288
+ /* @__PURE__ */ jsx2("span", { className: "text-muted-foreground", children: "\xB7" }),
289
+ /* @__PURE__ */ jsxs2(Badge, { variant: "success", className: "text-xs px-1.5 py-0", children: [
290
+ summaryCounts.yes,
291
+ " Yes"
292
+ ] }),
293
+ /* @__PURE__ */ jsxs2(Badge, { variant: "warning", className: "text-xs px-1.5 py-0", children: [
294
+ summaryCounts.softNo,
295
+ " Soft No"
296
+ ] }),
297
+ /* @__PURE__ */ jsxs2(Badge, { variant: "destructive", className: "text-xs px-1.5 py-0", children: [
298
+ summaryCounts.no,
299
+ " No"
300
+ ] })
301
+ ] }),
302
+ queryContext && /* @__PURE__ */ jsx2(QueryContextInfo, { label: queryContextLabel(queryContext) })
303
+ ] }),
304
+ /* @__PURE__ */ jsx2("div", { className: "px-4 py-2.5 border-b border-border", children: /* @__PURE__ */ jsx2(
305
+ Input,
306
+ {
307
+ "aria-label": "Search banks",
308
+ placeholder: "Search banks\u2026",
309
+ value: search,
310
+ onChange: (e) => setSearch(e.target.value),
311
+ className: "h-8 text-sm"
312
+ }
313
+ ) }),
314
+ /* @__PURE__ */ jsx2("div", { className: "border-b border-border px-4 pt-1", children: /* @__PURE__ */ jsx2(
315
+ Tabs,
316
+ {
317
+ value: filter,
318
+ onValueChange: (v) => setFilter(v),
319
+ children: /* @__PURE__ */ jsxs2(TabsList, { variant: "line", className: "justify-start h-8 gap-0 -mb-px", children: [
320
+ /* @__PURE__ */ jsx2(TabsTrigger, { value: "all", className: "text-sm px-3 h-7", children: "All" }),
321
+ /* @__PURE__ */ jsx2(TabsTrigger, { value: "yes", className: "text-sm px-3 h-7", children: "Yes" }),
322
+ /* @__PURE__ */ jsx2(TabsTrigger, { value: "soft_no", className: "text-sm px-3 h-7", children: "Soft No" }),
323
+ /* @__PURE__ */ jsx2(TabsTrigger, { value: "no", className: "text-sm px-3 h-7", children: "No" })
324
+ ] })
325
+ }
326
+ ) }),
327
+ /* @__PURE__ */ jsx2("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs2("table", { className: "w-full text-sm border-collapse", children: [
328
+ /* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsxs2("tr", { className: "border-b border-border bg-muted/20", children: [
329
+ /* @__PURE__ */ jsx2("th", { className: "text-left text-sm font-medium text-muted-foreground px-4 py-2", children: "Bank" }),
330
+ categories.map((cat) => /* @__PURE__ */ jsx2(
331
+ "th",
332
+ {
333
+ className: "text-left text-sm font-medium text-muted-foreground px-3 py-2 w-px whitespace-nowrap",
334
+ children: cat
335
+ },
336
+ cat
337
+ ))
338
+ ] }) }),
339
+ /* @__PURE__ */ jsx2("tbody", { children: filtered.length === 0 ? /* @__PURE__ */ jsx2("tr", { children: /* @__PURE__ */ jsx2(
340
+ "td",
341
+ {
342
+ colSpan: categories.length + 1,
343
+ className: "px-4 py-6 text-center text-sm text-muted-foreground",
344
+ children: "No banks match your search."
345
+ }
346
+ ) }) : filtered.map((bank) => /* @__PURE__ */ jsxs2("tr", { className: "border-b border-border", children: [
347
+ /* @__PURE__ */ jsx2("td", { className: "px-4 py-2.5", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
348
+ /* @__PURE__ */ jsx2(BankAvatar, { name: bank.bankName }),
349
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-0.5 min-w-0", children: [
350
+ /* @__PURE__ */ jsx2("span", { className: "text-sm font-medium text-foreground leading-snug", children: bank.bankName }),
351
+ bank.details && /* @__PURE__ */ jsx2("span", { className: "text-xs text-muted-foreground leading-snug", children: bank.details })
352
+ ] })
353
+ ] }) }),
354
+ categories.map((cat) => /* @__PURE__ */ jsx2(
355
+ "td",
356
+ {
357
+ className: "px-3 py-2.5 align-top w-px whitespace-nowrap",
358
+ children: /* @__PURE__ */ jsx2(PolicyVerdictBadge, { verdict: bank.verdict })
359
+ },
360
+ cat
361
+ ))
362
+ ] }, bank.bankName)) })
363
+ ] }) })
364
+ ]
365
+ }
366
+ );
367
+ }
368
+ var VERDICT_SCORE_COLORS = {
369
+ yes: { track: "bg-success/20", indicator: "bg-success" },
370
+ soft_no: { track: "bg-warning/20", indicator: "bg-warning" },
371
+ no: { track: "bg-destructive/20", indicator: "bg-destructive" },
372
+ unknown: { track: "bg-muted", indicator: "bg-muted-foreground" }
373
+ };
374
+ function PolicyRankedList({
375
+ categories,
376
+ banks,
377
+ citations,
378
+ queryContext,
379
+ className
380
+ }) {
381
+ return /* @__PURE__ */ jsxs2(
382
+ "div",
383
+ {
384
+ "data-slot": "policy-ranked-list",
385
+ className: cn("border border-border bg-card flex flex-col", className),
386
+ children: [
387
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 px-4 py-2.5 border-b border-border bg-muted/30", children: [
388
+ /* @__PURE__ */ jsx2("span", { className: "text-sm text-muted-foreground shrink-0", children: "Ranked:" }),
389
+ /* @__PURE__ */ jsx2("div", { className: "flex items-center gap-1 flex-wrap flex-1 min-w-0", children: categories.map((cat, i) => /* @__PURE__ */ jsxs2(React2.Fragment, { children: [
390
+ i > 0 && /* @__PURE__ */ jsx2("span", { className: "text-sm text-muted-foreground", children: "+" }),
391
+ /* @__PURE__ */ jsx2(Badge, { variant: "secondary", className: "text-xs px-1.5 py-0", children: cat })
392
+ ] }, cat)) }),
393
+ /* @__PURE__ */ jsxs2("span", { className: "text-sm text-muted-foreground shrink-0", children: [
394
+ banks.length,
395
+ " banks"
396
+ ] }),
397
+ queryContext && /* @__PURE__ */ jsx2(QueryContextInfo, { label: queryContextLabel(queryContext) })
398
+ ] }),
399
+ /* @__PURE__ */ jsx2("div", { className: "divide-y divide-border", children: banks.map((bank) => {
400
+ var _a, _b;
401
+ const scorePercent = Math.round(bank.score * 100);
402
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col", children: [
403
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 px-4 py-3", children: [
404
+ /* @__PURE__ */ jsxs2("span", { className: "shrink-0 w-6 text-center text-sm font-bold text-muted-foreground", children: [
405
+ "#",
406
+ bank.rank
407
+ ] }),
408
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 min-w-0 flex flex-col gap-1.5", children: [
409
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-2", children: [
410
+ /* @__PURE__ */ jsx2("span", { className: "text-sm font-medium text-foreground truncate", children: bank.bankName }),
411
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 shrink-0", children: [
412
+ /* @__PURE__ */ jsxs2("span", { className: "text-sm text-muted-foreground font-mono", children: [
413
+ scorePercent,
414
+ "%"
415
+ ] }),
416
+ /* @__PURE__ */ jsx2(PolicyVerdictBadge, { verdict: bank.verdict })
417
+ ] })
418
+ ] }),
419
+ /* @__PURE__ */ jsx2(
420
+ Progress,
421
+ {
422
+ value: scorePercent,
423
+ className: cn(
424
+ "h-1.5",
425
+ ((_a = VERDICT_SCORE_COLORS[bank.verdict]) != null ? _a : VERDICT_SCORE_COLORS.unknown).track
426
+ ),
427
+ indicatorClassName: ((_b = VERDICT_SCORE_COLORS[bank.verdict]) != null ? _b : VERDICT_SCORE_COLORS.unknown).indicator,
428
+ "aria-label": `${bank.bankName} policy score: ${scorePercent}%`
429
+ }
430
+ )
431
+ ] })
432
+ ] }),
433
+ bank.highlights.length > 0 && /* @__PURE__ */ jsx2("div", { className: "px-4 pb-3 pl-[52px]", children: /* @__PURE__ */ jsx2("ul", { className: "flex flex-col gap-0.5", children: bank.highlights.map((h, i) => /* @__PURE__ */ jsx2(
434
+ "li",
435
+ {
436
+ className: "text-sm text-muted-foreground leading-relaxed list-disc list-inside",
437
+ children: h
438
+ },
439
+ i
440
+ )) }) })
441
+ ] }, bank.bankName);
442
+ }) })
443
+ ]
444
+ }
445
+ );
446
+ }
447
+
448
+ // src/components/ui/policy-ai/policy-ai-panel.tsx
449
+ import * as React3 from "react";
450
+ import {
451
+ BrainCircuit,
452
+ Check,
453
+ ChevronDown as ChevronDown2,
454
+ Minus,
455
+ RotateCcw,
456
+ X
457
+ } from "lucide-react";
458
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
459
+ var DEFAULT_SUGGESTED = {
460
+ Income: [
461
+ "Which banks accept casual income?",
462
+ "Which lenders accept bonus income, ranked best to worst?",
463
+ "How is self-employed income assessed across lenders?"
464
+ ],
465
+ Security: [
466
+ "Which banks accept apartments under 40sqm?",
467
+ "What is the maximum LVR for a serviced apartment?",
468
+ "Do any lenders accept hobby farm properties?"
469
+ ],
470
+ Serviceability: [
471
+ "Which banks have the most flexible DTI ratio?",
472
+ "How is HECS debt treated by different lenders?",
473
+ "Which lenders apply the lowest assessment buffer rate?"
474
+ ],
475
+ "Loan Type": [
476
+ "Which lenders offer cashback on refinances?",
477
+ "Which banks accept low-doc construction loans?",
478
+ "What are the pre-approval policies across lenders?"
479
+ ],
480
+ Borrower: [
481
+ "Which banks accept non-resident borrowers?",
482
+ "Which lenders offer LMI waiver for medical professionals?",
483
+ "How do lenders treat prior credit impairment?"
484
+ ]
485
+ };
486
+ var DEFAULT_THINKING_STEPS = [
487
+ "Classifying query type\u2026",
488
+ "Searching across 40+ banks\u2026",
489
+ "Retrieving policy documents\u2026",
490
+ "Generating response\u2026"
491
+ ];
492
+ function PolicyAIThinkingSteps({ steps }) {
493
+ const [currentStep, setCurrentStep] = React3.useState(0);
494
+ React3.useEffect(() => {
495
+ setCurrentStep(0);
496
+ }, [steps]);
497
+ React3.useEffect(() => {
498
+ if (currentStep >= steps.length - 1) return;
499
+ const timer = setTimeout(() => setCurrentStep((s) => s + 1), 1400);
500
+ return () => clearTimeout(timer);
501
+ }, [currentStep, steps.length]);
502
+ return /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-2.5 px-4 py-3", children: steps.map((step, i) => {
503
+ const isDone = i < currentStep;
504
+ const isCurrent = i === currentStep;
505
+ const isPending = i > currentStep;
506
+ return /* @__PURE__ */ jsxs3(
507
+ "div",
508
+ {
509
+ className: cn(
510
+ "flex items-center gap-2.5 transition-opacity duration-300",
511
+ isPending && "opacity-30"
512
+ ),
513
+ children: [
514
+ /* @__PURE__ */ jsx3("span", { className: "shrink-0 size-4 flex items-center justify-center", children: isDone ? /* @__PURE__ */ jsx3(Check, { className: "size-3.5 text-primary", "aria-hidden": "true" }) : isCurrent ? /* @__PURE__ */ jsx3(
515
+ "span",
516
+ {
517
+ className: "flex items-center gap-0.5",
518
+ "aria-label": "In progress",
519
+ children: [0, 1, 2].map((j) => /* @__PURE__ */ jsx3(
520
+ "span",
521
+ {
522
+ className: "size-1 bg-muted-foreground/70 animate-bounce",
523
+ style: { animationDelay: `${j * 150}ms` }
524
+ },
525
+ j
526
+ ))
527
+ }
528
+ ) : null }),
529
+ /* @__PURE__ */ jsx3(
530
+ "span",
531
+ {
532
+ className: cn(
533
+ "text-sm leading-snug transition-colors duration-200",
534
+ isDone && "text-muted-foreground",
535
+ isCurrent && "text-foreground font-medium",
536
+ isPending && "text-muted-foreground"
537
+ ),
538
+ children: step
539
+ }
540
+ )
541
+ ]
542
+ },
543
+ step
544
+ );
545
+ }) });
546
+ }
547
+ function ResponseCard({
548
+ content,
549
+ queryContext
550
+ }) {
551
+ if (content.type === "single_bank") {
552
+ return /* @__PURE__ */ jsx3(
553
+ PolicySingleBankAnswer,
554
+ {
555
+ bankName: content.bankName,
556
+ verdict: content.verdict,
557
+ answer: content.answer,
558
+ citations: content.citations,
559
+ queryContext
560
+ }
561
+ );
562
+ }
563
+ if (content.type === "cross_bank_comparison") {
564
+ return /* @__PURE__ */ jsx3(
565
+ PolicyComparisonTable,
566
+ {
567
+ categories: content.categories,
568
+ banks: content.banks,
569
+ summaryCounts: content.summaryCounts,
570
+ citations: content.citations,
571
+ queryContext
572
+ }
573
+ );
574
+ }
575
+ if (content.type === "ranked_list") {
576
+ return /* @__PURE__ */ jsx3(
577
+ PolicyRankedList,
578
+ {
579
+ categories: content.categories,
580
+ banks: content.banks,
581
+ citations: content.citations,
582
+ queryContext
583
+ }
584
+ );
585
+ }
586
+ return null;
587
+ }
588
+ function PolicyAIFAB({
589
+ onClick,
590
+ hasNudge,
591
+ className
592
+ }) {
593
+ return /* @__PURE__ */ jsxs3("div", { className: cn("relative inline-flex shrink-0", className), children: [
594
+ /* @__PURE__ */ jsx3(
595
+ Button,
596
+ {
597
+ "data-slot": "policy-ai-fab",
598
+ size: "icon",
599
+ onClick,
600
+ className: "size-12 shadow-lg rounded-full",
601
+ "aria-label": "Open Policy AI",
602
+ children: /* @__PURE__ */ jsx3(BrainCircuit, { className: "size-5", "aria-hidden": "true" })
603
+ }
604
+ ),
605
+ hasNudge && /* @__PURE__ */ jsx3("span", { className: "absolute -top-1 -right-1 size-3 bg-destructive rounded-full border-2 border-background" })
606
+ ] });
607
+ }
608
+ function PolicyAIPanel({
609
+ open,
610
+ onClose,
611
+ messages = [],
612
+ suggestedQuestions,
613
+ isStreaming = false,
614
+ isLoading = false,
615
+ thinkingSteps,
616
+ onSendMessage,
617
+ onAttachFile,
618
+ onAttachImage,
619
+ onReset,
620
+ position = "bottom-right",
621
+ inline = false,
622
+ className
623
+ }) {
624
+ var _a;
625
+ const resolvedThinkingSteps = thinkingSteps != null ? thinkingSteps : DEFAULT_THINKING_STEPS;
626
+ const [inputValue, setInputValue] = React3.useState("");
627
+ const [minimised, setMinimised] = React3.useState(false);
628
+ const [activeTab, setActiveTab] = React3.useState("Income");
629
+ const messagesEndRef = React3.useRef(null);
630
+ const isChatMode = messages.length > 0;
631
+ React3.useEffect(() => {
632
+ var _a2;
633
+ if (open && !minimised) {
634
+ (_a2 = messagesEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
635
+ }
636
+ }, [messages, open, minimised]);
637
+ const handleSend = React3.useCallback(
638
+ (text) => {
639
+ if (!text || isStreaming) return;
640
+ onSendMessage == null ? void 0 : onSendMessage(text);
641
+ setInputValue("");
642
+ },
643
+ [isStreaming, onSendMessage]
644
+ );
645
+ if (!open) return null;
646
+ const positionClass = position === "bottom-left" ? "left-6" : "right-6";
647
+ return /* @__PURE__ */ jsxs3(
648
+ "div",
649
+ {
650
+ "data-slot": "policy-ai-panel",
651
+ className: cn(
652
+ "flex flex-col bg-card",
653
+ inline ? "h-full border-0" : [
654
+ "fixed z-50 border border-border shadow-2xl",
655
+ "bottom-20 w-[400px]",
656
+ "animate-in slide-in-from-bottom-4 fade-in duration-200",
657
+ positionClass,
658
+ minimised && "shadow-lg"
659
+ ],
660
+ className
661
+ ),
662
+ style: inline ? void 0 : {
663
+ maxHeight: minimised ? "auto" : "min(600px, calc(100vh - 120px))"
664
+ },
665
+ children: [
666
+ !inline && /* @__PURE__ */ jsxs3("div", { className: "shrink-0 flex items-center gap-2.5 px-3 py-2.5 border-b border-border bg-card", children: [
667
+ /* @__PURE__ */ jsx3(
668
+ BrainCircuit,
669
+ {
670
+ className: "size-4 text-primary shrink-0",
671
+ "aria-hidden": "true"
672
+ }
673
+ ),
674
+ /* @__PURE__ */ jsx3("span", { className: "text-sm font-semibold text-foreground flex-1 truncate", children: "Policy AI" }),
675
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-0.5", children: [
676
+ isChatMode && onReset && !minimised && /* @__PURE__ */ jsx3(
677
+ Button,
678
+ {
679
+ variant: "ghost",
680
+ size: "icon-sm",
681
+ onClick: onReset,
682
+ title: "New conversation",
683
+ "aria-label": "New conversation",
684
+ children: /* @__PURE__ */ jsx3(RotateCcw, { className: "size-3.5", "aria-hidden": "true" })
685
+ }
686
+ ),
687
+ /* @__PURE__ */ jsx3(
688
+ Button,
689
+ {
690
+ variant: "ghost",
691
+ size: "icon-sm",
692
+ onClick: () => setMinimised((v) => !v),
693
+ title: minimised ? "Restore" : "Minimise",
694
+ "aria-label": minimised ? "Restore panel" : "Minimise panel",
695
+ children: minimised ? /* @__PURE__ */ jsx3(ChevronDown2, { className: "size-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx3(Minus, { className: "size-3.5", "aria-hidden": "true" })
696
+ }
697
+ ),
698
+ /* @__PURE__ */ jsx3(
699
+ Button,
700
+ {
701
+ variant: "ghost",
702
+ size: "icon-sm",
703
+ onClick: onClose,
704
+ title: "Close",
705
+ "aria-label": "Close Policy AI",
706
+ children: /* @__PURE__ */ jsx3(X, { className: "size-3.5", "aria-hidden": "true" })
707
+ }
708
+ )
709
+ ] })
710
+ ] }),
711
+ (!minimised || inline) && /* @__PURE__ */ jsxs3(Fragment3, { children: [
712
+ /* @__PURE__ */ jsx3("div", { className: cn(!inline && "flex-1 overflow-y-auto min-h-0"), children: isLoading ? /* @__PURE__ */ jsx3("div", { className: "flex flex-col justify-center h-full py-8", children: /* @__PURE__ */ jsx3(PolicyAIThinkingSteps, { steps: resolvedThinkingSteps }) }) : !isChatMode ? (
713
+ /* Home state — suggested questions by policy type */
714
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-col", children: [
715
+ /* @__PURE__ */ jsx3("div", { className: "px-3 pt-3 pb-2", children: /* @__PURE__ */ jsx3("p", { className: "text-xs text-muted-foreground leading-relaxed", children: "Ask me about lending policies across 40+ Australian banks \u2014 income, LVR, security types, serviceability, and more." }) }),
716
+ /* @__PURE__ */ jsx3("div", { className: "border-b border-border px-3 pt-1", children: /* @__PURE__ */ jsx3(
717
+ Tabs,
718
+ {
719
+ value: activeTab,
720
+ onValueChange: (v) => setActiveTab(v),
721
+ children: /* @__PURE__ */ jsx3(
722
+ TabsList,
723
+ {
724
+ variant: "line",
725
+ className: "justify-start h-8 gap-0 -mb-px overflow-x-auto",
726
+ children: Object.keys(DEFAULT_SUGGESTED).map(
727
+ (type) => /* @__PURE__ */ jsx3(
728
+ TabsTrigger,
729
+ {
730
+ value: type,
731
+ className: "text-xs px-2 h-7 shrink-0",
732
+ children: type
733
+ },
734
+ type
735
+ )
736
+ )
737
+ }
738
+ )
739
+ }
740
+ ) }),
741
+ /* @__PURE__ */ jsx3("div", { className: "flex flex-col px-3 py-2 gap-1", children: ((_a = (suggestedQuestions != null ? suggestedQuestions : DEFAULT_SUGGESTED)[activeTab]) != null ? _a : []).map((q) => /* @__PURE__ */ jsx3(
742
+ "button",
743
+ {
744
+ onClick: () => setInputValue(q),
745
+ className: "w-full text-left text-xs text-foreground px-3 py-2 border border-border hover:bg-muted/60 transition-colors leading-relaxed",
746
+ children: q
747
+ },
748
+ q
749
+ )) })
750
+ ] })
751
+ ) : (
752
+ /* Chat state — messages + structured response cards */
753
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-4 px-3 py-3", children: [
754
+ messages.map(
755
+ (msg) => msg.role === "user" ? /* @__PURE__ */ jsx3(
756
+ ChatWidgetMessage,
757
+ {
758
+ role: "user",
759
+ content: msg.content
760
+ },
761
+ msg.id
762
+ ) : /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1.5", children: [
763
+ msg.content && /* @__PURE__ */ jsx3("p", { className: "text-sm text-foreground leading-relaxed", children: msg.content }),
764
+ msg.responseContent && /* @__PURE__ */ jsx3(
765
+ ResponseCard,
766
+ {
767
+ content: msg.responseContent,
768
+ queryContext: msg.queryContext
769
+ }
770
+ )
771
+ ] }, msg.id)
772
+ ),
773
+ isStreaming && /* @__PURE__ */ jsx3(PolicyAIThinkingSteps, { steps: resolvedThinkingSteps }),
774
+ /* @__PURE__ */ jsx3("div", { ref: messagesEndRef })
775
+ ] })
776
+ ) }),
777
+ !isLoading && /* @__PURE__ */ jsx3(
778
+ "div",
779
+ {
780
+ className: cn(
781
+ "border-t border-border px-3 py-3 bg-card",
782
+ inline ? "sticky bottom-0 z-10" : "shrink-0"
783
+ ),
784
+ children: /* @__PURE__ */ jsx3(
785
+ ChatInputArea,
786
+ {
787
+ value: inputValue,
788
+ onChange: setInputValue,
789
+ onSend: handleSend,
790
+ onAttachFile,
791
+ onAttachImage,
792
+ disabled: isStreaming,
793
+ placeholder: "Ask about lending policies\u2026",
794
+ autoFocus: !minimised
795
+ }
796
+ )
797
+ }
798
+ )
799
+ ] })
800
+ ]
801
+ }
802
+ );
803
+ }
804
+
805
+ export {
806
+ PolicyQueryChip,
807
+ PolicyVerdictBadge,
808
+ PolicyCitationPanel,
809
+ PolicySingleBankAnswer,
810
+ PolicyComparisonTable,
811
+ PolicyRankedList,
812
+ PolicyAIFAB,
813
+ PolicyAIPanel
814
+ };