@wealthx/shadcn 1.5.37 → 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.
- package/.turbo/turbo-build.log +142 -133
- 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-734FOOJC.mjs → chunk-B5PSUONN.mjs} +25 -58
- package/dist/{chunk-DSVKEVX6.mjs → chunk-CZOGJC76.mjs} +1 -1
- package/dist/chunk-EFHPSKVF.mjs +192 -0
- 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-NB3ZL36B.mjs → chunk-MZI77ZMX.mjs} +17 -2
- package/dist/chunk-R7M657QL.mjs +587 -0
- package/dist/{chunk-DIH2NZZ3.mjs → chunk-RRROLESJ.mjs} +33 -23
- package/dist/components/ui/ai-assistant-drawer.js +269 -121
- package/dist/components/ui/ai-assistant-drawer.mjs +2 -1
- package/dist/components/ui/ai-conversations/index.js +474 -286
- package/dist/components/ui/ai-conversations/index.mjs +2 -1
- package/dist/components/ui/chat-input-area.js +429 -0
- package/dist/components/ui/chat-input-area.mjs +11 -0
- 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/page-top-bar.js +182 -5
- package/dist/components/ui/page-top-bar.mjs +3 -1
- 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 +1131 -0
- package/dist/components/ui/support-agent/index.mjs +27 -0
- package/dist/index.js +5609 -4100
- package/dist/index.mjs +77 -41
- package/dist/styles.css +1 -1
- package/package.json +16 -1
- package/src/components/index.tsx +54 -0
- package/src/components/ui/ai-assistant-drawer.tsx +24 -51
- package/src/components/ui/ai-conversations/index.tsx +16 -8
- package/src/components/ui/ai-conversations/thread.tsx +38 -27
- package/src/components/ui/chat-input-area.tsx +244 -0
- package/src/components/ui/page-top-bar.tsx +31 -5
- 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/index.tsx +25 -0
- package/src/components/ui/support-agent/support-agent-fab.tsx +116 -0
- package/src/components/ui/support-agent/support-agent-panel.tsx +498 -0
- package/src/components/ui/support-agent/support-agent-primitives.tsx +354 -0
- package/src/styles/globals.css +1 -0
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +3 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Check,
|
|
4
|
+
ChevronDown,
|
|
5
|
+
ChevronUp,
|
|
6
|
+
ExternalLink,
|
|
7
|
+
MapPin,
|
|
8
|
+
MousePointerClick,
|
|
9
|
+
} from "lucide-react";
|
|
10
|
+
import { cn } from "@/lib/utils";
|
|
11
|
+
import { Button } from "@/components/ui/button";
|
|
12
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Support Agent Primitives — WealthX DS
|
|
16
|
+
*
|
|
17
|
+
* Atomic building blocks for the Support Agent panel.
|
|
18
|
+
* Inspired by Jira Rovo's sidebar chat pattern:
|
|
19
|
+
* - SupportContextChip — shows current backoffice page + entity (Rovo: page context header)
|
|
20
|
+
* - SupportSuggestedQuestion — clickable question cards (Rovo: conversation starters)
|
|
21
|
+
* - SupportStepGuideCard — numbered walkthrough with "Show me" DOM highlight (Rovo: message cards)
|
|
22
|
+
* - SupportArticleCard — help article reference link (Rovo: linked content cards)
|
|
23
|
+
*
|
|
24
|
+
* All components are pure display — no state or API calls.
|
|
25
|
+
* State and event handling are managed by SupportAgentPanel (the parent organism).
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Types
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
/** Context injected from the current backoffice route. */
|
|
33
|
+
export interface SupportAgentContext {
|
|
34
|
+
/** Page label from the current route, e.g. "Loan Applications". */
|
|
35
|
+
pageLabel: string;
|
|
36
|
+
/** Entity currently being viewed, e.g. "John Smith". Optional. */
|
|
37
|
+
entityLabel?: string;
|
|
38
|
+
/** Entity type for semantic use, e.g. "client" | "application" | "contact". */
|
|
39
|
+
entityType?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A single step inside a SupportStepGuideCard. */
|
|
43
|
+
export interface SupportAgentStep {
|
|
44
|
+
id: string;
|
|
45
|
+
/** Human-readable step instruction. */
|
|
46
|
+
label: string;
|
|
47
|
+
/**
|
|
48
|
+
* If set, the "Show me" button fires onHighlight(elementId).
|
|
49
|
+
* The consuming page listens for this and highlights the DOM element
|
|
50
|
+
* that has data-support-id matching this value.
|
|
51
|
+
*/
|
|
52
|
+
elementId?: string;
|
|
53
|
+
/** Whether the user has marked this step as done. */
|
|
54
|
+
completed?: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Rich content embedded inside an assistant message bubble. */
|
|
58
|
+
export type SupportAgentRichContent =
|
|
59
|
+
| {
|
|
60
|
+
type: "step-guide";
|
|
61
|
+
title: string;
|
|
62
|
+
steps: SupportAgentStep[];
|
|
63
|
+
}
|
|
64
|
+
| {
|
|
65
|
+
type: "article";
|
|
66
|
+
title: string;
|
|
67
|
+
excerpt: string;
|
|
68
|
+
href: string;
|
|
69
|
+
/** If true, opens in a new tab and shows an external link icon. */
|
|
70
|
+
isExternal?: boolean;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// SupportContextChip
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
export interface SupportContextChipProps {
|
|
78
|
+
context?: SupportAgentContext;
|
|
79
|
+
/** Show a skeleton loading placeholder. */
|
|
80
|
+
isLoading?: boolean;
|
|
81
|
+
className?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Pill that shows the current backoffice page and optional entity.
|
|
86
|
+
* Appears below the panel header to ground the AI conversation in context.
|
|
87
|
+
*
|
|
88
|
+
* Variants:
|
|
89
|
+
* - page-only → "📍 Loan Applications"
|
|
90
|
+
* - page+entity → "📍 Loan Applications › John Smith"
|
|
91
|
+
* - loading → skeleton placeholder
|
|
92
|
+
*/
|
|
93
|
+
export function SupportContextChip({
|
|
94
|
+
context,
|
|
95
|
+
isLoading = false,
|
|
96
|
+
className,
|
|
97
|
+
}: SupportContextChipProps) {
|
|
98
|
+
if (isLoading) {
|
|
99
|
+
return (
|
|
100
|
+
<Skeleton
|
|
101
|
+
className={cn("h-6 w-48", className)}
|
|
102
|
+
data-slot="support-context-chip"
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!context) return null;
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<div
|
|
111
|
+
className={cn(
|
|
112
|
+
"flex w-fit items-center gap-1.5 bg-muted px-2.5 py-1 text-xs text-muted-foreground",
|
|
113
|
+
className
|
|
114
|
+
)}
|
|
115
|
+
data-slot="support-context-chip"
|
|
116
|
+
>
|
|
117
|
+
<MapPin className="size-3 shrink-0" aria-hidden="true" />
|
|
118
|
+
<span className="font-medium text-foreground">{context.pageLabel}</span>
|
|
119
|
+
{context.entityLabel && (
|
|
120
|
+
<>
|
|
121
|
+
<span aria-hidden="true">›</span>
|
|
122
|
+
<span className="truncate max-w-[160px]">{context.entityLabel}</span>
|
|
123
|
+
</>
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// SupportSuggestedQuestion
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
export interface SupportSuggestedQuestionProps {
|
|
134
|
+
question: string;
|
|
135
|
+
onSelect: (question: string) => void;
|
|
136
|
+
className?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Single clickable question card — the Jira Rovo "conversation starter" pattern.
|
|
141
|
+
* Clicking pre-fills the chat input with the question text.
|
|
142
|
+
*/
|
|
143
|
+
export function SupportSuggestedQuestion({
|
|
144
|
+
question,
|
|
145
|
+
onSelect,
|
|
146
|
+
className,
|
|
147
|
+
}: SupportSuggestedQuestionProps) {
|
|
148
|
+
return (
|
|
149
|
+
<Button
|
|
150
|
+
type="button"
|
|
151
|
+
variant="ghost"
|
|
152
|
+
onClick={() => onSelect(question)}
|
|
153
|
+
className={cn(
|
|
154
|
+
"h-auto w-full justify-start border border-border bg-background px-3 py-2.5 text-left text-sm font-normal text-foreground hover:bg-muted/50",
|
|
155
|
+
className
|
|
156
|
+
)}
|
|
157
|
+
data-slot="support-suggested-question"
|
|
158
|
+
>
|
|
159
|
+
{question}
|
|
160
|
+
</Button>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// SupportStepGuideCard
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
export interface SupportStepGuideCardProps {
|
|
169
|
+
title: string;
|
|
170
|
+
steps: SupportAgentStep[];
|
|
171
|
+
/** Called when the user clicks "Show me" on a step that has an elementId. */
|
|
172
|
+
onHighlight?: (elementId: string) => void;
|
|
173
|
+
/** Called when the user marks a step as complete. */
|
|
174
|
+
onStepComplete?: (stepId: string) => void;
|
|
175
|
+
/** Default collapsed if more than 5 steps; otherwise expanded. */
|
|
176
|
+
defaultExpanded?: boolean;
|
|
177
|
+
className?: string;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* A numbered walkthrough card embedded inside an AI assistant message bubble.
|
|
182
|
+
* Inspired by Jira Rovo message cards — bordered container with structured content.
|
|
183
|
+
*
|
|
184
|
+
* - Completed steps show a checkmark icon instead of a number.
|
|
185
|
+
* - Steps with an `elementId` show a "→ Show me" button that fires onHighlight.
|
|
186
|
+
* - Collapsible when more than 5 steps (default collapsed).
|
|
187
|
+
*/
|
|
188
|
+
export function SupportStepGuideCard({
|
|
189
|
+
title,
|
|
190
|
+
steps,
|
|
191
|
+
onHighlight,
|
|
192
|
+
onStepComplete,
|
|
193
|
+
defaultExpanded,
|
|
194
|
+
className,
|
|
195
|
+
}: SupportStepGuideCardProps) {
|
|
196
|
+
const isLong = steps.length > 5;
|
|
197
|
+
const [expanded, setExpanded] = React.useState(defaultExpanded ?? !isLong);
|
|
198
|
+
|
|
199
|
+
const completedCount = steps.filter((s) => s.completed).length;
|
|
200
|
+
const allDone = completedCount === steps.length;
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<div
|
|
204
|
+
className={cn("mt-2 border border-border bg-background", className)}
|
|
205
|
+
data-slot="support-step-guide-card"
|
|
206
|
+
>
|
|
207
|
+
{/* Card header */}
|
|
208
|
+
<button
|
|
209
|
+
type="button"
|
|
210
|
+
onClick={() => setExpanded((v) => !v)}
|
|
211
|
+
className="flex w-full items-center justify-between gap-2 px-3 py-2.5 text-left hover:bg-muted/30"
|
|
212
|
+
aria-expanded={expanded}
|
|
213
|
+
>
|
|
214
|
+
<div className="flex flex-col gap-0.5">
|
|
215
|
+
<span className="text-sm font-semibold text-foreground">{title}</span>
|
|
216
|
+
<span className="text-xs text-muted-foreground">
|
|
217
|
+
{allDone
|
|
218
|
+
? "All steps complete"
|
|
219
|
+
: `${completedCount} of ${steps.length} complete`}
|
|
220
|
+
</span>
|
|
221
|
+
</div>
|
|
222
|
+
{expanded ? (
|
|
223
|
+
<ChevronUp
|
|
224
|
+
className="size-4 shrink-0 text-muted-foreground"
|
|
225
|
+
aria-hidden="true"
|
|
226
|
+
/>
|
|
227
|
+
) : (
|
|
228
|
+
<ChevronDown
|
|
229
|
+
className="size-4 shrink-0 text-muted-foreground"
|
|
230
|
+
aria-hidden="true"
|
|
231
|
+
/>
|
|
232
|
+
)}
|
|
233
|
+
</button>
|
|
234
|
+
|
|
235
|
+
{/* Steps list */}
|
|
236
|
+
{expanded && (
|
|
237
|
+
<ol className="flex flex-col divide-y divide-border border-t border-border">
|
|
238
|
+
{steps.map((step, index) => (
|
|
239
|
+
<li
|
|
240
|
+
key={step.id}
|
|
241
|
+
className={cn(
|
|
242
|
+
"flex items-start gap-3 px-3 py-2.5",
|
|
243
|
+
step.completed && "opacity-60"
|
|
244
|
+
)}
|
|
245
|
+
>
|
|
246
|
+
{/* Step number or checkmark */}
|
|
247
|
+
<button
|
|
248
|
+
type="button"
|
|
249
|
+
onClick={() => !step.completed && onStepComplete?.(step.id)}
|
|
250
|
+
disabled={step.completed}
|
|
251
|
+
title={step.completed ? "Step complete" : "Mark as done"}
|
|
252
|
+
className={cn(
|
|
253
|
+
"mt-0.5 flex size-5 shrink-0 items-center justify-center border text-xs font-semibold",
|
|
254
|
+
step.completed
|
|
255
|
+
? "border-primary bg-primary text-primary-foreground"
|
|
256
|
+
: "border-border text-muted-foreground hover:bg-muted"
|
|
257
|
+
)}
|
|
258
|
+
aria-label={
|
|
259
|
+
step.completed
|
|
260
|
+
? `Step ${index + 1} complete`
|
|
261
|
+
: `Mark step ${index + 1} as done`
|
|
262
|
+
}
|
|
263
|
+
>
|
|
264
|
+
{step.completed ? (
|
|
265
|
+
<Check className="size-3" aria-hidden="true" />
|
|
266
|
+
) : (
|
|
267
|
+
index + 1
|
|
268
|
+
)}
|
|
269
|
+
</button>
|
|
270
|
+
|
|
271
|
+
{/* Step label + show me button */}
|
|
272
|
+
<div className="flex flex-1 flex-col gap-1">
|
|
273
|
+
<span className="text-sm text-foreground leading-snug">
|
|
274
|
+
{step.label}
|
|
275
|
+
</span>
|
|
276
|
+
{step.elementId && onHighlight && !step.completed && (
|
|
277
|
+
<Button
|
|
278
|
+
type="button"
|
|
279
|
+
variant="ghost"
|
|
280
|
+
size="sm"
|
|
281
|
+
onClick={() => onHighlight(step.elementId!)}
|
|
282
|
+
className="h-auto w-fit gap-1 px-0 py-0 text-xs text-primary hover:bg-transparent hover:underline"
|
|
283
|
+
>
|
|
284
|
+
<MousePointerClick className="size-3" aria-hidden="true" />
|
|
285
|
+
Show me
|
|
286
|
+
</Button>
|
|
287
|
+
)}
|
|
288
|
+
</div>
|
|
289
|
+
</li>
|
|
290
|
+
))}
|
|
291
|
+
</ol>
|
|
292
|
+
)}
|
|
293
|
+
</div>
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// ---------------------------------------------------------------------------
|
|
298
|
+
// SupportArticleCard
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
|
|
301
|
+
export interface SupportArticleCardProps {
|
|
302
|
+
title: string;
|
|
303
|
+
excerpt: string;
|
|
304
|
+
href: string;
|
|
305
|
+
/** If true, opens in a new tab with an external link icon. */
|
|
306
|
+
isExternal?: boolean;
|
|
307
|
+
className?: string;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* A compact help article reference card embedded inside an AI message bubble.
|
|
312
|
+
* Inspired by Jira Rovo's linked content cards.
|
|
313
|
+
*
|
|
314
|
+
* - Internal links: navigates in the same tab (relative URL).
|
|
315
|
+
* - External links: opens new tab + shows ExternalLink icon.
|
|
316
|
+
*/
|
|
317
|
+
export function SupportArticleCard({
|
|
318
|
+
title,
|
|
319
|
+
excerpt,
|
|
320
|
+
href,
|
|
321
|
+
isExternal = false,
|
|
322
|
+
className,
|
|
323
|
+
}: SupportArticleCardProps) {
|
|
324
|
+
return (
|
|
325
|
+
<a
|
|
326
|
+
href={href}
|
|
327
|
+
target={isExternal ? "_blank" : undefined}
|
|
328
|
+
rel={isExternal ? "noopener noreferrer" : undefined}
|
|
329
|
+
className={cn(
|
|
330
|
+
"mt-2 flex flex-col gap-1.5 border border-border bg-background px-3 py-2.5 text-left no-underline hover:bg-muted/30 text-foreground",
|
|
331
|
+
className
|
|
332
|
+
)}
|
|
333
|
+
data-slot="support-article-card"
|
|
334
|
+
>
|
|
335
|
+
<div className="flex items-center justify-between gap-2">
|
|
336
|
+
<span className="min-w-0 flex-1 truncate text-sm font-semibold text-foreground">
|
|
337
|
+
{title}
|
|
338
|
+
</span>
|
|
339
|
+
{isExternal && (
|
|
340
|
+
<ExternalLink
|
|
341
|
+
className="size-3.5 shrink-0 text-muted-foreground"
|
|
342
|
+
aria-hidden="true"
|
|
343
|
+
/>
|
|
344
|
+
)}
|
|
345
|
+
</div>
|
|
346
|
+
<p className="text-xs text-muted-foreground line-clamp-2 leading-relaxed">
|
|
347
|
+
{excerpt}
|
|
348
|
+
</p>
|
|
349
|
+
<span className="text-xs font-medium text-primary">
|
|
350
|
+
{isExternal ? "Open article →" : "Read more →"}
|
|
351
|
+
</span>
|
|
352
|
+
</a>
|
|
353
|
+
);
|
|
354
|
+
}
|
package/src/styles/globals.css
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
@source inline("animate-in animate-out animate-pulse animate-caret-blink fade-in-0 fade-out-0 zoom-in-95 data-open:slide-in-from-bottom data-open:slide-in-from-left data-open:slide-in-from-right data-open:slide-in-from-top data-ending-style:slide-out-to-bottom data-ending-style:slide-out-to-left data-ending-style:slide-out-to-right data-ending-style:slide-out-to-top data-open:duration-500 data-ending-style:duration-300 data-open:bg-accent data-open:text-accent-foreground");
|
|
33
33
|
|
|
34
34
|
@source inline("focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 dark:bg-input/30 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground data-indeterminate:border-primary data-indeterminate:bg-primary data-indeterminate:text-primary-foreground dark:data-checked:bg-primary aria-invalid:data-checked:border-destructive aria-invalid:data-checked:bg-destructive aria-invalid:data-checked:text-destructive-foreground aria-invalid:data-indeterminate:border-destructive aria-invalid:data-indeterminate:bg-destructive aria-invalid:data-indeterminate:text-destructive-foreground group-data-indeterminate:hidden group-data-indeterminate:block bg-primary/5 bg-destructive/5 border-primary border-destructive peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-destructive text-muted-foreground disabled:pointer-events-none data-open:animate-in data-ending-style:animate-out data-open:fade-in-0 data-ending-style:fade-out-0 data-open:zoom-in-95 data-ending-style:zoom-out-95 animate-spin data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-unchecked:bg-input dark:data-unchecked:bg-input/80 group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-checked:translate-x-[calc(100%-2px)] data-unchecked:translate-x-0 dark:data-checked:bg-primary-foreground dark:data-unchecked:bg-foreground group/switch");
|
|
35
|
+
@source inline("bg-success/20 bg-warning/20 bg-destructive/20 bg-success bg-warning");
|
|
35
36
|
|
|
36
37
|
@custom-variant dark (&:is(.dark *));
|
|
37
38
|
|