@wealthx/shadcn 1.5.38 → 1.5.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +98 -95
- package/CHANGELOG.md +12 -0
- package/dist/{chunk-LSSIWLYU.mjs → chunk-6XNEHTII.mjs} +1 -1
- package/dist/{chunk-ULQ53FRJ.mjs → chunk-7NQKFPXE.mjs} +1 -1
- package/dist/{chunk-DSVKEVX6.mjs → chunk-CZOGJC76.mjs} +1 -1
- package/dist/{chunk-JPGL36WQ.mjs → chunk-FL7DEYUA.mjs} +6 -7
- package/dist/{chunk-2CHH5QOA.mjs → chunk-FQUT5XD6.mjs} +1 -1
- package/dist/chunk-MGIDYXOP.mjs +814 -0
- package/dist/{chunk-OG2VM34K.mjs → chunk-MHBQJVHE.mjs} +1 -1
- package/dist/{chunk-R7M657QL.mjs → chunk-STN5QIWN.mjs} +36 -1
- package/dist/components/ui/file-preview-dialog.js +6 -7
- package/dist/components/ui/file-preview-dialog.mjs +2 -2
- package/dist/components/ui/kanban-column.js +6 -7
- package/dist/components/ui/kanban-column.mjs +3 -3
- package/dist/components/ui/opportunity-card.js +6 -7
- package/dist/components/ui/opportunity-card.mjs +2 -2
- package/dist/components/ui/pipeline-board.js +6 -7
- package/dist/components/ui/pipeline-board.mjs +4 -4
- package/dist/components/ui/policy-ai/index.js +1636 -0
- package/dist/components/ui/policy-ai/index.mjs +36 -0
- package/dist/components/ui/progress.js +6 -7
- package/dist/components/ui/progress.mjs +1 -1
- package/dist/components/ui/stage-timeline.js +6 -7
- package/dist/components/ui/stage-timeline.mjs +2 -2
- package/dist/components/ui/support-agent/index.js +31 -1
- package/dist/components/ui/support-agent/index.mjs +1 -1
- package/dist/index.js +4105 -3299
- package/dist/index.mjs +25 -7
- package/dist/styles.css +1 -1
- package/package.json +10 -5
- package/src/components/index.tsx +30 -0
- package/src/components/ui/policy-ai/index.tsx +41 -0
- package/src/components/ui/policy-ai/policy-ai-panel.tsx +526 -0
- package/src/components/ui/policy-ai/policy-ai-primitives.tsx +332 -0
- package/src/components/ui/policy-ai/policy-ai-responses.tsx +543 -0
- package/src/components/ui/progress.tsx +15 -12
- package/src/components/ui/support-agent/support-agent-panel.tsx +46 -2
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +2 -1
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ChevronDown, ChevronUp, FileText, Tag } from "lucide-react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { Badge } from "@/components/ui/badge";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Types
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
export type PolicyType =
|
|
12
|
+
| "Income"
|
|
13
|
+
| "Security"
|
|
14
|
+
| "Serviceability"
|
|
15
|
+
| "Loan Type"
|
|
16
|
+
| "Borrower";
|
|
17
|
+
|
|
18
|
+
export type PolicyQueryType =
|
|
19
|
+
| "single_bank"
|
|
20
|
+
| "cross_bank_comparison"
|
|
21
|
+
| "threshold_filter"
|
|
22
|
+
| "ranking"
|
|
23
|
+
| "scenario_match"
|
|
24
|
+
| "general";
|
|
25
|
+
|
|
26
|
+
export type PolicyVerdict = "yes" | "soft_no" | "no" | "unknown";
|
|
27
|
+
|
|
28
|
+
/** What the Policy AI classifier detected about the broker's query. */
|
|
29
|
+
export interface PolicyQueryContext {
|
|
30
|
+
policyType: PolicyType;
|
|
31
|
+
/** Fine-grained categories, e.g. ["Bonus Income", "Add Backs"] */
|
|
32
|
+
categories: string[];
|
|
33
|
+
queryType: PolicyQueryType;
|
|
34
|
+
/** Only set for single_bank queries. */
|
|
35
|
+
bankName?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A single citation attached to a Policy AI response. */
|
|
39
|
+
export interface PolicyCitationItem {
|
|
40
|
+
/** 1-based index used inline as [N] markers in the answer text. */
|
|
41
|
+
index: number;
|
|
42
|
+
bankName: string;
|
|
43
|
+
category: string;
|
|
44
|
+
/** Excerpt from the source policy document. */
|
|
45
|
+
excerpt: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A bank's verdict in a cross-bank comparison. */
|
|
49
|
+
export interface PolicyBankVerdict {
|
|
50
|
+
bankName: string;
|
|
51
|
+
verdict: PolicyVerdict;
|
|
52
|
+
/** Brief human-readable explanation from the policy engine. */
|
|
53
|
+
details?: string;
|
|
54
|
+
citations?: number[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** A bank's entry in a TOPSIS-ranked list. */
|
|
58
|
+
export interface PolicyRankedBankItem {
|
|
59
|
+
rank: number;
|
|
60
|
+
bankName: string;
|
|
61
|
+
/** TOPSIS closeness score 0.0–1.0 (higher = better). */
|
|
62
|
+
score: number;
|
|
63
|
+
verdict: PolicyVerdict;
|
|
64
|
+
/** Key policy highlights shown as bullets under the bank row. */
|
|
65
|
+
highlights: string[];
|
|
66
|
+
citations?: number[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Discriminated union of the three response content formats that Policy AI
|
|
71
|
+
* can return, corresponding to retrieval tiers A / B / C.
|
|
72
|
+
*/
|
|
73
|
+
export type PolicyResponseContent =
|
|
74
|
+
| {
|
|
75
|
+
type: "single_bank";
|
|
76
|
+
bankName: string;
|
|
77
|
+
verdict: PolicyVerdict;
|
|
78
|
+
/** Prose answer with inline [N] citation markers. */
|
|
79
|
+
answer: string;
|
|
80
|
+
citations: PolicyCitationItem[];
|
|
81
|
+
}
|
|
82
|
+
| {
|
|
83
|
+
type: "cross_bank_comparison";
|
|
84
|
+
categories: string[];
|
|
85
|
+
banks: PolicyBankVerdict[];
|
|
86
|
+
summaryCounts: {
|
|
87
|
+
yes: number;
|
|
88
|
+
softNo: number;
|
|
89
|
+
no: number;
|
|
90
|
+
total: number;
|
|
91
|
+
};
|
|
92
|
+
citations: PolicyCitationItem[];
|
|
93
|
+
}
|
|
94
|
+
| {
|
|
95
|
+
type: "ranked_list";
|
|
96
|
+
categories: string[];
|
|
97
|
+
banks: PolicyRankedBankItem[];
|
|
98
|
+
citations: PolicyCitationItem[];
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A past conversation entry — reserved for the conversation history feature.
|
|
103
|
+
* Used when rendering a list of previous Policy AI sessions (inline mode).
|
|
104
|
+
*/
|
|
105
|
+
export interface PolicyConversationItem {
|
|
106
|
+
id: string;
|
|
107
|
+
/** First user query — used as the conversation title. */
|
|
108
|
+
title: string;
|
|
109
|
+
/** Human-readable timestamp, e.g. "Today, 10:24 AM". */
|
|
110
|
+
timestamp?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** A single message in the Policy AI chat. */
|
|
114
|
+
export interface PolicyAIMessage {
|
|
115
|
+
id: string;
|
|
116
|
+
role: "user" | "assistant";
|
|
117
|
+
content: string;
|
|
118
|
+
/** Classification info shown above the response as a query chip. */
|
|
119
|
+
queryContext?: PolicyQueryContext;
|
|
120
|
+
/** Structured response payload (absent for user messages). */
|
|
121
|
+
responseContent?: PolicyResponseContent;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// PolicyQueryChip props
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
export interface PolicyQueryChipProps {
|
|
129
|
+
context: PolicyQueryContext;
|
|
130
|
+
className?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// PolicyVerdictBadge props
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
export interface PolicyVerdictBadgeProps {
|
|
138
|
+
verdict: PolicyVerdict;
|
|
139
|
+
className?: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// PolicyCitationPanel props
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
export interface PolicyCitationPanelProps {
|
|
147
|
+
citations: PolicyCitationItem[];
|
|
148
|
+
className?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Internal helpers
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
const POLICY_TYPE_COLORS: Record<PolicyType, string> = {
|
|
156
|
+
Income: "text-primary",
|
|
157
|
+
Security: "text-warning",
|
|
158
|
+
Serviceability: "text-info",
|
|
159
|
+
"Loan Type": "text-secondary",
|
|
160
|
+
Borrower: "text-success",
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const QUERY_TYPE_LABELS: Record<PolicyQueryType, string> = {
|
|
164
|
+
single_bank: "Single bank",
|
|
165
|
+
cross_bank_comparison: "Cross-bank",
|
|
166
|
+
threshold_filter: "Filter",
|
|
167
|
+
ranking: "Ranking",
|
|
168
|
+
scenario_match: "Scenario",
|
|
169
|
+
general: "General",
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// PolicyQueryChip (Atom)
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Chip rendered above every Policy AI assistant response.
|
|
178
|
+
* Shows the detected policy type, categories, and query routing type —
|
|
179
|
+
* equivalent to Jira Rovo's "Searching Confluence…" context indicator.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* <PolicyQueryChip context={{ policyType: "Income", categories: ["Bonus Income"], queryType: "ranking" }} />
|
|
183
|
+
*/
|
|
184
|
+
export function PolicyQueryChip({ context, className }: PolicyQueryChipProps) {
|
|
185
|
+
const { policyType, categories, queryType, bankName } = context;
|
|
186
|
+
const typeColor = POLICY_TYPE_COLORS[policyType];
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<div
|
|
190
|
+
data-slot="policy-query-chip"
|
|
191
|
+
className={cn(
|
|
192
|
+
"inline-flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground",
|
|
193
|
+
className,
|
|
194
|
+
)}
|
|
195
|
+
>
|
|
196
|
+
<Tag className="size-3 shrink-0" aria-hidden="true" />
|
|
197
|
+
|
|
198
|
+
{/* Policy type */}
|
|
199
|
+
<span className={cn("font-medium", typeColor)}>{policyType}</span>
|
|
200
|
+
|
|
201
|
+
{/* Bank name (single_bank only) */}
|
|
202
|
+
{bankName && (
|
|
203
|
+
<>
|
|
204
|
+
<span className="text-border">·</span>
|
|
205
|
+
<span className="font-medium text-foreground">{bankName}</span>
|
|
206
|
+
</>
|
|
207
|
+
)}
|
|
208
|
+
|
|
209
|
+
{/* Categories */}
|
|
210
|
+
{categories.length > 0 && (
|
|
211
|
+
<>
|
|
212
|
+
<span className="text-border">·</span>
|
|
213
|
+
<span>{categories.join(", ")}</span>
|
|
214
|
+
</>
|
|
215
|
+
)}
|
|
216
|
+
|
|
217
|
+
{/* Query type badge */}
|
|
218
|
+
<Badge variant="outline" className="text-xs px-1.5 py-0 shrink-0">
|
|
219
|
+
{QUERY_TYPE_LABELS[queryType]}
|
|
220
|
+
</Badge>
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
// PolicyVerdictBadge (Atom)
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
const VERDICT_CONFIG: Record<
|
|
230
|
+
PolicyVerdict,
|
|
231
|
+
{
|
|
232
|
+
variant: "success" | "warning" | "destructive" | "secondary";
|
|
233
|
+
label: string;
|
|
234
|
+
}
|
|
235
|
+
> = {
|
|
236
|
+
yes: { variant: "success", label: "Yes" },
|
|
237
|
+
soft_no: { variant: "warning", label: "Soft No" },
|
|
238
|
+
no: { variant: "destructive", label: "No" },
|
|
239
|
+
unknown: { variant: "secondary", label: "—" },
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Coloured badge indicating a bank's lending policy verdict.
|
|
244
|
+
* - Yes → success (green)
|
|
245
|
+
* - Soft No → warning (amber) — accepted on specialist/non-prime products
|
|
246
|
+
* - No → destructive (red) — not accepted
|
|
247
|
+
* - Unknown → secondary — data not available
|
|
248
|
+
*/
|
|
249
|
+
export function PolicyVerdictBadge({
|
|
250
|
+
verdict,
|
|
251
|
+
className,
|
|
252
|
+
}: PolicyVerdictBadgeProps) {
|
|
253
|
+
const { variant, label } = VERDICT_CONFIG[verdict];
|
|
254
|
+
return (
|
|
255
|
+
<Badge
|
|
256
|
+
data-slot="policy-verdict-badge"
|
|
257
|
+
variant={variant}
|
|
258
|
+
className={cn("shrink-0", className)}
|
|
259
|
+
>
|
|
260
|
+
{label}
|
|
261
|
+
</Badge>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// PolicyCitationPanel (Molecule)
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Collapsible panel listing the policy document sources behind an AI response.
|
|
271
|
+
* Rendered below every response format (SingleBankAnswer, ComparisonTable, RankedList).
|
|
272
|
+
* Collapsed by default; expands on toggle.
|
|
273
|
+
*/
|
|
274
|
+
export function PolicyCitationPanel({
|
|
275
|
+
citations,
|
|
276
|
+
className,
|
|
277
|
+
}: PolicyCitationPanelProps) {
|
|
278
|
+
const [open, setOpen] = React.useState(false);
|
|
279
|
+
|
|
280
|
+
if (citations.length === 0) return null;
|
|
281
|
+
|
|
282
|
+
return (
|
|
283
|
+
<div
|
|
284
|
+
data-slot="policy-citation-panel"
|
|
285
|
+
className={cn("border-t border-border", className)}
|
|
286
|
+
>
|
|
287
|
+
<Button
|
|
288
|
+
variant="ghost"
|
|
289
|
+
size="sm"
|
|
290
|
+
className="w-full justify-between px-0 text-xs text-muted-foreground hover:text-foreground hover:bg-transparent gap-1"
|
|
291
|
+
onClick={() => setOpen((v) => !v)}
|
|
292
|
+
aria-expanded={open}
|
|
293
|
+
>
|
|
294
|
+
<span className="flex items-center gap-1.5">
|
|
295
|
+
<FileText className="size-3" aria-hidden="true" />
|
|
296
|
+
View sources ({citations.length})
|
|
297
|
+
</span>
|
|
298
|
+
{open ? (
|
|
299
|
+
<ChevronUp className="size-3" aria-hidden="true" />
|
|
300
|
+
) : (
|
|
301
|
+
<ChevronDown className="size-3" aria-hidden="true" />
|
|
302
|
+
)}
|
|
303
|
+
</Button>
|
|
304
|
+
|
|
305
|
+
{open && (
|
|
306
|
+
<ol className="mt-1 flex flex-col gap-2 pb-1">
|
|
307
|
+
{citations.map((cite) => (
|
|
308
|
+
<li key={cite.index} className="flex gap-2 text-xs">
|
|
309
|
+
{/* Index chip */}
|
|
310
|
+
<span className="shrink-0 flex items-center justify-center size-4 bg-muted text-muted-foreground font-mono font-semibold text-xs">
|
|
311
|
+
{cite.index}
|
|
312
|
+
</span>
|
|
313
|
+
<div className="flex flex-col gap-0.5 min-w-0">
|
|
314
|
+
<div className="flex items-center gap-1.5 flex-wrap">
|
|
315
|
+
<span className="font-medium text-foreground">
|
|
316
|
+
{cite.bankName}
|
|
317
|
+
</span>
|
|
318
|
+
<Badge variant="secondary" className="text-xs px-1.5 py-0">
|
|
319
|
+
{cite.category}
|
|
320
|
+
</Badge>
|
|
321
|
+
</div>
|
|
322
|
+
<p className="text-muted-foreground leading-relaxed line-clamp-3">
|
|
323
|
+
{cite.excerpt}
|
|
324
|
+
</p>
|
|
325
|
+
</div>
|
|
326
|
+
</li>
|
|
327
|
+
))}
|
|
328
|
+
</ol>
|
|
329
|
+
)}
|
|
330
|
+
</div>
|
|
331
|
+
);
|
|
332
|
+
}
|