@kite-copilot/chat-panel 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs DELETED
@@ -1,2692 +0,0 @@
1
- import * as React4 from 'react';
2
- import { clsx } from 'clsx';
3
- import { twMerge } from 'tailwind-merge';
4
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
- import { Slot } from '@radix-ui/react-slot';
6
- import { cva } from 'class-variance-authority';
7
- import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
8
- import { Loader2, Search, CheckCircle2, ArrowLeft, ChevronUp, ChevronDown, SquarePen, Command, CornerDownLeft, FileSpreadsheet, X, Paperclip, ArrowUp, Shield, MapPin, Key, Crown, TrendingUp, DollarSign, XCircle, RefreshCw, Clock, Users, Mail, Calendar, CreditCard, Smartphone, Laptop, Activity, Zap, AlertCircle } from 'lucide-react';
9
- import { AnimatePresence, motion } from 'framer-motion';
10
-
11
- function cn(...inputs) {
12
- return twMerge(clsx(inputs));
13
- }
14
- function Input({ className, type, ...props }) {
15
- return /* @__PURE__ */ jsx(
16
- "input",
17
- {
18
- type,
19
- "data-slot": "input",
20
- className: cn(
21
- "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
22
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
23
- "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
24
- className
25
- ),
26
- ...props
27
- }
28
- );
29
- }
30
- var buttonVariants = cva(
31
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
32
- {
33
- variants: {
34
- variant: {
35
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
36
- destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
37
- outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
38
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
39
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
40
- link: "text-primary underline-offset-4 hover:underline"
41
- },
42
- size: {
43
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
44
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
45
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
46
- icon: "size-9",
47
- "icon-sm": "size-8",
48
- "icon-lg": "size-10"
49
- }
50
- },
51
- defaultVariants: {
52
- variant: "default",
53
- size: "default"
54
- }
55
- }
56
- );
57
- function Button({
58
- className,
59
- variant,
60
- size,
61
- asChild = false,
62
- ...props
63
- }) {
64
- const Comp = asChild ? Slot : "button";
65
- return /* @__PURE__ */ jsx(
66
- Comp,
67
- {
68
- "data-slot": "button",
69
- className: cn(buttonVariants({ variant, size, className })),
70
- ...props
71
- }
72
- );
73
- }
74
- var ScrollArea = React4.forwardRef(({ className, children, ...props }, ref) => {
75
- return /* @__PURE__ */ jsxs(
76
- ScrollAreaPrimitive.Root,
77
- {
78
- "data-slot": "scroll-area",
79
- className: cn("relative", className),
80
- ...props,
81
- children: [
82
- /* @__PURE__ */ jsx(
83
- ScrollAreaPrimitive.Viewport,
84
- {
85
- ref,
86
- "data-slot": "scroll-area-viewport",
87
- className: "focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1",
88
- children
89
- }
90
- ),
91
- /* @__PURE__ */ jsx(ScrollBar, {}),
92
- /* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
93
- ]
94
- }
95
- );
96
- });
97
- ScrollArea.displayName = "ScrollArea";
98
- function ScrollBar({
99
- className,
100
- orientation = "vertical",
101
- ...props
102
- }) {
103
- return /* @__PURE__ */ jsx(
104
- ScrollAreaPrimitive.ScrollAreaScrollbar,
105
- {
106
- "data-slot": "scroll-area-scrollbar",
107
- orientation,
108
- className: cn(
109
- "flex touch-none p-px transition-colors select-none",
110
- orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
111
- orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
112
- className
113
- ),
114
- ...props,
115
- children: /* @__PURE__ */ jsx(
116
- ScrollAreaPrimitive.ScrollAreaThumb,
117
- {
118
- "data-slot": "scroll-area-thumb",
119
- className: "bg-border relative flex-1 rounded-full"
120
- }
121
- )
122
- }
123
- );
124
- }
125
- function AssistantActivity({
126
- phase,
127
- progressSteps = []
128
- }) {
129
- return /* @__PURE__ */ jsxs("div", { className: "mb-3 rounded-lg bg-muted/40 px-3 py-3 text-xs text-muted-foreground transition-colors", children: [
130
- phase === "thinking" && progressSteps.length === 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
131
- /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
132
- /* @__PURE__ */ jsx("span", { children: "Thinking\u2026" })
133
- ] }),
134
- phase === "searching" && /* @__PURE__ */ jsxs("div", { className: "space-y-2 animate-in fade-in-0 duration-300", children: [
135
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
136
- /* @__PURE__ */ jsx(Search, { className: "h-3.5 w-3.5 opacity-70" }),
137
- /* @__PURE__ */ jsx("span", { children: "Searching across pages\u2026" })
138
- ] }),
139
- /* @__PURE__ */ jsxs("ul", { className: "ml-6 list-disc space-y-1", children: [
140
- /* @__PURE__ */ jsx("li", { className: "opacity-90", children: "Checking recent changes" }),
141
- /* @__PURE__ */ jsx("li", { className: "opacity-90", children: "Scanning documentation" }),
142
- /* @__PURE__ */ jsx("li", { className: "opacity-90", children: "Summarizing relevant sections" })
143
- ] })
144
- ] }),
145
- (phase === "thinking" || phase === "executing" || phase === "responding") && progressSteps.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-2 animate-in fade-in-0 duration-200", children: [
146
- progressSteps.map((step, index) => /* @__PURE__ */ jsxs(
147
- "div",
148
- {
149
- className: `flex items-center gap-2 transition-opacity duration-200 ${step.completed ? "opacity-60" : "opacity-100"}`,
150
- children: [
151
- step.completed ? /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-green-500 flex-shrink-0" }) : /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin flex-shrink-0" }),
152
- /* @__PURE__ */ jsx("span", { className: step.completed ? "line-through" : "", children: step.message })
153
- ]
154
- },
155
- index
156
- )),
157
- phase === "responding" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 opacity-100 animate-in fade-in-0 duration-200", children: [
158
- /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin flex-shrink-0" }),
159
- /* @__PURE__ */ jsx("span", { children: "Preparing response..." })
160
- ] })
161
- ] })
162
- ] });
163
- }
164
- function GuideCursor({ x, y, visible, onClick = false }) {
165
- return /* @__PURE__ */ jsx(AnimatePresence, { children: visible && /* @__PURE__ */ jsx(
166
- motion.div,
167
- {
168
- className: "fixed pointer-events-none z-[9999]",
169
- initial: { opacity: 0, scale: 0.5 },
170
- animate: {
171
- opacity: 1,
172
- scale: 1,
173
- x,
174
- y
175
- },
176
- exit: { opacity: 0, scale: 0.5 },
177
- transition: {
178
- type: "spring",
179
- stiffness: 200,
180
- damping: 20
181
- },
182
- style: {
183
- left: 0,
184
- top: 0
185
- },
186
- children: /* @__PURE__ */ jsx(
187
- "svg",
188
- {
189
- width: "32",
190
- height: "32",
191
- viewBox: "0 0 24 24",
192
- fill: "none",
193
- xmlns: "http://www.w3.org/2000/svg",
194
- children: /* @__PURE__ */ jsx(
195
- "path",
196
- {
197
- d: "M3 3L10.07 19.97L12.58 12.58L19.97 10.07L3 3Z",
198
- fill: "#C4B5FD"
199
- }
200
- )
201
- }
202
- )
203
- }
204
- ) });
205
- }
206
- function useGuideCursor() {
207
- const [cursorState, setCursorState] = React4.useState({
208
- x: 0,
209
- y: 0,
210
- visible: false,
211
- onClick: false
212
- });
213
- const moveTo = React4.useCallback((target) => {
214
- const element = document.querySelector(target.selector);
215
- if (element) {
216
- const rect = element.getBoundingClientRect();
217
- const offsetX = target.offset?.x || 0;
218
- const offsetY = target.offset?.y || 0;
219
- setCursorState({
220
- x: rect.left + rect.width / 2 + offsetX,
221
- y: rect.top + rect.height / 2 + offsetY,
222
- visible: true,
223
- onClick: target.onClick || false
224
- });
225
- }
226
- }, []);
227
- const hide = React4.useCallback(() => {
228
- setCursorState((prev) => ({ ...prev, visible: false }));
229
- }, []);
230
- const show = React4.useCallback(() => {
231
- setCursorState((prev) => ({ ...prev, visible: true }));
232
- }, []);
233
- return {
234
- cursorState,
235
- moveTo,
236
- hide,
237
- show
238
- };
239
- }
240
- function Card({ className, ...props }) {
241
- return /* @__PURE__ */ jsx(
242
- "div",
243
- {
244
- "data-slot": "card",
245
- className: cn(
246
- "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
247
- className
248
- ),
249
- ...props
250
- }
251
- );
252
- }
253
- function CardHeader({ className, ...props }) {
254
- return /* @__PURE__ */ jsx(
255
- "div",
256
- {
257
- "data-slot": "card-header",
258
- className: cn(
259
- "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
260
- className
261
- ),
262
- ...props
263
- }
264
- );
265
- }
266
- function CardTitle({ className, ...props }) {
267
- return /* @__PURE__ */ jsx(
268
- "div",
269
- {
270
- "data-slot": "card-title",
271
- className: cn("leading-none font-semibold", className),
272
- ...props
273
- }
274
- );
275
- }
276
- function CardDescription({ className, ...props }) {
277
- return /* @__PURE__ */ jsx(
278
- "div",
279
- {
280
- "data-slot": "card-description",
281
- className: cn("text-muted-foreground text-sm", className),
282
- ...props
283
- }
284
- );
285
- }
286
- function CardAction({ className, ...props }) {
287
- return /* @__PURE__ */ jsx(
288
- "div",
289
- {
290
- "data-slot": "card-action",
291
- className: cn(
292
- "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
293
- className
294
- ),
295
- ...props
296
- }
297
- );
298
- }
299
- function CardContent({ className, ...props }) {
300
- return /* @__PURE__ */ jsx(
301
- "div",
302
- {
303
- "data-slot": "card-content",
304
- className: cn("px-6", className),
305
- ...props
306
- }
307
- );
308
- }
309
- function CardFooter({ className, ...props }) {
310
- return /* @__PURE__ */ jsx(
311
- "div",
312
- {
313
- "data-slot": "card-footer",
314
- className: cn("flex items-center px-6 [.border-t]:pt-6", className),
315
- ...props
316
- }
317
- );
318
- }
319
- var formatDate = (dateString) => {
320
- const date = new Date(dateString);
321
- return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
322
- };
323
- var formatCurrency = (amount, currency = "USD") => {
324
- return new Intl.NumberFormat("en-US", {
325
- style: "currency",
326
- currency
327
- }).format(amount);
328
- };
329
- var getPlanIcon = (plan) => {
330
- switch (plan) {
331
- case "Enterprise":
332
- return Crown;
333
- case "Professional":
334
- return Zap;
335
- default:
336
- return Activity;
337
- }
338
- };
339
- var getPlanColor = (plan) => {
340
- switch (plan) {
341
- case "Enterprise":
342
- return "bg-purple-50 text-purple-700 border-purple-200";
343
- case "Professional":
344
- return "bg-blue-50 text-blue-700 border-blue-200";
345
- default:
346
- return "bg-gray-50 text-gray-700 border-gray-200";
347
- }
348
- };
349
- function StatusBadge({ status, type = "default" }) {
350
- const getConfig = () => {
351
- const statusLower = status.toLowerCase();
352
- if (type === "transaction") {
353
- switch (statusLower) {
354
- case "paid":
355
- return { color: "bg-emerald-50 text-emerald-700 border-emerald-200", icon: CheckCircle2 };
356
- case "failed":
357
- return { color: "bg-red-50 text-red-700 border-red-200", icon: XCircle };
358
- case "pending":
359
- return { color: "bg-amber-50 text-amber-700 border-amber-200", icon: Clock };
360
- case "refunded":
361
- return { color: "bg-slate-50 text-slate-700 border-slate-200", icon: RefreshCw };
362
- default:
363
- return { color: "bg-gray-50 text-gray-700 border-gray-200", icon: AlertCircle };
364
- }
365
- }
366
- if (type === "subscription") {
367
- switch (statusLower) {
368
- case "active":
369
- return { color: "bg-emerald-50 text-emerald-700 border-emerald-200", icon: CheckCircle2 };
370
- case "cancelled":
371
- return { color: "bg-slate-50 text-slate-700 border-slate-200", icon: XCircle };
372
- case "past_due":
373
- return { color: "bg-red-50 text-red-700 border-red-200", icon: AlertCircle };
374
- case "trialing":
375
- return { color: "bg-blue-50 text-blue-700 border-blue-200", icon: Clock };
376
- default:
377
- return { color: "bg-gray-50 text-gray-700 border-gray-200", icon: AlertCircle };
378
- }
379
- }
380
- switch (statusLower) {
381
- case "active":
382
- return { color: "bg-emerald-50 text-emerald-700 border-emerald-200", icon: CheckCircle2 };
383
- case "inactive":
384
- return { color: "bg-slate-50 text-slate-700 border-slate-200", icon: XCircle };
385
- default:
386
- return { color: "bg-gray-50 text-gray-700 border-gray-200", icon: AlertCircle };
387
- }
388
- };
389
- const config = getConfig();
390
- const Icon = config.icon;
391
- return /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium border ${config.color}`, children: [
392
- /* @__PURE__ */ jsx(Icon, { className: "h-3 w-3" }),
393
- status
394
- ] });
395
- }
396
- function SubscriptionCard({ subscription }) {
397
- const PlanIcon = getPlanIcon(subscription.plan);
398
- return /* @__PURE__ */ jsxs(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
399
- /* @__PURE__ */ jsx("div", { className: `h-1 ${subscription.status === "active" ? "bg-emerald-500" : subscription.status === "past_due" ? "bg-red-500" : "bg-slate-300"}` }),
400
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
401
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
402
- /* @__PURE__ */ jsxs(CardTitle, { className: "text-base font-semibold text-gray-900 flex items-center gap-2", children: [
403
- /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-gray-500" }),
404
- subscription.id
405
- ] }),
406
- subscription.customer_email && /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-gray-500 flex items-center gap-1.5", children: [
407
- /* @__PURE__ */ jsx(Mail, { className: "h-3 w-3" }),
408
- subscription.customer_email
409
- ] })
410
- ] }),
411
- /* @__PURE__ */ jsx(StatusBadge, { status: subscription.status, type: "subscription" })
412
- ] }) }),
413
- /* @__PURE__ */ jsx(CardContent, { className: "pt-0", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
414
- /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
415
- /* @__PURE__ */ jsxs("div", { children: [
416
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 mb-1", children: "Plan" }),
417
- /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium border ${getPlanColor(subscription.plan)}`, children: [
418
- /* @__PURE__ */ jsx(PlanIcon, { className: "h-3 w-3" }),
419
- subscription.plan
420
- ] })
421
- ] }),
422
- /* @__PURE__ */ jsxs("div", { children: [
423
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 mb-1", children: "Billing" }),
424
- /* @__PURE__ */ jsxs("div", { className: "text-sm font-medium text-gray-900", children: [
425
- formatCurrency(subscription.amount, subscription.currency),
426
- " / ",
427
- subscription.billing_cycle
428
- ] })
429
- ] })
430
- ] }),
431
- /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
432
- /* @__PURE__ */ jsxs("div", { children: [
433
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 mb-1", children: "Current Period" }),
434
- /* @__PURE__ */ jsxs("div", { className: "text-sm text-gray-700", children: [
435
- formatDate(subscription.current_period_start),
436
- " - ",
437
- formatDate(subscription.current_period_end)
438
- ] })
439
- ] }),
440
- /* @__PURE__ */ jsxs("div", { children: [
441
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 mb-1", children: "Created" }),
442
- /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-700", children: formatDate(subscription.created_at) })
443
- ] })
444
- ] })
445
- ] }) })
446
- ] });
447
- }
448
- function SubscriptionList({ subscriptions, maxItems = 5 }) {
449
- const displaySubs = subscriptions.slice(0, maxItems);
450
- const hasMore = subscriptions.length > maxItems;
451
- if (subscriptions.length === 0) {
452
- return /* @__PURE__ */ jsx(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ jsxs(CardContent, { className: "py-8 text-center", children: [
453
- /* @__PURE__ */ jsx(CreditCard, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
454
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "No subscriptions found" })
455
- ] }) });
456
- }
457
- return /* @__PURE__ */ jsxs(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
458
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
459
- /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4" }),
460
- subscriptions.length,
461
- " Subscription",
462
- subscriptions.length !== 1 ? "s" : ""
463
- ] }) }),
464
- /* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
465
- /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: displaySubs.map((sub) => {
466
- const PlanIcon = getPlanIcon(sub.plan);
467
- return /* @__PURE__ */ jsx("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
468
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
469
- /* @__PURE__ */ jsx("div", { className: `p-2 rounded-lg ${getPlanColor(sub.plan).replace("text-", "text-").replace("border-", "bg-").split(" ")[0]}`, children: /* @__PURE__ */ jsx(PlanIcon, { className: "h-4 w-4" }) }),
470
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
471
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-900 truncate", children: sub.customer_email || sub.customer_id }),
472
- /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500", children: [
473
- sub.plan,
474
- " \xB7 ",
475
- formatCurrency(sub.amount, sub.currency),
476
- "/",
477
- sub.billing_cycle
478
- ] })
479
- ] })
480
- ] }),
481
- /* @__PURE__ */ jsx(StatusBadge, { status: sub.status, type: "subscription" })
482
- ] }) }, sub.id);
483
- }) }),
484
- hasMore && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 bg-gray-50 border-t border-gray-100 text-center", children: /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500", children: [
485
- "+",
486
- subscriptions.length - maxItems,
487
- " more subscription",
488
- subscriptions.length - maxItems !== 1 ? "s" : ""
489
- ] }) })
490
- ] })
491
- ] });
492
- }
493
- function CustomerCard({ customer }) {
494
- const PlanIcon = getPlanIcon(customer.subscription);
495
- return /* @__PURE__ */ jsxs(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
496
- /* @__PURE__ */ jsx("div", { className: `h-1 ${customer.status === "active" ? "bg-emerald-500" : "bg-slate-300"}` }),
497
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
498
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsxs("div", { children: [
499
- /* @__PURE__ */ jsx(CardTitle, { className: "text-base font-semibold text-gray-900", children: customer.name }),
500
- /* @__PURE__ */ jsxs(CardDescription, { className: "text-sm text-gray-500 flex items-center gap-1", children: [
501
- /* @__PURE__ */ jsx(Mail, { className: "h-3 w-3" }),
502
- customer.email
503
- ] })
504
- ] }) }),
505
- /* @__PURE__ */ jsx(StatusBadge, { status: customer.status, type: "customer" })
506
- ] }) }),
507
- /* @__PURE__ */ jsx(CardContent, { className: "pt-0", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
508
- /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
509
- /* @__PURE__ */ jsxs("div", { children: [
510
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 mb-1", children: "Plan" }),
511
- /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md text-xs font-medium border ${getPlanColor(customer.subscription)}`, children: [
512
- /* @__PURE__ */ jsx(PlanIcon, { className: "h-3 w-3" }),
513
- customer.subscription
514
- ] })
515
- ] }),
516
- /* @__PURE__ */ jsxs("div", { children: [
517
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 mb-1", children: "Revenue" }),
518
- /* @__PURE__ */ jsxs("div", { className: "text-sm font-medium text-gray-900 flex items-center gap-1", children: [
519
- /* @__PURE__ */ jsx(DollarSign, { className: "h-3 w-3 text-emerald-500" }),
520
- customer.revenue
521
- ] })
522
- ] })
523
- ] }),
524
- /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
525
- /* @__PURE__ */ jsxs("div", { children: [
526
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 mb-1", children: "Location" }),
527
- /* @__PURE__ */ jsxs("div", { className: "text-sm text-gray-700 flex items-center gap-1", children: [
528
- /* @__PURE__ */ jsx(MapPin, { className: "h-3 w-3 text-gray-400" }),
529
- customer.location || "Not specified"
530
- ] })
531
- ] }),
532
- /* @__PURE__ */ jsxs("div", { children: [
533
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 mb-1", children: "Customer Since" }),
534
- /* @__PURE__ */ jsxs("div", { className: "text-sm text-gray-700 flex items-center gap-1", children: [
535
- /* @__PURE__ */ jsx(Calendar, { className: "h-3 w-3 text-gray-400" }),
536
- customer.joinDate
537
- ] })
538
- ] })
539
- ] })
540
- ] }) })
541
- ] });
542
- }
543
- function CustomerList({ customers, maxItems = 5 }) {
544
- const displayCustomers = customers.slice(0, maxItems);
545
- const hasMore = customers.length > maxItems;
546
- if (customers.length === 0) {
547
- return /* @__PURE__ */ jsx(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ jsxs(CardContent, { className: "py-8 text-center", children: [
548
- /* @__PURE__ */ jsx(Users, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
549
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "No customers found" })
550
- ] }) });
551
- }
552
- return /* @__PURE__ */ jsxs(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
553
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
554
- /* @__PURE__ */ jsx(Users, { className: "h-4 w-4" }),
555
- customers.length,
556
- " Customer",
557
- customers.length !== 1 ? "s" : ""
558
- ] }) }),
559
- /* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
560
- /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: displayCustomers.map((customer) => /* @__PURE__ */ jsx("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
561
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
562
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-900 truncate", children: customer.name }),
563
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 truncate", children: customer.email })
564
- ] }) }),
565
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
566
- /* @__PURE__ */ jsx("span", { className: `px-2 py-0.5 rounded text-xs font-medium ${getPlanColor(customer.subscription)}`, children: customer.subscription }),
567
- /* @__PURE__ */ jsx(StatusBadge, { status: customer.status, type: "customer" })
568
- ] })
569
- ] }) }, customer.id)) }),
570
- hasMore && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 bg-gray-50 border-t border-gray-100 text-center", children: /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500", children: [
571
- "+",
572
- customers.length - maxItems,
573
- " more customer",
574
- customers.length - maxItems !== 1 ? "s" : ""
575
- ] }) })
576
- ] })
577
- ] });
578
- }
579
- function TransactionList({ transactions, maxItems = 5 }) {
580
- const displayTransactions = transactions.slice(0, maxItems);
581
- const hasMore = transactions.length > maxItems;
582
- if (transactions.length === 0) {
583
- return /* @__PURE__ */ jsx(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ jsxs(CardContent, { className: "py-8 text-center", children: [
584
- /* @__PURE__ */ jsx(DollarSign, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
585
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "No transactions found" })
586
- ] }) });
587
- }
588
- return /* @__PURE__ */ jsxs(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
589
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
590
- /* @__PURE__ */ jsx(DollarSign, { className: "h-4 w-4" }),
591
- transactions.length,
592
- " Transaction",
593
- transactions.length !== 1 ? "s" : ""
594
- ] }) }),
595
- /* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
596
- /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: displayTransactions.map((tx) => /* @__PURE__ */ jsx("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
597
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
598
- /* @__PURE__ */ jsx("div", { className: `p-2 rounded-lg ${tx.status === "paid" ? "bg-emerald-50" : tx.status === "failed" ? "bg-red-50" : tx.status === "refunded" ? "bg-slate-50" : "bg-amber-50"}`, children: tx.status === "paid" ? /* @__PURE__ */ jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }) : tx.status === "failed" ? /* @__PURE__ */ jsx(XCircle, { className: "h-4 w-4 text-red-600" }) : tx.status === "refunded" ? /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4 text-slate-600" }) : /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-amber-600" }) }),
599
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
600
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-900 truncate", children: tx.customer }),
601
- /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500", children: [
602
- tx.date,
603
- tx.decline_reason && /* @__PURE__ */ jsxs("span", { className: "text-red-500 ml-2", children: [
604
- "\xB7 ",
605
- tx.decline_reason
606
- ] })
607
- ] })
608
- ] })
609
- ] }),
610
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
611
- /* @__PURE__ */ jsxs("span", { className: `text-sm font-semibold ${tx.status === "paid" ? "text-emerald-600" : tx.status === "failed" ? "text-red-600" : tx.status === "refunded" ? "text-slate-600" : "text-amber-600"}`, children: [
612
- tx.status === "refunded" ? "-" : "",
613
- tx.amount
614
- ] }),
615
- /* @__PURE__ */ jsx(StatusBadge, { status: tx.status, type: "transaction" })
616
- ] })
617
- ] }) }, tx.id)) }),
618
- hasMore && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 bg-gray-50 border-t border-gray-100 text-center", children: /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500", children: [
619
- "+",
620
- transactions.length - maxItems,
621
- " more transaction",
622
- transactions.length - maxItems !== 1 ? "s" : ""
623
- ] }) })
624
- ] })
625
- ] });
626
- }
627
- function ApiKeyList({ apiKeys }) {
628
- if (apiKeys.length === 0) {
629
- return /* @__PURE__ */ jsx(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ jsxs(CardContent, { className: "py-8 text-center", children: [
630
- /* @__PURE__ */ jsx(Key, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
631
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "No API keys found" })
632
- ] }) });
633
- }
634
- return /* @__PURE__ */ jsxs(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
635
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
636
- /* @__PURE__ */ jsx(Key, { className: "h-4 w-4" }),
637
- apiKeys.length,
638
- " API Key",
639
- apiKeys.length !== 1 ? "s" : ""
640
- ] }) }),
641
- /* @__PURE__ */ jsx(CardContent, { className: "p-0", children: /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: apiKeys.map((key, idx) => /* @__PURE__ */ jsx("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
642
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
643
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-900", children: key.name }),
644
- /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500 font-mono", children: [
645
- key.key.slice(0, 12),
646
- "...",
647
- key.key.slice(-4)
648
- ] })
649
- ] }),
650
- /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500", children: [
651
- "Last used: ",
652
- key.lastUsed
653
- ] })
654
- ] }) }, idx)) }) })
655
- ] });
656
- }
657
- function SessionList({ sessions }) {
658
- const getDeviceIcon = (device) => {
659
- const deviceLower = device.toLowerCase();
660
- if (deviceLower.includes("mobile") || deviceLower.includes("iphone") || deviceLower.includes("android")) {
661
- return Smartphone;
662
- }
663
- return Laptop;
664
- };
665
- if (sessions.length === 0) {
666
- return /* @__PURE__ */ jsx(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ jsxs(CardContent, { className: "py-8 text-center", children: [
667
- /* @__PURE__ */ jsx(Shield, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
668
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "No active sessions" })
669
- ] }) });
670
- }
671
- return /* @__PURE__ */ jsxs(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
672
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
673
- /* @__PURE__ */ jsx(Shield, { className: "h-4 w-4" }),
674
- sessions.length,
675
- " Active Session",
676
- sessions.length !== 1 ? "s" : ""
677
- ] }) }),
678
- /* @__PURE__ */ jsx(CardContent, { className: "p-0", children: /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: sessions.map((session, idx) => {
679
- const DeviceIcon = getDeviceIcon(session.device);
680
- return /* @__PURE__ */ jsx("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
681
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
682
- /* @__PURE__ */ jsx("div", { className: "p-2 rounded-lg bg-gray-100", children: /* @__PURE__ */ jsx(DeviceIcon, { className: "h-4 w-4 text-gray-600" }) }),
683
- /* @__PURE__ */ jsxs("div", { children: [
684
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-900", children: session.device }),
685
- /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500 flex items-center gap-1", children: [
686
- /* @__PURE__ */ jsx(MapPin, { className: "h-3 w-3" }),
687
- session.location
688
- ] })
689
- ] })
690
- ] }),
691
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500", children: session.time })
692
- ] }) }, idx);
693
- }) }) })
694
- ] });
695
- }
696
- function DashboardStatsCard({ stats }) {
697
- return /* @__PURE__ */ jsxs(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
698
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
699
- /* @__PURE__ */ jsx(TrendingUp, { className: "h-4 w-4" }),
700
- "Dashboard Overview"
701
- ] }) }),
702
- /* @__PURE__ */ jsx(CardContent, { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
703
- stats.revenue && /* @__PURE__ */ jsxs("div", { className: "p-3 rounded-lg bg-emerald-50 border border-emerald-100", children: [
704
- /* @__PURE__ */ jsx("div", { className: "text-xs text-emerald-600 font-medium mb-1", children: "Revenue" }),
705
- /* @__PURE__ */ jsx("div", { className: "text-lg font-bold text-emerald-700", children: formatCurrency(stats.revenue.total) }),
706
- stats.revenue.change !== 0 && /* @__PURE__ */ jsxs("div", { className: `text-xs ${stats.revenue.change > 0 ? "text-emerald-600" : "text-red-600"}`, children: [
707
- stats.revenue.change > 0 ? "+" : "",
708
- stats.revenue.change,
709
- "% from last period"
710
- ] })
711
- ] }),
712
- stats.subscriptions && /* @__PURE__ */ jsxs("div", { className: "p-3 rounded-lg bg-blue-50 border border-blue-100", children: [
713
- /* @__PURE__ */ jsx("div", { className: "text-xs text-blue-600 font-medium mb-1", children: "Subscriptions" }),
714
- /* @__PURE__ */ jsxs("div", { className: "text-lg font-bold text-blue-700", children: [
715
- stats.subscriptions.active,
716
- " active"
717
- ] }),
718
- /* @__PURE__ */ jsxs("div", { className: "text-xs text-blue-600", children: [
719
- stats.subscriptions.total,
720
- " total"
721
- ] })
722
- ] }),
723
- stats.customers && /* @__PURE__ */ jsxs("div", { className: "p-3 rounded-lg bg-purple-50 border border-purple-100", children: [
724
- /* @__PURE__ */ jsx("div", { className: "text-xs text-purple-600 font-medium mb-1", children: "Customers" }),
725
- /* @__PURE__ */ jsx("div", { className: "text-lg font-bold text-purple-700", children: stats.customers.total }),
726
- stats.customers.new > 0 && /* @__PURE__ */ jsxs("div", { className: "text-xs text-purple-600", children: [
727
- "+",
728
- stats.customers.new,
729
- " new this period"
730
- ] })
731
- ] }),
732
- stats.transactions && /* @__PURE__ */ jsxs("div", { className: "p-3 rounded-lg bg-amber-50 border border-amber-100", children: [
733
- /* @__PURE__ */ jsx("div", { className: "text-xs text-amber-600 font-medium mb-1", children: "Transactions" }),
734
- /* @__PURE__ */ jsxs("div", { className: "text-lg font-bold text-amber-700", children: [
735
- stats.transactions.successful,
736
- " successful"
737
- ] }),
738
- stats.transactions.failed > 0 && /* @__PURE__ */ jsxs("div", { className: "text-xs text-red-600", children: [
739
- stats.transactions.failed,
740
- " failed"
741
- ] })
742
- ] })
743
- ] }) })
744
- ] });
745
- }
746
- function TopCustomersList({ customers }) {
747
- if (customers.length === 0) {
748
- return /* @__PURE__ */ jsx(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ jsxs(CardContent, { className: "py-8 text-center", children: [
749
- /* @__PURE__ */ jsx(Crown, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
750
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "No top customers data" })
751
- ] }) });
752
- }
753
- return /* @__PURE__ */ jsxs(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
754
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ jsxs(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
755
- /* @__PURE__ */ jsx(Crown, { className: "h-4 w-4 text-amber-500" }),
756
- "Top Customers"
757
- ] }) }),
758
- /* @__PURE__ */ jsx(CardContent, { className: "p-0", children: /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: customers.map((customer, idx) => /* @__PURE__ */ jsx("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
759
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
760
- /* @__PURE__ */ jsx("div", { className: `h-6 w-6 rounded-full flex items-center justify-center text-xs font-bold ${idx === 0 ? "bg-amber-100 text-amber-700" : idx === 1 ? "bg-slate-100 text-slate-700" : idx === 2 ? "bg-orange-100 text-orange-700" : "bg-gray-100 text-gray-700"}`, children: idx + 1 }),
761
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
762
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-900 truncate", children: customer.name }),
763
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 truncate", children: customer.email })
764
- ] })
765
- ] }),
766
- /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-emerald-600", children: typeof customer.revenue === "number" ? formatCurrency(customer.revenue) : customer.revenue })
767
- ] }) }, idx)) }) })
768
- ] });
769
- }
770
- function DataRenderer({ type, data }) {
771
- if (!data) return null;
772
- const dataObj = data;
773
- switch (type) {
774
- case "subscription":
775
- return /* @__PURE__ */ jsx(SubscriptionCard, { subscription: dataObj });
776
- case "subscriptions": {
777
- const subs = dataObj.subscriptions || dataObj || [];
778
- return /* @__PURE__ */ jsx(SubscriptionList, { subscriptions: Array.isArray(subs) ? subs : [subs] });
779
- }
780
- case "customer":
781
- return /* @__PURE__ */ jsx(CustomerCard, { customer: dataObj.customer || dataObj });
782
- case "customers": {
783
- const custs = dataObj.customers || dataObj || [];
784
- return /* @__PURE__ */ jsx(CustomerList, { customers: Array.isArray(custs) ? custs : [custs] });
785
- }
786
- case "transactions": {
787
- const txs = dataObj.transactions || dataObj || [];
788
- return /* @__PURE__ */ jsx(TransactionList, { transactions: Array.isArray(txs) ? txs : [txs] });
789
- }
790
- case "transaction":
791
- return /* @__PURE__ */ jsx(TransactionList, { transactions: [dataObj], maxItems: 1 });
792
- case "dashboard_stats":
793
- return /* @__PURE__ */ jsx(DashboardStatsCard, { stats: dataObj });
794
- case "top_customers": {
795
- const topCustomers = dataObj.customers || dataObj || [];
796
- return /* @__PURE__ */ jsx(TopCustomersList, { customers: Array.isArray(topCustomers) ? topCustomers : [topCustomers] });
797
- }
798
- case "api_keys": {
799
- const keys = dataObj.api_keys || dataObj || [];
800
- return /* @__PURE__ */ jsx(ApiKeyList, { apiKeys: Array.isArray(keys) ? keys : [keys] });
801
- }
802
- case "sessions": {
803
- const sessions = dataObj.sessions || dataObj || [];
804
- return /* @__PURE__ */ jsx(SessionList, { sessions: Array.isArray(sessions) ? sessions : [sessions] });
805
- }
806
- default:
807
- return null;
808
- }
809
- }
810
- var defaultFolders = [
811
- {
812
- id: "customers",
813
- title: "Customer Management",
814
- topics: [
815
- { id: "add-customer", label: "Add a new customer", prompt: "How do I add a new customer to the system and what information is required?" },
816
- { id: "customer-details", label: "View customer details", prompt: "Show me how to view and edit customer information and their payment history." }
817
- ]
818
- },
819
- {
820
- id: "payments",
821
- title: "Payment Processing",
822
- topics: [
823
- { id: "process-payment", label: "Process a payment", prompt: "How do I process a one-time payment for a customer?" },
824
- { id: "refund-payment", label: "Issue a refund", prompt: "Walk me through issuing a refund for a customer payment." }
825
- ]
826
- },
827
- {
828
- id: "subscriptions",
829
- title: "Subscription Management",
830
- topics: [
831
- { id: "create-subscription", label: "Create a subscription", prompt: "How do I set up a new subscription plan for a customer?" },
832
- { id: "update-subscription", label: "Update subscription tier", prompt: "How can I change a customer's subscription plan or upgrade them?" }
833
- ]
834
- },
835
- {
836
- id: "billing",
837
- title: "Billing",
838
- topics: [
839
- { id: "payment-methods", label: "Manage payment methods", prompt: "How do customers add or update their payment methods?" }
840
- ]
841
- }
842
- ];
843
- var defaultGuides = {
844
- "getting-started": {
845
- id: "getting-started",
846
- title: "Getting started",
847
- steps: [
848
- {
849
- text: "Step 1: Let's open the Customers page. This is where you'll manage all your customers - you can view, add, and edit customer information here.",
850
- navigation: { page: "customers" },
851
- cursorTarget: {
852
- selector: '[data-page="customers"]',
853
- offset: { x: 0, y: 0 },
854
- onClick: true
855
- }
856
- },
857
- {
858
- text: "Step 2: Now let's open the Add customer dialog. This is where you'll enter information for your first customer, including their name, email, and other details.",
859
- navigation: { page: "customers" },
860
- cursorTarget: {
861
- selector: '[data-testid="add-customer-button"]',
862
- offset: { x: 0, y: 0 },
863
- onClick: true
864
- }
865
- },
866
- {
867
- text: "Step 3: Let's open the Settings page. This is where you manage your account settings, company information, and preferences. We're going to verify your company address is correct.",
868
- navigation: { page: "settings", subtab: "general" },
869
- cursorTarget: {
870
- selector: '[data-page="settings"]',
871
- offset: { x: 0, y: 0 },
872
- onClick: true
873
- }
874
- },
875
- {
876
- text: "Step 4: Here's the address field in the Company Information section. It's important to verify this address is accurate since it's used for billing and shipping purposes. Make sure it's up to date.",
877
- navigation: { page: "settings", subtab: "general" },
878
- cursorTarget: {
879
- selector: '[data-testid="company-address-input"]',
880
- offset: { x: 0, y: 0 },
881
- onClick: false
882
- }
883
- },
884
- {
885
- text: "Step 5: Let's open the API Keys tab. This section shows all your API keys - you'll need these to connect your application to our services. We're going to create a production API key.",
886
- navigation: { page: "settings", subtab: "api" },
887
- cursorTarget: {
888
- selector: '[data-settings-tab="api"]',
889
- offset: { x: 0, y: 0 },
890
- onClick: true
891
- }
892
- },
893
- {
894
- text: "Step 6: Let's open the Create API Key dialog. Here you can create a new API key for your application. You'll want to give it a descriptive name like 'Production Key' so you can easily identify it later.",
895
- navigation: { page: "settings", subtab: "api" },
896
- cursorTarget: {
897
- selector: '[data-testid="create-api-key-button"]',
898
- offset: { x: 0, y: 0 },
899
- onClick: true
900
- }
901
- }
902
- ]
903
- },
904
- "add-api-key": {
905
- id: "add-api-key",
906
- title: "How to add an API key",
907
- steps: [
908
- {
909
- text: "Step 1: Let's open the Settings page. This is where you manage your account settings and preferences. We need to go here to access the API Keys section.",
910
- navigation: { page: "settings", subtab: "general" },
911
- cursorTarget: {
912
- selector: '[data-page="settings"]',
913
- offset: { x: 0, y: 0 },
914
- onClick: false
915
- }
916
- },
917
- {
918
- text: "Step 2: Let's open the API Keys tab. This section shows all your API keys and allows you to create new ones. You'll need API keys to connect your application to our services.",
919
- navigation: { page: "settings", subtab: "api" },
920
- cursorTarget: {
921
- selector: '[data-settings-tab="api"]',
922
- offset: { x: 0, y: 0 },
923
- onClick: true
924
- }
925
- },
926
- {
927
- text: "Step 3: Let's open the Create API Key dialog. This is where you'll create your new API key - you can give it a name to help you identify it later, then click Create to generate it.",
928
- navigation: { page: "settings", subtab: "api" },
929
- cursorTarget: {
930
- selector: '[data-testid="create-api-key-button"]',
931
- offset: { x: 0, y: 0 },
932
- onClick: true
933
- }
934
- }
935
- ]
936
- },
937
- "refund-payment": {
938
- id: "refund-payment",
939
- title: "How to refund a payment",
940
- steps: [
941
- {
942
- text: "Step 1: Let's open the Home page. This is your dashboard where you can view today's activity, your payment overview, and recent transactions. We need to go here to find the payment you want to refund.",
943
- navigation: { page: "dashboard" },
944
- cursorTarget: {
945
- selector: '[data-page="dashboard"]',
946
- offset: { x: 0, y: 0 },
947
- onClick: true
948
- }
949
- },
950
- {
951
- text: "Step 2: Scroll down to see the Recent transactions section at the bottom. This shows your latest payment transactions with their status. We're looking for a transaction with 'paid' status - only paid transactions can be refunded.",
952
- navigation: { page: "dashboard" },
953
- cursorTarget: {
954
- selector: '[data-testid="recent-activity-card"]',
955
- offset: { x: 0, y: 0 },
956
- onClick: false
957
- }
958
- },
959
- {
960
- text: "Step 3: Let's open the dropdown menu for this paid transaction. Each transaction has a menu button (three dots) that gives you options like refunding. Click it to see the available actions, including the 'Refund payment' option.",
961
- navigation: { page: "dashboard" },
962
- cursorTarget: {
963
- selector: '[data-testid="transaction-menu-button"]',
964
- offset: { x: 0, y: 0 },
965
- onClick: true
966
- }
967
- }
968
- ]
969
- }
970
- };
971
- var BUILT_IN_AGENT_URL = process.env.AGENT_URL || "https://api.kite.com";
972
- var defaultConfig = {
973
- userId: "",
974
- orgId: "",
975
- agentUrl: BUILT_IN_AGENT_URL,
976
- theme: "light",
977
- position: "bottom-right",
978
- showDefaultFolders: true,
979
- enableGuideCursor: true
980
- };
981
- var ChatPanelContext = React4.createContext(null);
982
- function ChatPanelProvider({ children, config }) {
983
- const value = React4.useMemo(() => {
984
- const mergedConfig = { ...defaultConfig, ...config };
985
- let folders = [];
986
- if (mergedConfig.showDefaultFolders !== false) {
987
- folders = [...defaultFolders];
988
- }
989
- if (mergedConfig.folders) {
990
- folders = [...folders, ...mergedConfig.folders];
991
- }
992
- const guides = {
993
- ...mergedConfig.showDefaultFolders !== false ? defaultGuides : {},
994
- ...mergedConfig.guides
995
- };
996
- return {
997
- config: mergedConfig,
998
- userId: mergedConfig.userId,
999
- orgId: mergedConfig.orgId,
1000
- agentUrl: mergedConfig.agentUrl || BUILT_IN_AGENT_URL,
1001
- folders,
1002
- guides,
1003
- theme: mergedConfig.theme || "light",
1004
- position: mergedConfig.position || "bottom-right",
1005
- enableGuideCursor: mergedConfig.enableGuideCursor !== false
1006
- };
1007
- }, [config]);
1008
- return /* @__PURE__ */ jsx(ChatPanelContext.Provider, { value, children });
1009
- }
1010
- function useChatPanelConfig() {
1011
- const context = React4.useContext(ChatPanelContext);
1012
- if (!context) {
1013
- return {
1014
- config: defaultConfig,
1015
- userId: defaultConfig.userId,
1016
- orgId: defaultConfig.orgId,
1017
- agentUrl: defaultConfig.agentUrl || BUILT_IN_AGENT_URL,
1018
- folders: defaultFolders,
1019
- guides: defaultGuides,
1020
- theme: "light",
1021
- position: "bottom-right",
1022
- enableGuideCursor: true
1023
- };
1024
- }
1025
- return context;
1026
- }
1027
- function renderMarkdown(text) {
1028
- if (!text) return null;
1029
- const lines = text.split("\n");
1030
- const elements = [];
1031
- let currentList = null;
1032
- let inCodeBlock = false;
1033
- let codeContent = [];
1034
- const processInlineFormatting = (line) => {
1035
- const parts = [];
1036
- let remaining = line;
1037
- let keyIndex = 0;
1038
- while (remaining.length > 0) {
1039
- const codeMatch = remaining.match(/^`([^`]+)`/);
1040
- if (codeMatch) {
1041
- parts.push(
1042
- /* @__PURE__ */ jsx("code", { className: "bg-gray-100 px-1 py-0.5 rounded text-xs font-mono", children: codeMatch[1] }, keyIndex++)
1043
- );
1044
- remaining = remaining.slice(codeMatch[0].length);
1045
- continue;
1046
- }
1047
- const boldMatch = remaining.match(/^\*\*([^*]+)\*\*/);
1048
- if (boldMatch) {
1049
- parts.push(/* @__PURE__ */ jsx("strong", { children: boldMatch[1] }, keyIndex++));
1050
- remaining = remaining.slice(boldMatch[0].length);
1051
- continue;
1052
- }
1053
- const italicMatch = remaining.match(/^\*([^*]+)\*/);
1054
- if (italicMatch) {
1055
- parts.push(/* @__PURE__ */ jsx("em", { children: italicMatch[1] }, keyIndex++));
1056
- remaining = remaining.slice(italicMatch[0].length);
1057
- continue;
1058
- }
1059
- const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)]+)\)/);
1060
- if (linkMatch) {
1061
- parts.push(
1062
- /* @__PURE__ */ jsx("a", { href: linkMatch[2], className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: linkMatch[1] }, keyIndex++)
1063
- );
1064
- remaining = remaining.slice(linkMatch[0].length);
1065
- continue;
1066
- }
1067
- const nextSpecial = remaining.search(/[`*\[]/);
1068
- if (nextSpecial === -1) {
1069
- parts.push(remaining);
1070
- break;
1071
- } else if (nextSpecial === 0) {
1072
- parts.push(remaining[0]);
1073
- remaining = remaining.slice(1);
1074
- } else {
1075
- parts.push(remaining.slice(0, nextSpecial));
1076
- remaining = remaining.slice(nextSpecial);
1077
- }
1078
- }
1079
- return parts.length === 1 ? parts[0] : /* @__PURE__ */ jsx(Fragment, { children: parts });
1080
- };
1081
- const flushList = () => {
1082
- if (currentList) {
1083
- const ListTag = currentList.type === "ul" ? "ul" : "ol";
1084
- elements.push(
1085
- /* @__PURE__ */ jsx(ListTag, { className: `${currentList.type === "ul" ? "list-disc" : "list-decimal"} ml-4 my-1`, children: currentList.items.map((item, i) => /* @__PURE__ */ jsx("li", { className: "ml-2", children: item }, i)) }, elements.length)
1086
- );
1087
- currentList = null;
1088
- }
1089
- };
1090
- for (let i = 0; i < lines.length; i++) {
1091
- const line = lines[i];
1092
- if (line.startsWith("```")) {
1093
- if (inCodeBlock) {
1094
- elements.push(
1095
- /* @__PURE__ */ jsx("pre", { className: "bg-gray-100 rounded p-2 my-1 overflow-x-auto", children: /* @__PURE__ */ jsx("code", { className: "text-xs font-mono", children: codeContent.join("\n") }) }, elements.length)
1096
- );
1097
- inCodeBlock = false;
1098
- codeContent = [];
1099
- } else {
1100
- flushList();
1101
- inCodeBlock = true;
1102
- line.slice(3).trim();
1103
- }
1104
- continue;
1105
- }
1106
- if (inCodeBlock) {
1107
- codeContent.push(line);
1108
- continue;
1109
- }
1110
- const headerMatch = line.match(/^(#{1,6})\s+(.+)/);
1111
- if (headerMatch) {
1112
- flushList();
1113
- const level = headerMatch[1].length;
1114
- const content = processInlineFormatting(headerMatch[2]);
1115
- const className = level === 1 ? "text-lg font-bold my-1" : level === 2 ? "text-base font-bold my-1" : "text-sm font-semibold my-1";
1116
- elements.push(/* @__PURE__ */ jsx("div", { className, children: content }, elements.length));
1117
- continue;
1118
- }
1119
- const ulMatch = line.match(/^[-*]\s+(.+)/);
1120
- if (ulMatch) {
1121
- if (!currentList || currentList.type !== "ul") {
1122
- flushList();
1123
- currentList = { type: "ul", items: [] };
1124
- }
1125
- currentList.items.push(processInlineFormatting(ulMatch[1]));
1126
- continue;
1127
- }
1128
- const olMatch = line.match(/^\d+[.)]\s+(.+)/);
1129
- if (olMatch) {
1130
- if (!currentList || currentList.type !== "ol") {
1131
- flushList();
1132
- currentList = { type: "ol", items: [] };
1133
- }
1134
- currentList.items.push(processInlineFormatting(olMatch[1]));
1135
- continue;
1136
- }
1137
- if (line.trim() === "") {
1138
- flushList();
1139
- elements.push(/* @__PURE__ */ jsx("div", { className: "h-2" }, elements.length));
1140
- continue;
1141
- }
1142
- if (line.match(/^[-*_]{3,}$/)) {
1143
- flushList();
1144
- elements.push(/* @__PURE__ */ jsx("hr", { className: "my-2 border-gray-200" }, elements.length));
1145
- continue;
1146
- }
1147
- flushList();
1148
- elements.push(/* @__PURE__ */ jsx("div", { children: processInlineFormatting(line) }, elements.length));
1149
- }
1150
- flushList();
1151
- return /* @__PURE__ */ jsx(Fragment, { children: elements });
1152
- }
1153
- var initialMessages = [];
1154
- function ChatPanel({
1155
- onBack,
1156
- onNavigate,
1157
- onActionComplete,
1158
- onAgentAction,
1159
- currentPage,
1160
- config: propConfig
1161
- }) {
1162
- const contextConfig = useChatPanelConfig();
1163
- const { userId, orgId, agentUrl, folders, guides, enableGuideCursor } = propConfig ? { ...contextConfig, ...propConfig, userId: propConfig.userId || contextConfig.userId, orgId: propConfig.orgId || contextConfig.orgId, agentUrl: propConfig.agentUrl || contextConfig.agentUrl } : contextConfig;
1164
- const [messages, setMessages] = React4.useState(initialMessages);
1165
- const [input, setInput] = React4.useState("");
1166
- const [sessionId] = React4.useState(() => crypto.randomUUID());
1167
- const streamIntervals = React4.useRef({});
1168
- const isEmpty = messages.length === 0;
1169
- const [phase, setPhase] = React4.useState("idle");
1170
- const [progressSteps, setProgressSteps] = React4.useState([]);
1171
- const phaseTimers = React4.useRef([]);
1172
- const lastRole = messages.length ? messages[messages.length - 1].role : void 0;
1173
- const [panelView, setPanelView] = React4.useState("landing");
1174
- const [currentFolderId, setCurrentFolderId] = React4.useState(void 0);
1175
- const [activeGuide, setActiveGuide] = React4.useState(void 0);
1176
- const activeGuideRef = React4.useRef(void 0);
1177
- const latestBulkSummaryNavigationRef = React4.useRef(null);
1178
- const [guideComplete, setGuideComplete] = React4.useState(false);
1179
- React4.useEffect(() => {
1180
- window.resetIntegrationNotification = () => {
1181
- localStorage.removeItem("gmailNotificationSeen");
1182
- console.log("Integration notification reset! Click the Integrations tab to see it again.");
1183
- };
1184
- const handleIntegrationTabClick = () => {
1185
- const hasSeenNotification = localStorage.getItem("gmailNotificationSeen");
1186
- if (!hasSeenNotification) {
1187
- setPanelView("landing");
1188
- setCurrentFolderId(void 0);
1189
- const messageId = Date.now();
1190
- const notificationText = "I see you're exploring integrations. Let me know if there are any other connections we should add.";
1191
- const draftMessage = {
1192
- id: messageId,
1193
- role: "assistant",
1194
- kind: "text",
1195
- content: "",
1196
- isNotificationMessage: true
1197
- };
1198
- setMessages((prev) => [...prev, draftMessage]);
1199
- streamAssistantMessage(messageId, notificationText);
1200
- localStorage.setItem("gmailNotificationSeen", "true");
1201
- }
1202
- };
1203
- window.addEventListener("integrationTabClicked", handleIntegrationTabClick);
1204
- return () => {
1205
- window.removeEventListener("integrationTabClicked", handleIntegrationTabClick);
1206
- };
1207
- }, []);
1208
- React4.useEffect(() => {
1209
- if (activeGuide) {
1210
- if (!activeGuideRef.current || activeGuideRef.current.id !== activeGuide.id || activeGuideRef.current.stepIndex !== activeGuide.stepIndex) {
1211
- activeGuideRef.current = activeGuide;
1212
- }
1213
- } else {
1214
- activeGuideRef.current = void 0;
1215
- }
1216
- }, [activeGuide]);
1217
- const [pendingNavigation, setPendingNavigation] = React4.useState(null);
1218
- const [pendingAction, setPendingAction] = React4.useState(null);
1219
- const [actionFormData, setActionFormData] = React4.useState({});
1220
- const messagesEndRef = React4.useRef(null);
1221
- const messagesContainerRef = React4.useRef(null);
1222
- const currentStepRef = React4.useRef(null);
1223
- const { cursorState, moveTo, hide } = useGuideCursor();
1224
- const [pendingFile, setPendingFile] = React4.useState(null);
1225
- const [pendingBulkSession, setPendingBulkSession] = React4.useState(null);
1226
- const [isCollapsed, setIsCollapsed] = React4.useState(false);
1227
- const pendingBulkSessionRef = React4.useRef(null);
1228
- const fileInputRef = React4.useRef(null);
1229
- React4.useEffect(() => {
1230
- if (!activeGuide || activeGuide.id !== "add-api-key" || activeGuide.stepIndex !== 2) {
1231
- return;
1232
- }
1233
- const checkForDialogOpen = () => {
1234
- const dialog = document.querySelector('[role="dialog"]');
1235
- const currentGuide = activeGuideRef.current;
1236
- if (dialog && currentGuide && currentGuide.id === "add-api-key" && currentGuide.stepIndex === 2) {
1237
- hide();
1238
- const id = Date.now() + 1;
1239
- setMessages((prev) => [
1240
- ...prev,
1241
- { id, role: "assistant", kind: "guideComplete", content: "Perfect! The dialog is now open. You can enter a name and create your API key." }
1242
- ]);
1243
- setActiveGuide(void 0);
1244
- setGuideComplete(true);
1245
- }
1246
- };
1247
- const interval = setInterval(checkForDialogOpen, 300);
1248
- return () => clearInterval(interval);
1249
- }, [activeGuide, hide]);
1250
- React4.useEffect(() => {
1251
- return () => {
1252
- Object.values(streamIntervals.current).forEach((id) => window.clearInterval(id));
1253
- streamIntervals.current = {};
1254
- phaseTimers.current.forEach((id) => window.clearTimeout(id));
1255
- phaseTimers.current = [];
1256
- };
1257
- }, []);
1258
- React4.useEffect(() => {
1259
- if (activeGuide && messages.length > 0) {
1260
- const lastMessage = messages[messages.length - 1];
1261
- if (lastMessage.kind === "guideStep" || lastMessage.kind === "guideComplete") {
1262
- if (currentStepRef.current && messagesContainerRef.current) {
1263
- const container = messagesContainerRef.current;
1264
- const stepElement = currentStepRef.current;
1265
- const containerRect = container.getBoundingClientRect();
1266
- const stepRect = stepElement.getBoundingClientRect();
1267
- const scrollTop = stepRect.top - containerRect.top + container.scrollTop;
1268
- container.scrollTop = scrollTop;
1269
- }
1270
- }
1271
- } else if (!activeGuide && messages.length > 0) {
1272
- messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
1273
- }
1274
- }, [messages, phase, activeGuide]);
1275
- const latestBulkSummaryNavigation = React4.useMemo(() => {
1276
- for (let i = messages.length - 1; i >= 0; i--) {
1277
- const msg = messages[i];
1278
- if (msg.kind === "bulkSummary" && msg.bulkSummary?.navigationPage && msg.bulkSummary.successes > 0) {
1279
- return msg.bulkSummary.navigationPage;
1280
- }
1281
- }
1282
- return null;
1283
- }, [messages]);
1284
- React4.useEffect(() => {
1285
- latestBulkSummaryNavigationRef.current = latestBulkSummaryNavigation;
1286
- }, [latestBulkSummaryNavigation]);
1287
- React4.useEffect(() => {
1288
- pendingBulkSessionRef.current = pendingBulkSession;
1289
- }, [pendingBulkSession]);
1290
- React4.useEffect(() => {
1291
- const handleKeyDown = (e) => {
1292
- if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
1293
- const currentBulkSession = pendingBulkSessionRef.current;
1294
- if (currentBulkSession) {
1295
- e.preventDefault();
1296
- e.stopPropagation();
1297
- confirmBulkOperation(currentBulkSession);
1298
- return;
1299
- }
1300
- if (pendingAction) {
1301
- e.preventDefault();
1302
- e.stopPropagation();
1303
- handleActionSubmit();
1304
- return;
1305
- }
1306
- if (pendingNavigation) {
1307
- e.preventDefault();
1308
- e.stopPropagation();
1309
- handleConfirmNavigation(pendingNavigation);
1310
- return;
1311
- }
1312
- const currentBulkNav = latestBulkSummaryNavigationRef.current;
1313
- if (currentBulkNav && onNavigate) {
1314
- e.preventDefault();
1315
- e.stopPropagation();
1316
- onNavigate(currentBulkNav.page, currentBulkNav.subtab);
1317
- return;
1318
- }
1319
- if (guideComplete) {
1320
- e.preventDefault();
1321
- e.stopPropagation();
1322
- handleBack();
1323
- return;
1324
- }
1325
- const currentGuide = activeGuideRef.current;
1326
- if (currentGuide) {
1327
- e.preventDefault();
1328
- e.stopPropagation();
1329
- advanceGuide();
1330
- return;
1331
- }
1332
- }
1333
- };
1334
- window.addEventListener("keydown", handleKeyDown);
1335
- return () => window.removeEventListener("keydown", handleKeyDown);
1336
- }, [pendingAction, pendingNavigation, activeGuide, guideComplete, onNavigate]);
1337
- function streamAssistantMessage(messageId, fullText, followups) {
1338
- const tokens = fullText.split(" ");
1339
- let i = 0;
1340
- const intervalId = window.setInterval(() => {
1341
- i += 1;
1342
- setMessages(
1343
- (prev) => prev.map((m) => m.id === messageId ? { ...m, content: tokens.slice(0, i).join(" ") } : m)
1344
- );
1345
- if (i >= tokens.length) {
1346
- window.clearInterval(intervalId);
1347
- delete streamIntervals.current[messageId];
1348
- if (followups && followups.length > 0) {
1349
- setMessages(
1350
- (prev) => prev.map((m) => m.id === messageId ? { ...m, followups } : m)
1351
- );
1352
- }
1353
- setPhase("idle");
1354
- }
1355
- }, 25);
1356
- streamIntervals.current[messageId] = intervalId;
1357
- }
1358
- function handleFollowupClick(messageId, followup) {
1359
- setMessages(
1360
- (prev) => prev.map((m) => m.id === messageId ? { ...m, followupSelected: true } : m)
1361
- );
1362
- startChatFlow(followup.label);
1363
- }
1364
- function handleBack() {
1365
- Object.values(streamIntervals.current).forEach((id) => window.clearInterval(id));
1366
- streamIntervals.current = {};
1367
- phaseTimers.current.forEach((id) => window.clearTimeout(id));
1368
- phaseTimers.current = [];
1369
- hide();
1370
- setMessages([]);
1371
- setInput("");
1372
- setPhase("idle");
1373
- setPanelView("landing");
1374
- setCurrentFolderId(void 0);
1375
- setActiveGuide(void 0);
1376
- setGuideComplete(false);
1377
- setPendingNavigation(null);
1378
- setPendingAction(null);
1379
- setActionFormData({});
1380
- if (onBack) onBack();
1381
- }
1382
- function openFolder(folderId) {
1383
- setPanelView("folder");
1384
- setCurrentFolderId(folderId);
1385
- }
1386
- function closeFolder() {
1387
- setPanelView("landing");
1388
- setCurrentFolderId(void 0);
1389
- }
1390
- function handleSubmit(e) {
1391
- e.preventDefault();
1392
- const trimmed = input.trim();
1393
- if (pendingFile) {
1394
- if (!trimmed) {
1395
- const errorMessage = {
1396
- id: Date.now(),
1397
- role: "assistant",
1398
- kind: "text",
1399
- content: "Please describe what you'd like to do with this CSV file. For example: 'Create customers from this file' or 'Add these subscriptions'."
1400
- };
1401
- setMessages((prev) => [...prev, errorMessage]);
1402
- return;
1403
- }
1404
- startBulkUploadFlow(trimmed, pendingFile);
1405
- setInput("");
1406
- setPendingFile(null);
1407
- if (fileInputRef.current) fileInputRef.current.value = "";
1408
- return;
1409
- }
1410
- if (!trimmed) return;
1411
- startChatFlow(trimmed);
1412
- setInput("");
1413
- }
1414
- function getPageDisplayName(target) {
1415
- const pageNames = {
1416
- dashboard: "dashboard",
1417
- customers: "customers page",
1418
- payments: "payments page",
1419
- settings: "settings page",
1420
- support: "support page",
1421
- disputes: "disputes page",
1422
- radar: "radar page"
1423
- };
1424
- if (target.page === "settings" && target.subtab) {
1425
- const subtabNames = {
1426
- general: "General",
1427
- billing: "Billing",
1428
- security: "Security",
1429
- api: "API Keys",
1430
- notifications: "Notifications",
1431
- integrations: "Integrations"
1432
- };
1433
- return `${pageNames.settings} (${subtabNames[target.subtab] || target.subtab} tab)`;
1434
- }
1435
- return pageNames[target.page] || target.page;
1436
- }
1437
- function handleConfirmNavigation(target) {
1438
- if (onNavigate) {
1439
- onNavigate(target.page, target.subtab);
1440
- }
1441
- setPendingNavigation(null);
1442
- setMessages(
1443
- (prev) => prev.map(
1444
- (msg) => msg.kind === "navigationAction" && msg.navigationTarget?.page === target.page && msg.navigationTarget?.subtab === target.subtab ? { ...msg, navigationTarget: void 0 } : msg
1445
- )
1446
- );
1447
- }
1448
- function handleActionSubmit() {
1449
- if (!pendingAction) return;
1450
- const data = actionFormData;
1451
- if (pendingAction.type === "updateCompanyInfo") ; else if (pendingAction.type === "addApiKey") {
1452
- const apiData = data;
1453
- if (!apiData.name || !apiData.name.trim()) return;
1454
- } else if (pendingAction.type === "addCustomer") {
1455
- const customerData = data;
1456
- if (!customerData.name || !customerData.name.trim() || !customerData.email || !customerData.email.trim()) return;
1457
- } else if (pendingAction.type === "changePassword") {
1458
- const passwordData = data;
1459
- if (!passwordData.currentPassword || !passwordData.newPassword || !passwordData.confirmPassword) return;
1460
- if (passwordData.newPassword !== passwordData.confirmPassword) return;
1461
- } else if (pendingAction.type === "addWebhook") {
1462
- const webhookData = data;
1463
- if (!webhookData.url || !webhookData.url.trim()) return;
1464
- } else if (pendingAction.type === "addPaymentMethod") {
1465
- const paymentData = data;
1466
- if (!paymentData.cardNumber || !paymentData.expiryDate) return;
1467
- } else if (pendingAction.type === "deleteApiKey") {
1468
- const apiData = data;
1469
- if (!apiData.keyId && !apiData.name) return;
1470
- } else if (pendingAction.type === "refundPayment") {
1471
- const refundData = data;
1472
- if (!refundData.transactionId && !refundData.customer) return;
1473
- } else if (pendingAction.type === "createSubscription") {
1474
- const subscriptionData = data;
1475
- if (!subscriptionData.customerEmail && !subscriptionData.customerId) return;
1476
- } else if (pendingAction.type === "toggleBlockRule" || pendingAction.type === "enableBlockRule" || pendingAction.type === "disableBlockRule") {
1477
- const blockRuleData = data;
1478
- if (!blockRuleData.ruleId) return;
1479
- }
1480
- if (onActionComplete) {
1481
- onActionComplete(pendingAction.type, data);
1482
- }
1483
- setMessages(
1484
- (prev) => prev.map(
1485
- (msg) => msg.kind === "actionForm" && msg.actionType === pendingAction.type ? { ...msg, isSubmitted: true } : msg
1486
- )
1487
- );
1488
- setPendingAction(null);
1489
- setActionFormData({});
1490
- }
1491
- function dispatchAgentAction(result) {
1492
- if (onAgentAction) {
1493
- onAgentAction({ result });
1494
- }
1495
- window.dispatchEvent(new CustomEvent("agentActionComplete", {
1496
- detail: { result }
1497
- }));
1498
- }
1499
- async function startChatFlow(userText) {
1500
- phaseTimers.current.forEach((id) => window.clearTimeout(id));
1501
- phaseTimers.current = [];
1502
- const lastAssistantMessage = [...messages].reverse().find((m) => m.role === "assistant");
1503
- const isRespondingToNotification = lastAssistantMessage?.isNotificationMessage === true;
1504
- const now = Date.now();
1505
- const userMessage = { id: now, role: "user", content: userText };
1506
- setMessages((prev) => [...prev, userMessage]);
1507
- if (isRespondingToNotification) {
1508
- const thankYouMessageId = Date.now() + 1;
1509
- const thankYouMessage = {
1510
- id: thankYouMessageId,
1511
- role: "assistant",
1512
- kind: "text",
1513
- content: ""
1514
- };
1515
- setMessages((prev) => [...prev, thankYouMessage]);
1516
- streamAssistantMessage(thankYouMessageId, "Thank you, your feedback is saved.");
1517
- return;
1518
- }
1519
- setPendingNavigation(null);
1520
- setPhase("thinking");
1521
- setProgressSteps([]);
1522
- let streamCompleted = false;
1523
- try {
1524
- const controller = new AbortController();
1525
- const timeoutId = setTimeout(() => controller.abort(), 6e4);
1526
- const response = await fetch(`${agentUrl}/chat/stream`, {
1527
- method: "POST",
1528
- headers: {
1529
- "Content-Type": "application/json"
1530
- },
1531
- body: JSON.stringify({
1532
- session_id: sessionId,
1533
- user_id: userId,
1534
- org_id: orgId,
1535
- message: userText,
1536
- current_page: currentPage || "dashboard"
1537
- }),
1538
- signal: controller.signal
1539
- });
1540
- clearTimeout(timeoutId);
1541
- if (!response.ok) {
1542
- throw new Error(`Agent request failed: ${response.status}`);
1543
- }
1544
- const reader = response.body?.getReader();
1545
- if (!reader) throw new Error("No response body");
1546
- const decoder = new TextDecoder();
1547
- let buffer = "";
1548
- while (true) {
1549
- const { done, value } = await reader.read();
1550
- if (done) break;
1551
- buffer += decoder.decode(value, { stream: true });
1552
- const events = buffer.split("\n\n");
1553
- buffer = events.pop() || "";
1554
- for (const eventStr of events) {
1555
- if (!eventStr.trim()) continue;
1556
- const lines = eventStr.split("\n");
1557
- let eventType = "";
1558
- let eventData = "";
1559
- for (const line of lines) {
1560
- if (line.startsWith("event: ")) {
1561
- eventType = line.slice(7);
1562
- } else if (line.startsWith("data: ")) {
1563
- eventData = line.slice(6);
1564
- }
1565
- }
1566
- if (!eventType || !eventData) continue;
1567
- try {
1568
- const data = JSON.parse(eventData);
1569
- if (eventType === "progress") {
1570
- if (data.status === "thinking") {
1571
- setPhase("thinking");
1572
- setProgressSteps([{ message: data.message, completed: false }]);
1573
- } else if (data.status === "executing") {
1574
- setPhase("executing");
1575
- setProgressSteps((prev) => {
1576
- const updated = prev.map((step) => ({ ...step, completed: true }));
1577
- const lastStep = updated[updated.length - 1];
1578
- if (!lastStep || lastStep.message !== data.message) {
1579
- updated.push({ message: data.message, completed: false });
1580
- }
1581
- return updated;
1582
- });
1583
- } else if (data.status === "responding") {
1584
- setPhase("responding");
1585
- setProgressSteps((prev) => prev.map((step) => ({ ...step, completed: true })));
1586
- }
1587
- } else if (eventType === "response") {
1588
- setProgressSteps([]);
1589
- streamCompleted = true;
1590
- const agentResponse = data;
1591
- if (agentResponse.action === "navigate" && agentResponse.navigation) {
1592
- const navTarget = {
1593
- page: agentResponse.navigation.page,
1594
- subtab: agentResponse.navigation.subtab
1595
- };
1596
- setPendingNavigation(navTarget);
1597
- const navigationMessage = {
1598
- id: now + 1,
1599
- role: "assistant",
1600
- kind: "navigationAction",
1601
- content: agentResponse.response || `I can take you to the ${getPageDisplayName(navTarget)}.`,
1602
- navigationTarget: navTarget
1603
- };
1604
- setMessages((prev) => [...prev, navigationMessage]);
1605
- setPhase("idle");
1606
- } else if (agentResponse.action === "show_form" && agentResponse.actionType) {
1607
- setPendingAction({
1608
- type: agentResponse.actionType,
1609
- data: agentResponse.actionData || {}
1610
- });
1611
- setActionFormData(agentResponse.actionData || {});
1612
- const actionMessage = {
1613
- id: now + 1,
1614
- role: "assistant",
1615
- kind: "actionForm",
1616
- content: agentResponse.response || agentResponse.message || "Please fill in the details below:",
1617
- actionType: agentResponse.actionType,
1618
- actionData: agentResponse.actionData || {}
1619
- };
1620
- setMessages((prev) => [...prev, actionMessage]);
1621
- setPhase("idle");
1622
- } else if (agentResponse.action === "suggest_bulk") {
1623
- const assistantMessageId = now + 1;
1624
- const assistantMessage = {
1625
- id: assistantMessageId,
1626
- role: "assistant",
1627
- kind: "text",
1628
- content: ""
1629
- };
1630
- setMessages((prev) => [...prev, assistantMessage]);
1631
- streamAssistantMessage(assistantMessageId, agentResponse.response || agentResponse.message || "For bulk operations, you can upload a CSV file - just click the \u{1F4CE} button!", agentResponse.followups);
1632
- streamCompleted = true;
1633
- } else if (agentResponse.action === "bulk_preview") {
1634
- setPendingBulkSession(agentResponse.bulk_session_id);
1635
- const previewMessage = {
1636
- id: now + 1,
1637
- role: "assistant",
1638
- kind: "bulkPreview",
1639
- content: agentResponse.message || `I found ${agentResponse.csv_data?.rowCount || 0} items. Here's a preview:`,
1640
- csvData: agentResponse.csv_data ? {
1641
- rowCount: agentResponse.csv_data.rowCount,
1642
- columns: agentResponse.csv_data.columns,
1643
- sampleRows: agentResponse.csv_data.sampleRows,
1644
- fileName: "From context"
1645
- } : void 0,
1646
- suggestedAction: agentResponse.suggested_action,
1647
- bulkSessionId: agentResponse.bulk_session_id
1648
- };
1649
- setMessages((prev) => [...prev, previewMessage]);
1650
- setPhase("idle");
1651
- streamCompleted = true;
1652
- } else if (agentResponse.action === "execute" && agentResponse.executionResult) {
1653
- const assistantMessageId = now + 1;
1654
- const assistantMessage = {
1655
- id: assistantMessageId,
1656
- role: "assistant",
1657
- kind: "text",
1658
- content: "",
1659
- structuredData: agentResponse.structuredData || void 0
1660
- };
1661
- setMessages((prev) => [...prev, assistantMessage]);
1662
- streamAssistantMessage(assistantMessageId, agentResponse.response, agentResponse.followups);
1663
- streamCompleted = true;
1664
- setTimeout(() => {
1665
- dispatchAgentAction(agentResponse.executionResult);
1666
- }, 100);
1667
- } else {
1668
- const assistantMessageId = now + 1;
1669
- const assistantMessage = {
1670
- id: assistantMessageId,
1671
- role: "assistant",
1672
- kind: "text",
1673
- content: ""
1674
- };
1675
- setMessages((prev) => [...prev, assistantMessage]);
1676
- streamAssistantMessage(assistantMessageId, agentResponse.response, agentResponse.followups);
1677
- streamCompleted = true;
1678
- }
1679
- } else if (eventType === "error") {
1680
- setPhase("idle");
1681
- setProgressSteps([]);
1682
- streamCompleted = true;
1683
- const errorMessageId = now + 1;
1684
- const errorMessage = {
1685
- id: errorMessageId,
1686
- role: "assistant",
1687
- kind: "text",
1688
- content: ""
1689
- };
1690
- setMessages((prev) => [...prev, errorMessage]);
1691
- streamAssistantMessage(errorMessageId, data.message || "An error occurred processing your request.");
1692
- } else if (eventType === "done") {
1693
- setProgressSteps([]);
1694
- setPhase("idle");
1695
- streamCompleted = true;
1696
- }
1697
- } catch (parseError) {
1698
- console.error("Failed to parse SSE event:", parseError);
1699
- }
1700
- }
1701
- }
1702
- setProgressSteps([]);
1703
- if (!streamCompleted) {
1704
- setPhase("idle");
1705
- }
1706
- } catch (error) {
1707
- console.error("Agent request failed:", error);
1708
- setProgressSteps([]);
1709
- const errorMessageId = now + 1;
1710
- const errorMessage = {
1711
- id: errorMessageId,
1712
- role: "assistant",
1713
- kind: "text",
1714
- content: ""
1715
- };
1716
- setMessages((prev) => [...prev, errorMessage]);
1717
- streamAssistantMessage(errorMessageId, "I'm having trouble connecting to my backend. Please make sure the agent server is running.");
1718
- } finally {
1719
- setProgressSteps([]);
1720
- }
1721
- }
1722
- async function startBulkUploadFlow(userText, file) {
1723
- phaseTimers.current.forEach((id) => window.clearTimeout(id));
1724
- phaseTimers.current = [];
1725
- const now = Date.now();
1726
- const userMessage = {
1727
- id: now,
1728
- role: "user",
1729
- content: `\u{1F4CE} ${file.name}
1730
-
1731
- ${userText}`
1732
- };
1733
- setMessages((prev) => [...prev, userMessage]);
1734
- setPhase("thinking");
1735
- setProgressSteps([{ message: "Analyzing CSV file...", completed: false }]);
1736
- try {
1737
- const formData = new FormData();
1738
- formData.append("file", file);
1739
- formData.append("message", userText);
1740
- formData.append("session_id", sessionId);
1741
- formData.append("user_id", userId);
1742
- formData.append("org_id", orgId);
1743
- formData.append("current_page", currentPage || "dashboard");
1744
- const controller = new AbortController();
1745
- const timeoutId = setTimeout(() => controller.abort(), 12e4);
1746
- const response = await fetch(`${agentUrl}/chat/bulk/stream`, {
1747
- method: "POST",
1748
- body: formData,
1749
- signal: controller.signal
1750
- });
1751
- clearTimeout(timeoutId);
1752
- if (!response.ok) {
1753
- throw new Error(`Bulk upload failed: ${response.status}`);
1754
- }
1755
- const reader = response.body?.getReader();
1756
- if (!reader) throw new Error("No response body");
1757
- const decoder = new TextDecoder();
1758
- let buffer = "";
1759
- let bulkSessionId = null;
1760
- while (true) {
1761
- const { done, value } = await reader.read();
1762
- if (done) break;
1763
- buffer += decoder.decode(value, { stream: true });
1764
- const events = buffer.split("\n\n");
1765
- buffer = events.pop() || "";
1766
- for (const eventStr of events) {
1767
- if (!eventStr.trim()) continue;
1768
- const lines = eventStr.split("\n");
1769
- let eventType = "";
1770
- let eventData = "";
1771
- for (const line of lines) {
1772
- if (line.startsWith("event: ")) {
1773
- eventType = line.slice(7);
1774
- } else if (line.startsWith("data: ")) {
1775
- eventData = line.slice(6);
1776
- }
1777
- }
1778
- if (!eventType || !eventData) continue;
1779
- try {
1780
- const data = JSON.parse(eventData);
1781
- if (eventType === "preview") {
1782
- setPhase("idle");
1783
- setProgressSteps([]);
1784
- bulkSessionId = data.bulk_session_id;
1785
- setPendingBulkSession(bulkSessionId);
1786
- const previewMessage = {
1787
- id: now + 1,
1788
- role: "assistant",
1789
- kind: "bulkPreview",
1790
- content: data.message || `I found ${data.csv_data?.rowCount || 0} items. Here's a preview:`,
1791
- csvData: data.csv_data ? {
1792
- rowCount: data.csv_data.rowCount,
1793
- columns: data.csv_data.columns,
1794
- sampleRows: data.csv_data.sampleRows,
1795
- fileName: file.name
1796
- } : void 0,
1797
- suggestedAction: data.suggested_action,
1798
- bulkSessionId: bulkSessionId || void 0
1799
- };
1800
- setMessages((prev) => [...prev, previewMessage]);
1801
- } else if (eventType === "error") {
1802
- setPhase("idle");
1803
- setProgressSteps([]);
1804
- const errorMessage = {
1805
- id: Date.now(),
1806
- role: "assistant",
1807
- kind: "text",
1808
- content: data.message || "An error occurred processing the CSV file."
1809
- };
1810
- setMessages((prev) => [...prev, errorMessage]);
1811
- }
1812
- } catch (parseError) {
1813
- console.error("Failed to parse SSE event:", parseError);
1814
- }
1815
- }
1816
- }
1817
- } catch (error) {
1818
- console.error("Bulk upload failed:", error);
1819
- setPhase("idle");
1820
- setProgressSteps([]);
1821
- const errorMessage = {
1822
- id: Date.now(),
1823
- role: "assistant",
1824
- kind: "text",
1825
- content: "Failed to process CSV file. Please try again."
1826
- };
1827
- setMessages((prev) => [...prev, errorMessage]);
1828
- }
1829
- }
1830
- async function confirmBulkOperation(bulkSessionId) {
1831
- setPhase("executing");
1832
- setProgressSteps([{ message: "Starting bulk operation...", completed: false }]);
1833
- setPendingBulkSession(null);
1834
- try {
1835
- const response = await fetch(`${agentUrl}/chat/bulk/confirm`, {
1836
- method: "POST",
1837
- headers: {
1838
- "Content-Type": "application/json"
1839
- },
1840
- body: JSON.stringify({
1841
- session_id: sessionId,
1842
- user_id: userId,
1843
- org_id: orgId,
1844
- bulk_session_id: bulkSessionId
1845
- })
1846
- });
1847
- if (!response.ok) {
1848
- throw new Error(`Bulk confirm failed: ${response.status}`);
1849
- }
1850
- const reader = response.body?.getReader();
1851
- if (!reader) throw new Error("No response body");
1852
- const decoder = new TextDecoder();
1853
- let buffer = "";
1854
- while (true) {
1855
- const { done, value } = await reader.read();
1856
- if (done) break;
1857
- buffer += decoder.decode(value, { stream: true });
1858
- const events = buffer.split("\n\n");
1859
- buffer = events.pop() || "";
1860
- for (const eventStr of events) {
1861
- if (!eventStr.trim()) continue;
1862
- const lines = eventStr.split("\n");
1863
- let eventType = "";
1864
- let eventData = "";
1865
- for (const line of lines) {
1866
- if (line.startsWith("event: ")) {
1867
- eventType = line.slice(7);
1868
- } else if (line.startsWith("data: ")) {
1869
- eventData = line.slice(6);
1870
- }
1871
- }
1872
- if (!eventType || !eventData) continue;
1873
- try {
1874
- const data = JSON.parse(eventData);
1875
- if (eventType === "progress") {
1876
- setMessages((prev) => {
1877
- const existing = prev.find((m) => m.kind === "bulkProgress");
1878
- if (existing) {
1879
- return prev.map(
1880
- (m) => m.kind === "bulkProgress" ? { ...m, bulkProgress: { processed: data.row, total: data.total, successes: data.successes || 0, failures: data.failures || 0 } } : m
1881
- );
1882
- } else {
1883
- return [...prev, {
1884
- id: Date.now(),
1885
- role: "assistant",
1886
- kind: "bulkProgress",
1887
- bulkProgress: {
1888
- processed: data.row,
1889
- total: data.total,
1890
- successes: data.successes || 0,
1891
- failures: data.failures || 0
1892
- }
1893
- }];
1894
- }
1895
- });
1896
- } else if (eventType === "summary") {
1897
- setPhase("idle");
1898
- setProgressSteps([]);
1899
- setPendingBulkSession(null);
1900
- setMessages((prev) => {
1901
- const filtered = prev.filter((m) => m.kind !== "bulkProgress" && m.kind !== "bulkPreview");
1902
- const newMsg = {
1903
- id: Date.now(),
1904
- role: "assistant",
1905
- kind: "bulkSummary",
1906
- content: data.message,
1907
- bulkSummary: {
1908
- total: data.total,
1909
- successes: data.successes,
1910
- failures: data.failures || [],
1911
- navigationPage: data.navigationPage
1912
- }
1913
- };
1914
- return [...filtered, newMsg];
1915
- });
1916
- setTimeout(() => {
1917
- dispatchAgentAction({ bulk: true, total: data.total, successes: data.successes });
1918
- }, 100);
1919
- } else if (eventType === "error") {
1920
- setPhase("idle");
1921
- setProgressSteps([]);
1922
- setPendingBulkSession(null);
1923
- const errorMessage = {
1924
- id: Date.now(),
1925
- role: "assistant",
1926
- kind: "text",
1927
- content: data.message || "An error occurred during bulk processing."
1928
- };
1929
- setMessages((prev) => [...prev, errorMessage]);
1930
- }
1931
- } catch (parseError) {
1932
- console.error("Failed to parse confirm SSE event:", parseError);
1933
- }
1934
- }
1935
- }
1936
- } catch (error) {
1937
- console.error("Bulk confirm failed:", error);
1938
- setPhase("idle");
1939
- setProgressSteps([]);
1940
- setPendingBulkSession(null);
1941
- const errorMessage = {
1942
- id: Date.now(),
1943
- role: "assistant",
1944
- kind: "text",
1945
- content: "Failed to start bulk operation. Please try again."
1946
- };
1947
- setMessages((prev) => [...prev, errorMessage]);
1948
- }
1949
- }
1950
- function cancelBulkOperation() {
1951
- setPendingBulkSession(null);
1952
- setMessages((prev) => prev.filter((m) => m.kind !== "bulkPreview"));
1953
- }
1954
- function sendTopic(prompt) {
1955
- startChatFlow(prompt);
1956
- }
1957
- function appendAssistantText(text) {
1958
- const id = Date.now() + Math.floor(Math.random() * 1e3);
1959
- setMessages((prev) => [...prev, { id, role: "assistant", kind: "text", content: text }]);
1960
- }
1961
- function startGuide(guideId) {
1962
- const guide = guides[guideId];
1963
- if (!guide) return;
1964
- setPanelView("landing");
1965
- setCurrentFolderId(void 0);
1966
- const initialGuideState = { id: guideId, stepIndex: 0 };
1967
- setActiveGuide(initialGuideState);
1968
- activeGuideRef.current = initialGuideState;
1969
- appendAssistantText(`I'll help you with ${guide.title}.`);
1970
- const firstStep = guide.steps[0];
1971
- const isNavigationButton = firstStep.cursorTarget?.selector.includes("[data-page=");
1972
- const isTabButton = firstStep.cursorTarget?.selector.includes("[data-settings-tab=");
1973
- if (firstStep.cursorTarget && (isNavigationButton || isTabButton)) {
1974
- const cursorTarget = firstStep.cursorTarget;
1975
- if (isTabButton && firstStep.navigation && onNavigate) {
1976
- onNavigate(firstStep.navigation.page, void 0);
1977
- }
1978
- const initialDelay = isTabButton ? 800 : 400;
1979
- setTimeout(() => {
1980
- const waitForElement = () => {
1981
- const element = document.querySelector(cursorTarget.selector);
1982
- if (element && element.offsetParent !== null) {
1983
- moveTo(cursorTarget);
1984
- if (cursorTarget.onClick) {
1985
- setTimeout(() => {
1986
- element.click();
1987
- }, 1200);
1988
- }
1989
- } else {
1990
- setTimeout(waitForElement, 200);
1991
- }
1992
- };
1993
- waitForElement();
1994
- }, initialDelay);
1995
- } else {
1996
- if (firstStep.navigation && onNavigate) {
1997
- onNavigate(firstStep.navigation.page, firstStep.navigation.subtab);
1998
- }
1999
- if (firstStep.cursorTarget) {
2000
- const cursorTarget = firstStep.cursorTarget;
2001
- const isSettingsTab = cursorTarget.selector.includes("data-settings-tab");
2002
- const isDialogElement = cursorTarget.selector.includes("dialog") || cursorTarget.selector.includes("api-key-name-input");
2003
- const initialDelay = isDialogElement ? 700 : isSettingsTab ? 600 : 400;
2004
- setTimeout(() => {
2005
- const waitForElement = () => {
2006
- const element = document.querySelector(cursorTarget.selector);
2007
- if (element && element.offsetParent !== null) {
2008
- moveTo(cursorTarget);
2009
- if (cursorTarget.onClick) {
2010
- setTimeout(() => {
2011
- element.click();
2012
- }, 1e3);
2013
- }
2014
- } else if (isSettingsTab || isDialogElement) {
2015
- setTimeout(waitForElement, 200);
2016
- } else {
2017
- moveTo(cursorTarget);
2018
- if (cursorTarget.onClick) {
2019
- setTimeout(() => {
2020
- const el = document.querySelector(cursorTarget.selector);
2021
- if (el) el.click();
2022
- }, 1e3);
2023
- }
2024
- }
2025
- };
2026
- waitForElement();
2027
- }, initialDelay);
2028
- }
2029
- }
2030
- const draft = {
2031
- id: Date.now() + 1,
2032
- role: "assistant",
2033
- kind: "guideStep",
2034
- content: "",
2035
- guideStepIndex: 0
2036
- };
2037
- setMessages((prev) => [...prev, draft]);
2038
- streamAssistantMessage(draft.id, firstStep.text);
2039
- }
2040
- function advanceGuide() {
2041
- if (!activeGuide) return;
2042
- const guide = guides[activeGuide.id];
2043
- if (!guide) return;
2044
- let currentGuide = activeGuideRef.current;
2045
- if (!currentGuide || currentGuide.id !== activeGuide.id) {
2046
- currentGuide = activeGuide;
2047
- activeGuideRef.current = activeGuide;
2048
- }
2049
- const currentStepIndex = currentGuide.stepIndex;
2050
- const nextIndex = currentStepIndex + 1;
2051
- if (nextIndex >= guide.steps.length) {
2052
- hide();
2053
- const id = Date.now() + 1;
2054
- setMessages((prev) => [
2055
- ...prev,
2056
- { id, role: "assistant", kind: "guideComplete", content: "Guide Complete" }
2057
- ]);
2058
- setActiveGuide(void 0);
2059
- setGuideComplete(true);
2060
- return;
2061
- }
2062
- const nextStep = guide.steps[nextIndex];
2063
- const newGuideState = { id: activeGuide.id, stepIndex: nextIndex };
2064
- setActiveGuide(newGuideState);
2065
- activeGuideRef.current = newGuideState;
2066
- const draft = {
2067
- id: Date.now() + 1,
2068
- role: "assistant",
2069
- kind: "guideStep",
2070
- content: "",
2071
- guideStepIndex: nextIndex
2072
- };
2073
- setMessages((prev) => [...prev, draft]);
2074
- const isNavigationButton = nextStep.cursorTarget?.selector.includes("[data-page=");
2075
- const isTabButton = nextStep.cursorTarget?.selector.includes("[data-settings-tab=");
2076
- if (nextStep.cursorTarget && (isNavigationButton || isTabButton)) {
2077
- const cursorTarget = nextStep.cursorTarget;
2078
- if (isTabButton && nextStep.navigation && onNavigate) {
2079
- onNavigate(nextStep.navigation.page, void 0);
2080
- }
2081
- const initialDelay = isTabButton ? 800 : 400;
2082
- setTimeout(() => {
2083
- const currentGuideCheck = activeGuideRef.current;
2084
- if (!currentGuideCheck || currentGuideCheck.id !== guide.id || currentGuideCheck.stepIndex !== nextIndex) {
2085
- return;
2086
- }
2087
- const waitForElement = () => {
2088
- const currentGuideCheck2 = activeGuideRef.current;
2089
- if (!currentGuideCheck2 || currentGuideCheck2.id !== guide.id || currentGuideCheck2.stepIndex !== nextIndex) {
2090
- return;
2091
- }
2092
- const element = document.querySelector(cursorTarget.selector);
2093
- if (element && element.offsetParent !== null) {
2094
- moveTo(cursorTarget);
2095
- if (cursorTarget.onClick) {
2096
- setTimeout(() => {
2097
- const currentGuideCheck3 = activeGuideRef.current;
2098
- if (!currentGuideCheck3 || currentGuideCheck3.id !== guide.id || currentGuideCheck3.stepIndex !== nextIndex) {
2099
- return;
2100
- }
2101
- element.click();
2102
- }, 1200);
2103
- }
2104
- } else {
2105
- setTimeout(waitForElement, 200);
2106
- }
2107
- };
2108
- waitForElement();
2109
- }, initialDelay);
2110
- } else {
2111
- if (nextStep.navigation && onNavigate) {
2112
- onNavigate(nextStep.navigation.page, nextStep.navigation.subtab);
2113
- }
2114
- if (nextStep.cursorTarget) {
2115
- const cursorTarget = nextStep.cursorTarget;
2116
- const isDialogElement = cursorTarget.selector.includes("dialog") || cursorTarget.selector.includes("api-key-name-input");
2117
- const isSettingsTab = cursorTarget.selector.includes("data-settings-tab");
2118
- const navigationSetTab = nextStep.navigation?.subtab && isSettingsTab && cursorTarget.selector.includes(`data-settings-tab="${nextStep.navigation.subtab}"`);
2119
- const shouldAutoClick = cursorTarget.onClick && !navigationSetTab;
2120
- const hasNavigation = !!nextStep.navigation;
2121
- const hasSubtab = !!nextStep.navigation?.subtab;
2122
- const navigationDelay = hasSubtab ? 700 : hasNavigation ? 400 : 0;
2123
- const initialDelay = navigationDelay + (isDialogElement ? 700 : isSettingsTab ? 600 : 400);
2124
- setTimeout(() => {
2125
- const currentGuideCheck = activeGuideRef.current;
2126
- if (!currentGuideCheck || currentGuideCheck.id !== guide.id || currentGuideCheck.stepIndex !== nextIndex) {
2127
- return;
2128
- }
2129
- const waitForElement = () => {
2130
- const currentGuideCheck2 = activeGuideRef.current;
2131
- if (!currentGuideCheck2 || currentGuideCheck2.id !== guide.id || currentGuideCheck2.stepIndex !== nextIndex) {
2132
- return;
2133
- }
2134
- const element = document.querySelector(cursorTarget.selector);
2135
- if (element && element.offsetParent !== null) {
2136
- moveTo(cursorTarget);
2137
- if (shouldAutoClick) {
2138
- setTimeout(() => {
2139
- const currentGuideCheck3 = activeGuideRef.current;
2140
- if (!currentGuideCheck3 || currentGuideCheck3.id !== guide.id || currentGuideCheck3.stepIndex !== nextIndex) {
2141
- return;
2142
- }
2143
- element.click();
2144
- }, 1e3);
2145
- }
2146
- } else if (isDialogElement || isSettingsTab || cursorTarget.selector.includes("data-testid")) {
2147
- setTimeout(waitForElement, 200);
2148
- } else {
2149
- moveTo(cursorTarget);
2150
- if (shouldAutoClick) {
2151
- setTimeout(() => {
2152
- const currentGuideCheck3 = activeGuideRef.current;
2153
- if (!currentGuideCheck3 || currentGuideCheck3.id !== guide.id || currentGuideCheck3.stepIndex !== nextIndex) {
2154
- return;
2155
- }
2156
- const el = document.querySelector(cursorTarget.selector);
2157
- if (el) el.click();
2158
- }, 1e3);
2159
- }
2160
- }
2161
- };
2162
- waitForElement();
2163
- }, initialDelay);
2164
- } else {
2165
- hide();
2166
- }
2167
- }
2168
- streamAssistantMessage(draft.id, nextStep.text);
2169
- }
2170
- function goBackGuide() {
2171
- if (!activeGuide) return;
2172
- const guide = guides[activeGuide.id];
2173
- if (!guide) return;
2174
- const prevIndex = activeGuide.stepIndex - 1;
2175
- if (prevIndex >= 0) {
2176
- setActiveGuide({ id: activeGuide.id, stepIndex: prevIndex });
2177
- activeGuideRef.current = { id: activeGuide.id, stepIndex: prevIndex };
2178
- const prevStep = guide.steps[prevIndex];
2179
- if (prevStep.navigation && onNavigate) {
2180
- onNavigate(prevStep.navigation.page, prevStep.navigation.subtab);
2181
- }
2182
- if (prevStep.cursorTarget) {
2183
- moveTo(prevStep.cursorTarget);
2184
- } else {
2185
- hide();
2186
- }
2187
- }
2188
- }
2189
- const currentFolder = folders.find((f) => f.id === currentFolderId);
2190
- return /* @__PURE__ */ jsxs("section", { className: "fixed bottom-6 right-6 z-50 flex flex-col w-[380px] max-h-[600px] rounded-2xl border border-gray-200 bg-white shadow-2xl overflow-hidden", children: [
2191
- /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-between px-4 py-3 border-b border-gray-100 bg-white", children: [
2192
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2193
- (!isEmpty || panelView === "folder") && /* @__PURE__ */ jsx(
2194
- Button,
2195
- {
2196
- type: "button",
2197
- size: "icon",
2198
- variant: "ghost",
2199
- className: "h-7 w-7 rounded-full",
2200
- onClick: panelView === "folder" ? closeFolder : handleBack,
2201
- children: /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" })
2202
- }
2203
- ),
2204
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700", children: panelView === "folder" && currentFolder ? currentFolder.title : "AI Assistant" })
2205
- ] }),
2206
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2207
- /* @__PURE__ */ jsx(
2208
- Button,
2209
- {
2210
- type: "button",
2211
- size: "icon",
2212
- variant: "ghost",
2213
- className: "h-7 w-7 rounded-full",
2214
- onClick: () => setIsCollapsed(!isCollapsed),
2215
- children: isCollapsed ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
2216
- }
2217
- ),
2218
- !isEmpty && /* @__PURE__ */ jsx(
2219
- Button,
2220
- {
2221
- type: "button",
2222
- size: "icon",
2223
- variant: "ghost",
2224
- className: "h-7 w-7 rounded-full",
2225
- onClick: handleBack,
2226
- children: /* @__PURE__ */ jsx(SquarePen, { className: "h-4 w-4" })
2227
- }
2228
- )
2229
- ] })
2230
- ] }),
2231
- !isCollapsed && /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 flex flex-col overflow-hidden", children: isEmpty && panelView === "landing" ? (
2232
- // Landing view
2233
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: [
2234
- /* @__PURE__ */ jsx("div", { className: "space-y-2", children: folders.map((folder) => /* @__PURE__ */ jsxs(
2235
- "button",
2236
- {
2237
- type: "button",
2238
- onClick: () => openFolder(folder.id),
2239
- className: "w-full text-left px-3 py-2.5 rounded-lg hover:bg-gray-50 transition-colors border border-gray-100",
2240
- children: [
2241
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-800", children: folder.title }),
2242
- /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500 mt-0.5", children: [
2243
- folder.topics.length,
2244
- " topics"
2245
- ] })
2246
- ]
2247
- },
2248
- folder.id
2249
- )) }),
2250
- /* @__PURE__ */ jsxs("div", { className: "mt-6 pt-4 border-t border-gray-100", children: [
2251
- /* @__PURE__ */ jsx("div", { className: "text-xs uppercase tracking-wider text-gray-400 mb-2", children: "Interactive Guides" }),
2252
- Object.values(guides).map((guide) => /* @__PURE__ */ jsx(
2253
- "button",
2254
- {
2255
- type: "button",
2256
- onClick: () => startGuide(guide.id),
2257
- className: "w-full text-left px-3 py-2 rounded-lg hover:bg-gray-50 transition-colors text-sm text-gray-700",
2258
- children: guide.title
2259
- },
2260
- guide.id
2261
- ))
2262
- ] })
2263
- ] })
2264
- ) : isEmpty && panelView === "folder" && currentFolder ? (
2265
- // Folder view
2266
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: /* @__PURE__ */ jsx("div", { className: "space-y-1", children: currentFolder.topics.map((topic) => /* @__PURE__ */ jsx(
2267
- "button",
2268
- {
2269
- type: "button",
2270
- onClick: () => sendTopic(topic.prompt),
2271
- className: "w-full text-left px-3 py-2 rounded-lg hover:bg-gray-50 transition-colors text-sm text-gray-700",
2272
- children: topic.label
2273
- },
2274
- topic.id
2275
- )) }) })
2276
- ) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: /* @__PURE__ */ jsx(ScrollArea, { ref: messagesContainerRef, className: "h-full px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2277
- messages.map((message, idx) => {
2278
- const prevMessage = idx > 0 ? messages[idx - 1] : null;
2279
- const isRoleChange = !prevMessage || prevMessage.role !== message.role;
2280
- const isCurrentGuideStep = message.kind === "guideStep" && activeGuide && message.guideStepIndex === activeGuide.stepIndex;
2281
- if (message.role === "user") {
2282
- return /* @__PURE__ */ jsx("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: /* @__PURE__ */ jsx("div", { className: "text-sm leading-6 text-gray-900 whitespace-pre-wrap", children: message.content }) }, message.id);
2283
- }
2284
- if (message.kind === "navigationAction" && message.navigationTarget) {
2285
- return /* @__PURE__ */ jsxs("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: [
2286
- /* @__PURE__ */ jsx("div", { className: "text-sm leading-6 text-gray-700 mb-2", children: message.content }),
2287
- /* @__PURE__ */ jsxs(
2288
- Button,
2289
- {
2290
- type: "button",
2291
- size: "sm",
2292
- variant: "secondary",
2293
- className: "h-8 rounded-xl px-3 text-xs gap-1.5 bg-gray-100 hover:bg-gray-200 border border-gray-200",
2294
- onClick: () => message.navigationTarget && handleConfirmNavigation(message.navigationTarget),
2295
- children: [
2296
- /* @__PURE__ */ jsxs("span", { children: [
2297
- "Go to ",
2298
- getPageDisplayName(message.navigationTarget)
2299
- ] }),
2300
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-0.5 text-gray-400", children: [
2301
- /* @__PURE__ */ jsx(Command, { className: "h-3 w-3" }),
2302
- /* @__PURE__ */ jsx(CornerDownLeft, { className: "h-3 w-3" })
2303
- ] })
2304
- ]
2305
- }
2306
- )
2307
- ] }, message.id);
2308
- }
2309
- if (message.kind === "actionForm" && message.actionType && !message.isSubmitted) {
2310
- return /* @__PURE__ */ jsxs("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: [
2311
- /* @__PURE__ */ jsx("div", { className: "text-sm leading-6 text-gray-700 mb-3", children: message.content }),
2312
- /* @__PURE__ */ jsx("div", { className: "space-y-2 mb-3", children: /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500", children: [
2313
- "Form for: ",
2314
- message.actionType
2315
- ] }) }),
2316
- /* @__PURE__ */ jsxs(
2317
- Button,
2318
- {
2319
- type: "button",
2320
- size: "sm",
2321
- variant: "secondary",
2322
- className: "h-8 rounded-xl px-3 text-xs gap-1.5 bg-gray-100 hover:bg-gray-200 border border-gray-200",
2323
- onClick: handleActionSubmit,
2324
- children: [
2325
- /* @__PURE__ */ jsx("span", { children: "Submit" }),
2326
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-0.5 text-gray-400", children: [
2327
- /* @__PURE__ */ jsx(Command, { className: "h-3 w-3" }),
2328
- /* @__PURE__ */ jsx(CornerDownLeft, { className: "h-3 w-3" })
2329
- ] })
2330
- ]
2331
- }
2332
- )
2333
- ] }, message.id);
2334
- }
2335
- if (message.kind === "bulkPreview" && message.csvData) {
2336
- return /* @__PURE__ */ jsxs("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: [
2337
- /* @__PURE__ */ jsx("div", { className: "text-sm leading-6 text-gray-700 mb-2", children: message.content }),
2338
- /* @__PURE__ */ jsxs("div", { className: "bg-gray-50 rounded-lg border border-gray-200 overflow-hidden", children: [
2339
- /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 border-b border-gray-200 flex items-center gap-2", children: [
2340
- /* @__PURE__ */ jsx(FileSpreadsheet, { className: "h-4 w-4 text-gray-500" }),
2341
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-700", children: message.csvData.fileName }),
2342
- /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-400 ml-auto", children: [
2343
- message.csvData.rowCount,
2344
- " rows"
2345
- ] })
2346
- ] }),
2347
- message.csvData.sampleRows && message.csvData.sampleRows.length > 0 && /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx("div", { className: "space-y-1", children: message.csvData.sampleRows.slice(0, 3).map((row, i) => /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-600 bg-white rounded px-2 py-1 border border-gray-100", children: [
2348
- Object.entries(row).slice(0, 3).map(([key, val], j) => /* @__PURE__ */ jsxs("span", { children: [
2349
- j > 0 && " \u2022 ",
2350
- /* @__PURE__ */ jsxs("span", { className: "text-gray-400", children: [
2351
- key,
2352
- ":"
2353
- ] }),
2354
- " ",
2355
- val
2356
- ] }, key)),
2357
- Object.keys(row).length > 3 && /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: " ..." })
2358
- ] }, i)) }) }),
2359
- message.suggestedAction && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 bg-blue-50 border-t border-blue-100", children: /* @__PURE__ */ jsxs("span", { className: "text-xs text-blue-700", children: [
2360
- "Suggested action: ",
2361
- /* @__PURE__ */ jsx("strong", { children: message.suggestedAction.replace(/_/g, " ") })
2362
- ] }) })
2363
- ] }),
2364
- message.bulkSessionId && /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center gap-2", children: [
2365
- /* @__PURE__ */ jsxs(
2366
- Button,
2367
- {
2368
- type: "button",
2369
- size: "sm",
2370
- variant: "secondary",
2371
- className: "h-8 rounded-xl px-3 text-xs gap-1.5 bg-gray-100 hover:bg-gray-200 border border-gray-200",
2372
- onClick: () => message.bulkSessionId && confirmBulkOperation(message.bulkSessionId),
2373
- children: [
2374
- /* @__PURE__ */ jsxs("span", { children: [
2375
- "Process ",
2376
- message.csvData.rowCount,
2377
- " rows"
2378
- ] }),
2379
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-0.5 text-gray-400", children: [
2380
- /* @__PURE__ */ jsx(Command, { className: "h-3 w-3" }),
2381
- /* @__PURE__ */ jsx(CornerDownLeft, { className: "h-3 w-3" })
2382
- ] })
2383
- ]
2384
- }
2385
- ),
2386
- /* @__PURE__ */ jsx(
2387
- Button,
2388
- {
2389
- type: "button",
2390
- size: "sm",
2391
- variant: "ghost",
2392
- className: "h-8 rounded-xl px-3 text-xs text-gray-500 hover:text-gray-700 hover:bg-gray-100",
2393
- onClick: cancelBulkOperation,
2394
- children: "Cancel"
2395
- }
2396
- )
2397
- ] })
2398
- ] }, message.id);
2399
- }
2400
- if (message.kind === "bulkProgress" && message.bulkProgress) {
2401
- const { processed, total, successes, failures } = message.bulkProgress;
2402
- const percentage = Math.round(processed / total * 100);
2403
- return /* @__PURE__ */ jsx("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: /* @__PURE__ */ jsxs("div", { className: "bg-gray-50 rounded-lg border border-gray-200 p-3", children: [
2404
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
2405
- /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-blue-600" }),
2406
- /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-gray-700", children: [
2407
- "Processing... ",
2408
- processed,
2409
- " of ",
2410
- total
2411
- ] })
2412
- ] }),
2413
- /* @__PURE__ */ jsx("div", { className: "h-2 bg-gray-200 rounded-full overflow-hidden mb-2", children: /* @__PURE__ */ jsx(
2414
- "div",
2415
- {
2416
- className: "h-full bg-blue-600 transition-all duration-300",
2417
- style: { width: `${percentage}%` }
2418
- }
2419
- ) }),
2420
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs text-gray-600", children: [
2421
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
2422
- /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3 w-3 text-green-600" }),
2423
- successes,
2424
- " successful"
2425
- ] }),
2426
- failures > 0 && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-red-600", children: [
2427
- /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }),
2428
- failures,
2429
- " failed"
2430
- ] })
2431
- ] })
2432
- ] }) }, message.id);
2433
- }
2434
- if (message.kind === "bulkSummary" && message.bulkSummary) {
2435
- const { total, successes, failures, navigationPage } = message.bulkSummary;
2436
- const hasFailures = failures.length > 0;
2437
- const pageLabel = navigationPage?.page === "customers" ? "Customers" : navigationPage?.page === "dashboard" ? "Dashboard" : navigationPage?.page === "settings" ? "Settings" : "Results";
2438
- return /* @__PURE__ */ jsx("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: /* @__PURE__ */ jsxs("div", { className: `rounded-lg border p-3 ${hasFailures ? "bg-amber-50 border-amber-200" : "bg-green-50 border-green-200"}`, children: [
2439
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
2440
- /* @__PURE__ */ jsx(CheckCircle2, { className: `h-5 w-5 ${hasFailures ? "text-amber-600" : "text-green-600"}` }),
2441
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-800", children: "Bulk operation complete" })
2442
- ] }),
2443
- /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-600 mb-2", children: message.content || `Processed ${total} rows: ${successes} successful${hasFailures ? `, ${failures.length} failed` : ""}.` }),
2444
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 text-xs", children: [
2445
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-green-700", children: [
2446
- /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3 w-3" }),
2447
- successes,
2448
- " successful"
2449
- ] }),
2450
- hasFailures && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-red-600", children: [
2451
- /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }),
2452
- failures.length,
2453
- " failed"
2454
- ] })
2455
- ] }),
2456
- hasFailures && /* @__PURE__ */ jsxs("div", { className: "mt-3 pt-2 border-t border-amber-200", children: [
2457
- /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wider text-amber-700 mb-1", children: "Failed Rows" }),
2458
- /* @__PURE__ */ jsxs("div", { className: "space-y-1 max-h-32 overflow-y-auto", children: [
2459
- failures.slice(0, 5).map((failure, i) => /* @__PURE__ */ jsxs("div", { className: "text-xs text-red-700 bg-red-50 rounded px-2 py-1", children: [
2460
- "Row ",
2461
- failure.row,
2462
- ": ",
2463
- failure.error || "Unknown error"
2464
- ] }, i)),
2465
- failures.length > 5 && /* @__PURE__ */ jsxs("div", { className: "text-xs text-amber-600", children: [
2466
- "...and ",
2467
- failures.length - 5,
2468
- " more"
2469
- ] })
2470
- ] })
2471
- ] }),
2472
- navigationPage && successes > 0 && /* @__PURE__ */ jsx("div", { className: "mt-3 pt-2 border-t border-gray-200", children: /* @__PURE__ */ jsxs(
2473
- "button",
2474
- {
2475
- type: "button",
2476
- onClick: () => {
2477
- if (onNavigate && navigationPage.page) {
2478
- onNavigate(navigationPage.page, navigationPage.subtab);
2479
- }
2480
- },
2481
- className: "flex items-center gap-2 text-xs text-gray-500 hover:text-gray-700 transition-colors group cursor-pointer",
2482
- children: [
2483
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 px-1.5 py-0.5 bg-gray-100 rounded text-[10px] font-medium text-gray-600 group-hover:bg-gray-200", children: [
2484
- /* @__PURE__ */ jsx(Command, { className: "h-2.5 w-2.5" }),
2485
- /* @__PURE__ */ jsx("span", { children: "+" }),
2486
- /* @__PURE__ */ jsx(CornerDownLeft, { className: "h-2.5 w-2.5" })
2487
- ] }),
2488
- /* @__PURE__ */ jsxs("span", { children: [
2489
- "View ",
2490
- pageLabel
2491
- ] })
2492
- ]
2493
- }
2494
- ) })
2495
- ] }) }, message.id);
2496
- }
2497
- return /* @__PURE__ */ jsx(React4.Fragment, { children: /* @__PURE__ */ jsxs(
2498
- "div",
2499
- {
2500
- ref: isCurrentGuideStep ? currentStepRef : null,
2501
- className: `${isRoleChange ? "mt-3" : ""}`,
2502
- children: [
2503
- /* @__PURE__ */ jsx("div", { className: "text-sm leading-6 text-gray-700", children: (() => {
2504
- const text = message.content || "";
2505
- if (message.kind === "guideStep") {
2506
- const m = text.match(/^(Step\s+\d+:)([\s\S]*)/);
2507
- if (m) {
2508
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2509
- /* @__PURE__ */ jsx("strong", { children: m[1] }),
2510
- m[2]
2511
- ] });
2512
- }
2513
- }
2514
- if (message.role === "assistant" && text) {
2515
- return renderMarkdown(text);
2516
- }
2517
- return text || /* @__PURE__ */ jsx("span", { className: "opacity-70", children: "Thinking\u2026" });
2518
- })() }),
2519
- message.role === "assistant" && message.structuredData && /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
2520
- DataRenderer,
2521
- {
2522
- type: message.structuredData.type,
2523
- data: message.structuredData.data
2524
- }
2525
- ) }),
2526
- message.role === "assistant" && message.followups && message.followups.length > 0 && !message.followupSelected && /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-1.5", children: message.followups.map((followup) => /* @__PURE__ */ jsx(
2527
- "button",
2528
- {
2529
- type: "button",
2530
- onClick: () => handleFollowupClick(message.id, followup),
2531
- className: "inline-flex items-center px-3 py-1.5 text-xs font-medium rounded-xl\n bg-white hover:bg-gray-50 text-gray-600 hover:text-gray-800\n border border-gray-200 hover:border-gray-300 shadow-sm\n transition-all duration-150 ease-in-out",
2532
- children: followup.label
2533
- },
2534
- followup.id
2535
- )) })
2536
- ]
2537
- }
2538
- ) }, message.id);
2539
- }),
2540
- (activeGuide || guideComplete) && /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center gap-2", children: [
2541
- activeGuide && activeGuide.stepIndex > 0 && /* @__PURE__ */ jsx(
2542
- Button,
2543
- {
2544
- type: "button",
2545
- size: "sm",
2546
- variant: "secondary",
2547
- className: "h-7 w-7 rounded-full p-0 bg-gray-100 hover:bg-gray-200 border border-gray-200",
2548
- onClick: goBackGuide,
2549
- children: /* @__PURE__ */ jsx(ArrowLeft, { className: "h-3.5 w-3.5 text-gray-600" })
2550
- }
2551
- ),
2552
- /* @__PURE__ */ jsxs(
2553
- Button,
2554
- {
2555
- type: "button",
2556
- size: "sm",
2557
- variant: "secondary",
2558
- className: "h-8 rounded-xl px-3 text-xs gap-1.5 bg-gray-100 hover:bg-gray-200 border border-gray-200",
2559
- onClick: guideComplete ? handleBack : advanceGuide,
2560
- children: [
2561
- /* @__PURE__ */ jsx("span", { children: guideComplete ? "Done" : "Next" }),
2562
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-0.5 text-gray-400", children: [
2563
- /* @__PURE__ */ jsx(Command, { className: "h-3 w-3" }),
2564
- /* @__PURE__ */ jsx(CornerDownLeft, { className: "h-3 w-3" })
2565
- ] })
2566
- ]
2567
- }
2568
- )
2569
- ] }),
2570
- (phase === "thinking" || phase === "searching" || phase === "executing" || phase === "responding") && /* @__PURE__ */ jsx("div", { className: `${lastRole === "user" ? "mt-3" : ""}`, children: /* @__PURE__ */ jsx(
2571
- AssistantActivity,
2572
- {
2573
- phase,
2574
- progressSteps
2575
- }
2576
- ) }),
2577
- !activeGuide && /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
2578
- ] }) }) }) }) }),
2579
- !isCollapsed && /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 border-t border-gray-100 bg-gray-50/50 shrink-0", children: [
2580
- pendingFile && /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2 rounded-xl bg-blue-50 border border-blue-200 px-3 py-2", children: [
2581
- /* @__PURE__ */ jsx(FileSpreadsheet, { className: "h-4 w-4 text-blue-600" }),
2582
- /* @__PURE__ */ jsx("span", { className: "text-xs text-blue-700 flex-1 truncate", children: pendingFile.name }),
2583
- /* @__PURE__ */ jsx(
2584
- "button",
2585
- {
2586
- type: "button",
2587
- onClick: () => {
2588
- setPendingFile(null);
2589
- if (fileInputRef.current) fileInputRef.current.value = "";
2590
- },
2591
- className: "text-blue-600 hover:text-blue-800",
2592
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
2593
- }
2594
- )
2595
- ] }),
2596
- /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "w-full", children: [
2597
- /* @__PURE__ */ jsx(
2598
- "input",
2599
- {
2600
- ref: fileInputRef,
2601
- type: "file",
2602
- accept: ".csv",
2603
- className: "hidden",
2604
- onChange: (e) => {
2605
- const file = e.target.files?.[0];
2606
- if (file) {
2607
- setPendingFile(file);
2608
- }
2609
- }
2610
- }
2611
- ),
2612
- /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-2 rounded-2xl border border-gray-200 bg-white px-3 py-2 shadow-sm", children: [
2613
- /* @__PURE__ */ jsx(
2614
- Button,
2615
- {
2616
- type: "button",
2617
- size: "icon",
2618
- variant: "ghost",
2619
- onClick: () => fileInputRef.current?.click(),
2620
- className: "h-8 w-8 rounded-full text-gray-400 hover:text-gray-600 hover:bg-gray-100",
2621
- title: "Upload CSV for bulk operations",
2622
- children: /* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4" })
2623
- }
2624
- ),
2625
- /* @__PURE__ */ jsx(
2626
- Input,
2627
- {
2628
- placeholder: pendingFile ? "Describe what to do with this CSV..." : "Ask anything...",
2629
- value: input,
2630
- onChange: (e) => setInput(e.target.value),
2631
- className: "flex-1 border-0 bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 text-sm placeholder:text-gray-400"
2632
- }
2633
- ),
2634
- /* @__PURE__ */ jsx(
2635
- Button,
2636
- {
2637
- type: "submit",
2638
- size: "icon",
2639
- disabled: !input.trim() && !pendingFile,
2640
- className: "h-8 w-8 rounded-full bg-gray-900 hover:bg-gray-800 disabled:bg-gray-300",
2641
- children: /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4" })
2642
- }
2643
- )
2644
- ] })
2645
- ] })
2646
- ] }),
2647
- enableGuideCursor && /* @__PURE__ */ jsx(
2648
- GuideCursor,
2649
- {
2650
- x: cursorState.x,
2651
- y: cursorState.y,
2652
- visible: cursorState.visible,
2653
- onClick: cursorState.onClick
2654
- }
2655
- )
2656
- ] });
2657
- }
2658
- function AssistantSearchSummary({
2659
- title,
2660
- links
2661
- }) {
2662
- const [open, setOpen] = React4.useState(false);
2663
- return /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
2664
- /* @__PURE__ */ jsx(
2665
- "button",
2666
- {
2667
- type: "button",
2668
- onClick: () => setOpen((v) => !v),
2669
- className: "flex w-full items-center gap-2 px-0 py-0",
2670
- "aria-expanded": open,
2671
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
2672
- open ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 opacity-70" }) : /* @__PURE__ */ jsx(Search, { className: "h-3 w-3 opacity-70" }),
2673
- /* @__PURE__ */ jsx("span", { children: title })
2674
- ] })
2675
- }
2676
- ),
2677
- open && links.length > 0 && /* @__PURE__ */ jsx("ul", { className: "ml-5 mt-1 space-y-0.5", children: links.map((link) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
2678
- "a",
2679
- {
2680
- href: link.href,
2681
- target: "_blank",
2682
- rel: "noreferrer",
2683
- className: "text-primary hover:underline",
2684
- children: link.label
2685
- }
2686
- ) }, link.href)) })
2687
- ] });
2688
- }
2689
-
2690
- export { AssistantActivity, AssistantSearchSummary, Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatPanel, ChatPanelProvider, DataRenderer, GuideCursor, Input, ScrollArea, ScrollBar, buttonVariants, cn, defaultConfig, defaultFolders, defaultGuides, useChatPanelConfig, useGuideCursor };
2691
- //# sourceMappingURL=index.mjs.map
2692
- //# sourceMappingURL=index.mjs.map