@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/README.md +186 -9
- package/dist/auto.cjs +3638 -0
- package/dist/auto.d.cts +60 -0
- package/dist/auto.d.ts +60 -0
- package/dist/auto.js +23 -0
- package/dist/chunk-R73Y24JS.js +3665 -0
- package/dist/createKiteChat-CFo7NUHz.d.cts +254 -0
- package/dist/createKiteChat-CFo7NUHz.d.ts +254 -0
- package/dist/embed.global.js +22 -23
- package/dist/index.cjs +3718 -0
- package/dist/index.d.cts +179 -0
- package/dist/index.d.ts +90 -287
- package/dist/index.js +67 -2737
- package/dist/styles.css +1 -1
- package/package.json +34 -23
- package/dist/index.d.mts +0 -376
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -2692
- package/dist/index.mjs.map +0 -1
package/dist/auto.cjs
ADDED
|
@@ -0,0 +1,3638 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/auto.ts
|
|
31
|
+
var auto_exports = {};
|
|
32
|
+
__export(auto_exports, {
|
|
33
|
+
mountKiteChat: () => mountKiteChat
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(auto_exports);
|
|
36
|
+
|
|
37
|
+
// src/createKiteChat.tsx
|
|
38
|
+
var import_react = __toESM(require("react"), 1);
|
|
39
|
+
var import_client = require("react-dom/client");
|
|
40
|
+
|
|
41
|
+
// src/ChatPanel.tsx
|
|
42
|
+
var React4 = __toESM(require("react"), 1);
|
|
43
|
+
|
|
44
|
+
// src/lib/utils.ts
|
|
45
|
+
var import_clsx = require("clsx");
|
|
46
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
47
|
+
function cn(...inputs) {
|
|
48
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/components/ui/input.tsx
|
|
52
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
53
|
+
function Input({ className, type, ...props }) {
|
|
54
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
55
|
+
"input",
|
|
56
|
+
{
|
|
57
|
+
type,
|
|
58
|
+
"data-slot": "input",
|
|
59
|
+
className: cn(
|
|
60
|
+
"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",
|
|
61
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
62
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
63
|
+
className
|
|
64
|
+
),
|
|
65
|
+
...props
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/components/ui/button.tsx
|
|
71
|
+
var import_react_slot = require("@radix-ui/react-slot");
|
|
72
|
+
var import_class_variance_authority = require("class-variance-authority");
|
|
73
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
74
|
+
var buttonVariants = (0, import_class_variance_authority.cva)(
|
|
75
|
+
"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",
|
|
76
|
+
{
|
|
77
|
+
variants: {
|
|
78
|
+
variant: {
|
|
79
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
80
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
81
|
+
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",
|
|
82
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
83
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
84
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
85
|
+
},
|
|
86
|
+
size: {
|
|
87
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
88
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
89
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
90
|
+
icon: "size-9",
|
|
91
|
+
"icon-sm": "size-8",
|
|
92
|
+
"icon-lg": "size-10"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
defaultVariants: {
|
|
96
|
+
variant: "default",
|
|
97
|
+
size: "default"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
function Button({
|
|
102
|
+
className,
|
|
103
|
+
variant,
|
|
104
|
+
size,
|
|
105
|
+
asChild = false,
|
|
106
|
+
...props
|
|
107
|
+
}) {
|
|
108
|
+
const Comp = asChild ? import_react_slot.Slot : "button";
|
|
109
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
110
|
+
Comp,
|
|
111
|
+
{
|
|
112
|
+
"data-slot": "button",
|
|
113
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
114
|
+
...props
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/components/ui/scroll-area.tsx
|
|
120
|
+
var React = __toESM(require("react"), 1);
|
|
121
|
+
var ScrollAreaPrimitive = __toESM(require("@radix-ui/react-scroll-area"), 1);
|
|
122
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
123
|
+
var ScrollArea = React.forwardRef(({ className, children, ...props }, ref) => {
|
|
124
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
125
|
+
ScrollAreaPrimitive.Root,
|
|
126
|
+
{
|
|
127
|
+
"data-slot": "scroll-area",
|
|
128
|
+
className: cn("relative", className),
|
|
129
|
+
...props,
|
|
130
|
+
children: [
|
|
131
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
132
|
+
ScrollAreaPrimitive.Viewport,
|
|
133
|
+
{
|
|
134
|
+
ref,
|
|
135
|
+
"data-slot": "scroll-area-viewport",
|
|
136
|
+
className: "focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1",
|
|
137
|
+
children
|
|
138
|
+
}
|
|
139
|
+
),
|
|
140
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ScrollBar, {}),
|
|
141
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ScrollAreaPrimitive.Corner, {})
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
ScrollArea.displayName = "ScrollArea";
|
|
147
|
+
function ScrollBar({
|
|
148
|
+
className,
|
|
149
|
+
orientation = "vertical",
|
|
150
|
+
...props
|
|
151
|
+
}) {
|
|
152
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
153
|
+
ScrollAreaPrimitive.ScrollAreaScrollbar,
|
|
154
|
+
{
|
|
155
|
+
"data-slot": "scroll-area-scrollbar",
|
|
156
|
+
orientation,
|
|
157
|
+
className: cn(
|
|
158
|
+
"flex touch-none p-px transition-colors select-none",
|
|
159
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
|
|
160
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
|
|
161
|
+
className
|
|
162
|
+
),
|
|
163
|
+
...props,
|
|
164
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
165
|
+
ScrollAreaPrimitive.ScrollAreaThumb,
|
|
166
|
+
{
|
|
167
|
+
"data-slot": "scroll-area-thumb",
|
|
168
|
+
className: "bg-border relative flex-1 rounded-full"
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/ChatPanel.tsx
|
|
176
|
+
var import_lucide_react4 = require("lucide-react");
|
|
177
|
+
|
|
178
|
+
// src/components/AssistantActivity.tsx
|
|
179
|
+
var import_lucide_react = require("lucide-react");
|
|
180
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
181
|
+
function AssistantActivity({
|
|
182
|
+
phase,
|
|
183
|
+
progressSteps = []
|
|
184
|
+
}) {
|
|
185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mb-3 rounded-lg bg-muted/40 px-3 py-3 text-xs text-muted-foreground transition-colors", children: [
|
|
186
|
+
phase === "thinking" && progressSteps.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
187
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
188
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Thinking\u2026" })
|
|
189
|
+
] }),
|
|
190
|
+
phase === "searching" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-2 animate-in fade-in-0 duration-300", children: [
|
|
191
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
192
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Search, { className: "h-3.5 w-3.5 opacity-70" }),
|
|
193
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Searching across pages\u2026" })
|
|
194
|
+
] }),
|
|
195
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("ul", { className: "ml-6 list-disc space-y-1", children: [
|
|
196
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { className: "opacity-90", children: "Checking recent changes" }),
|
|
197
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { className: "opacity-90", children: "Scanning documentation" }),
|
|
198
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { className: "opacity-90", children: "Summarizing relevant sections" })
|
|
199
|
+
] })
|
|
200
|
+
] }),
|
|
201
|
+
(phase === "thinking" || phase === "executing" || phase === "responding") && progressSteps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-2 animate-in fade-in-0 duration-200", children: [
|
|
202
|
+
progressSteps.map((step, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
203
|
+
"div",
|
|
204
|
+
{
|
|
205
|
+
className: `flex items-center gap-2 transition-opacity duration-200 ${step.completed ? "opacity-60" : "opacity-100"}`,
|
|
206
|
+
children: [
|
|
207
|
+
step.completed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.CheckCircle2, { className: "h-3.5 w-3.5 text-green-500 flex-shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Loader2, { className: "h-3.5 w-3.5 animate-spin flex-shrink-0" }),
|
|
208
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: step.completed ? "line-through" : "", children: step.message })
|
|
209
|
+
]
|
|
210
|
+
},
|
|
211
|
+
index
|
|
212
|
+
)),
|
|
213
|
+
phase === "responding" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2 opacity-100 animate-in fade-in-0 duration-200", children: [
|
|
214
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Loader2, { className: "h-3.5 w-3.5 animate-spin flex-shrink-0" }),
|
|
215
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Preparing response..." })
|
|
216
|
+
] })
|
|
217
|
+
] })
|
|
218
|
+
] });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/components/AssistantSearchSummary.tsx
|
|
222
|
+
var React2 = __toESM(require("react"), 1);
|
|
223
|
+
var import_lucide_react2 = require("lucide-react");
|
|
224
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
225
|
+
function AssistantSearchSummary({
|
|
226
|
+
title,
|
|
227
|
+
links
|
|
228
|
+
}) {
|
|
229
|
+
const [open, setOpen] = React2.useState(false);
|
|
230
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-xs text-muted-foreground", children: [
|
|
231
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
232
|
+
"button",
|
|
233
|
+
{
|
|
234
|
+
type: "button",
|
|
235
|
+
onClick: () => setOpen((v) => !v),
|
|
236
|
+
className: "flex w-full items-center gap-2 px-0 py-0",
|
|
237
|
+
"aria-expanded": open,
|
|
238
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
239
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react2.ChevronDown, { className: "h-3 w-3 opacity-70" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react2.Search, { className: "h-3 w-3 opacity-70" }),
|
|
240
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: title })
|
|
241
|
+
] })
|
|
242
|
+
}
|
|
243
|
+
),
|
|
244
|
+
open && links.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "ml-5 mt-1 space-y-0.5", children: links.map((link) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
245
|
+
"a",
|
|
246
|
+
{
|
|
247
|
+
href: link.href,
|
|
248
|
+
target: "_blank",
|
|
249
|
+
rel: "noreferrer",
|
|
250
|
+
className: "text-primary hover:underline",
|
|
251
|
+
children: link.label
|
|
252
|
+
}
|
|
253
|
+
) }, link.href)) })
|
|
254
|
+
] });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/components/GuideCursor.tsx
|
|
258
|
+
var import_framer_motion = require("framer-motion");
|
|
259
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
260
|
+
function GuideCursor({ x, y, visible, onClick = false }) {
|
|
261
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_framer_motion.AnimatePresence, { children: visible && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
262
|
+
import_framer_motion.motion.div,
|
|
263
|
+
{
|
|
264
|
+
className: "fixed pointer-events-none z-[9999]",
|
|
265
|
+
initial: { opacity: 0, scale: 0.5 },
|
|
266
|
+
animate: {
|
|
267
|
+
opacity: 1,
|
|
268
|
+
scale: 1,
|
|
269
|
+
x,
|
|
270
|
+
y
|
|
271
|
+
},
|
|
272
|
+
exit: { opacity: 0, scale: 0.5 },
|
|
273
|
+
transition: {
|
|
274
|
+
type: "spring",
|
|
275
|
+
stiffness: 200,
|
|
276
|
+
damping: 20
|
|
277
|
+
},
|
|
278
|
+
style: {
|
|
279
|
+
left: 0,
|
|
280
|
+
top: 0
|
|
281
|
+
},
|
|
282
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
283
|
+
"svg",
|
|
284
|
+
{
|
|
285
|
+
width: "32",
|
|
286
|
+
height: "32",
|
|
287
|
+
viewBox: "0 0 24 24",
|
|
288
|
+
fill: "none",
|
|
289
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
290
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
291
|
+
"path",
|
|
292
|
+
{
|
|
293
|
+
d: "M3 3L10.07 19.97L12.58 12.58L19.97 10.07L3 3Z",
|
|
294
|
+
fill: "#C4B5FD"
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
) });
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/hooks/useGuideCursor.ts
|
|
304
|
+
var React3 = __toESM(require("react"), 1);
|
|
305
|
+
function useGuideCursor() {
|
|
306
|
+
const [cursorState, setCursorState] = React3.useState({
|
|
307
|
+
x: 0,
|
|
308
|
+
y: 0,
|
|
309
|
+
visible: false,
|
|
310
|
+
onClick: false
|
|
311
|
+
});
|
|
312
|
+
const moveTo = React3.useCallback((target) => {
|
|
313
|
+
const element = document.querySelector(target.selector);
|
|
314
|
+
if (element) {
|
|
315
|
+
const rect = element.getBoundingClientRect();
|
|
316
|
+
const offsetX = target.offset?.x || 0;
|
|
317
|
+
const offsetY = target.offset?.y || 0;
|
|
318
|
+
setCursorState({
|
|
319
|
+
x: rect.left + rect.width / 2 + offsetX,
|
|
320
|
+
y: rect.top + rect.height / 2 + offsetY,
|
|
321
|
+
visible: true,
|
|
322
|
+
onClick: target.onClick || false
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}, []);
|
|
326
|
+
const hide = React3.useCallback(() => {
|
|
327
|
+
setCursorState((prev) => ({ ...prev, visible: false }));
|
|
328
|
+
}, []);
|
|
329
|
+
const show = React3.useCallback(() => {
|
|
330
|
+
setCursorState((prev) => ({ ...prev, visible: true }));
|
|
331
|
+
}, []);
|
|
332
|
+
return {
|
|
333
|
+
cursorState,
|
|
334
|
+
moveTo,
|
|
335
|
+
hide,
|
|
336
|
+
show
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/components/ui/card.tsx
|
|
341
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
342
|
+
function Card({ className, ...props }) {
|
|
343
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
344
|
+
"div",
|
|
345
|
+
{
|
|
346
|
+
"data-slot": "card",
|
|
347
|
+
className: cn(
|
|
348
|
+
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
349
|
+
className
|
|
350
|
+
),
|
|
351
|
+
...props
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
function CardHeader({ className, ...props }) {
|
|
356
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
357
|
+
"div",
|
|
358
|
+
{
|
|
359
|
+
"data-slot": "card-header",
|
|
360
|
+
className: cn(
|
|
361
|
+
"@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",
|
|
362
|
+
className
|
|
363
|
+
),
|
|
364
|
+
...props
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
function CardTitle({ className, ...props }) {
|
|
369
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
370
|
+
"div",
|
|
371
|
+
{
|
|
372
|
+
"data-slot": "card-title",
|
|
373
|
+
className: cn("leading-none font-semibold", className),
|
|
374
|
+
...props
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
function CardDescription({ className, ...props }) {
|
|
379
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
380
|
+
"div",
|
|
381
|
+
{
|
|
382
|
+
"data-slot": "card-description",
|
|
383
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
384
|
+
...props
|
|
385
|
+
}
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
function CardContent({ className, ...props }) {
|
|
389
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
390
|
+
"div",
|
|
391
|
+
{
|
|
392
|
+
"data-slot": "card-content",
|
|
393
|
+
className: cn("px-6", className),
|
|
394
|
+
...props
|
|
395
|
+
}
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// src/components/ui/data-cards.tsx
|
|
400
|
+
var import_lucide_react3 = require("lucide-react");
|
|
401
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
402
|
+
var formatDate = (dateString) => {
|
|
403
|
+
const date = new Date(dateString);
|
|
404
|
+
return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
405
|
+
};
|
|
406
|
+
var formatCurrency = (amount, currency = "USD") => {
|
|
407
|
+
return new Intl.NumberFormat("en-US", {
|
|
408
|
+
style: "currency",
|
|
409
|
+
currency
|
|
410
|
+
}).format(amount);
|
|
411
|
+
};
|
|
412
|
+
var getPlanIcon = (plan) => {
|
|
413
|
+
switch (plan) {
|
|
414
|
+
case "Enterprise":
|
|
415
|
+
return import_lucide_react3.Crown;
|
|
416
|
+
case "Professional":
|
|
417
|
+
return import_lucide_react3.Zap;
|
|
418
|
+
default:
|
|
419
|
+
return import_lucide_react3.Activity;
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
var getPlanColor = (plan) => {
|
|
423
|
+
switch (plan) {
|
|
424
|
+
case "Enterprise":
|
|
425
|
+
return "bg-purple-50 text-purple-700 border-purple-200";
|
|
426
|
+
case "Professional":
|
|
427
|
+
return "bg-blue-50 text-blue-700 border-blue-200";
|
|
428
|
+
default:
|
|
429
|
+
return "bg-gray-50 text-gray-700 border-gray-200";
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
function StatusBadge({ status, type = "default" }) {
|
|
433
|
+
const getConfig = () => {
|
|
434
|
+
const statusLower = status.toLowerCase();
|
|
435
|
+
if (type === "transaction") {
|
|
436
|
+
switch (statusLower) {
|
|
437
|
+
case "paid":
|
|
438
|
+
return { color: "bg-emerald-50 text-emerald-700 border-emerald-200", icon: import_lucide_react3.CheckCircle2 };
|
|
439
|
+
case "failed":
|
|
440
|
+
return { color: "bg-red-50 text-red-700 border-red-200", icon: import_lucide_react3.XCircle };
|
|
441
|
+
case "pending":
|
|
442
|
+
return { color: "bg-amber-50 text-amber-700 border-amber-200", icon: import_lucide_react3.Clock };
|
|
443
|
+
case "refunded":
|
|
444
|
+
return { color: "bg-slate-50 text-slate-700 border-slate-200", icon: import_lucide_react3.RefreshCw };
|
|
445
|
+
default:
|
|
446
|
+
return { color: "bg-gray-50 text-gray-700 border-gray-200", icon: import_lucide_react3.AlertCircle };
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (type === "subscription") {
|
|
450
|
+
switch (statusLower) {
|
|
451
|
+
case "active":
|
|
452
|
+
return { color: "bg-emerald-50 text-emerald-700 border-emerald-200", icon: import_lucide_react3.CheckCircle2 };
|
|
453
|
+
case "cancelled":
|
|
454
|
+
return { color: "bg-slate-50 text-slate-700 border-slate-200", icon: import_lucide_react3.XCircle };
|
|
455
|
+
case "past_due":
|
|
456
|
+
return { color: "bg-red-50 text-red-700 border-red-200", icon: import_lucide_react3.AlertCircle };
|
|
457
|
+
case "trialing":
|
|
458
|
+
return { color: "bg-blue-50 text-blue-700 border-blue-200", icon: import_lucide_react3.Clock };
|
|
459
|
+
default:
|
|
460
|
+
return { color: "bg-gray-50 text-gray-700 border-gray-200", icon: import_lucide_react3.AlertCircle };
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
switch (statusLower) {
|
|
464
|
+
case "active":
|
|
465
|
+
return { color: "bg-emerald-50 text-emerald-700 border-emerald-200", icon: import_lucide_react3.CheckCircle2 };
|
|
466
|
+
case "inactive":
|
|
467
|
+
return { color: "bg-slate-50 text-slate-700 border-slate-200", icon: import_lucide_react3.XCircle };
|
|
468
|
+
default:
|
|
469
|
+
return { color: "bg-gray-50 text-gray-700 border-gray-200", icon: import_lucide_react3.AlertCircle };
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
const config = getConfig();
|
|
473
|
+
const Icon = config.icon;
|
|
474
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.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: [
|
|
475
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, { className: "h-3 w-3" }),
|
|
476
|
+
status
|
|
477
|
+
] });
|
|
478
|
+
}
|
|
479
|
+
function SubscriptionCard({ subscription }) {
|
|
480
|
+
const PlanIcon = getPlanIcon(subscription.plan);
|
|
481
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
|
|
482
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: `h-1 ${subscription.status === "active" ? "bg-emerald-500" : subscription.status === "past_due" ? "bg-red-500" : "bg-slate-300"}` }),
|
|
483
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardHeader, { className: "pb-3", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-start justify-between", children: [
|
|
484
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-1", children: [
|
|
485
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardTitle, { className: "text-base font-semibold text-gray-900 flex items-center gap-2", children: [
|
|
486
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.CreditCard, { className: "h-4 w-4 text-gray-500" }),
|
|
487
|
+
subscription.id
|
|
488
|
+
] }),
|
|
489
|
+
subscription.customer_email && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardDescription, { className: "text-sm text-gray-500 flex items-center gap-1.5", children: [
|
|
490
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Mail, { className: "h-3 w-3" }),
|
|
491
|
+
subscription.customer_email
|
|
492
|
+
] })
|
|
493
|
+
] }),
|
|
494
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusBadge, { status: subscription.status, type: "subscription" })
|
|
495
|
+
] }) }),
|
|
496
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardContent, { className: "pt-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
497
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-3", children: [
|
|
498
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
499
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500 mb-1", children: "Plan" }),
|
|
500
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.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: [
|
|
501
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PlanIcon, { className: "h-3 w-3" }),
|
|
502
|
+
subscription.plan
|
|
503
|
+
] })
|
|
504
|
+
] }),
|
|
505
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
506
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500 mb-1", children: "Billing" }),
|
|
507
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-sm font-medium text-gray-900", children: [
|
|
508
|
+
formatCurrency(subscription.amount, subscription.currency),
|
|
509
|
+
" / ",
|
|
510
|
+
subscription.billing_cycle
|
|
511
|
+
] })
|
|
512
|
+
] })
|
|
513
|
+
] }),
|
|
514
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-3", children: [
|
|
515
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
516
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500 mb-1", children: "Current Period" }),
|
|
517
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-sm text-gray-700", children: [
|
|
518
|
+
formatDate(subscription.current_period_start),
|
|
519
|
+
" - ",
|
|
520
|
+
formatDate(subscription.current_period_end)
|
|
521
|
+
] })
|
|
522
|
+
] }),
|
|
523
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
524
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500 mb-1", children: "Created" }),
|
|
525
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm text-gray-700", children: formatDate(subscription.created_at) })
|
|
526
|
+
] })
|
|
527
|
+
] })
|
|
528
|
+
] }) })
|
|
529
|
+
] });
|
|
530
|
+
}
|
|
531
|
+
function SubscriptionList({ subscriptions, maxItems = 5 }) {
|
|
532
|
+
const displaySubs = subscriptions.slice(0, maxItems);
|
|
533
|
+
const hasMore = subscriptions.length > maxItems;
|
|
534
|
+
if (subscriptions.length === 0) {
|
|
535
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { className: "py-8 text-center", children: [
|
|
536
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.CreditCard, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
|
|
537
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-gray-500", children: "No subscriptions found" })
|
|
538
|
+
] }) });
|
|
539
|
+
}
|
|
540
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
|
|
541
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
|
|
542
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.CreditCard, { className: "h-4 w-4" }),
|
|
543
|
+
subscriptions.length,
|
|
544
|
+
" Subscription",
|
|
545
|
+
subscriptions.length !== 1 ? "s" : ""
|
|
546
|
+
] }) }),
|
|
547
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { className: "p-0", children: [
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "divide-y divide-gray-100", children: displaySubs.map((sub) => {
|
|
549
|
+
const PlanIcon = getPlanIcon(sub.plan);
|
|
550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
551
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
552
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: `p-2 rounded-lg ${getPlanColor(sub.plan).replace("text-", "text-").replace("border-", "bg-").split(" ")[0]}`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PlanIcon, { className: "h-4 w-4" }) }),
|
|
553
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "min-w-0", children: [
|
|
554
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm font-medium text-gray-900 truncate", children: sub.customer_email || sub.customer_id }),
|
|
555
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-xs text-gray-500", children: [
|
|
556
|
+
sub.plan,
|
|
557
|
+
" \xB7 ",
|
|
558
|
+
formatCurrency(sub.amount, sub.currency),
|
|
559
|
+
"/",
|
|
560
|
+
sub.billing_cycle
|
|
561
|
+
] })
|
|
562
|
+
] })
|
|
563
|
+
] }),
|
|
564
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusBadge, { status: sub.status, type: "subscription" })
|
|
565
|
+
] }) }, sub.id);
|
|
566
|
+
}) }),
|
|
567
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 py-2 bg-gray-50 border-t border-gray-100 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-xs text-gray-500", children: [
|
|
568
|
+
"+",
|
|
569
|
+
subscriptions.length - maxItems,
|
|
570
|
+
" more subscription",
|
|
571
|
+
subscriptions.length - maxItems !== 1 ? "s" : ""
|
|
572
|
+
] }) })
|
|
573
|
+
] })
|
|
574
|
+
] });
|
|
575
|
+
}
|
|
576
|
+
function CustomerCard({ customer }) {
|
|
577
|
+
const PlanIcon = getPlanIcon(customer.subscription);
|
|
578
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
|
|
579
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: `h-1 ${customer.status === "active" ? "bg-emerald-500" : "bg-slate-300"}` }),
|
|
580
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardHeader, { className: "pb-3", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-start justify-between", children: [
|
|
581
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
582
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardTitle, { className: "text-base font-semibold text-gray-900", children: customer.name }),
|
|
583
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardDescription, { className: "text-sm text-gray-500 flex items-center gap-1", children: [
|
|
584
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Mail, { className: "h-3 w-3" }),
|
|
585
|
+
customer.email
|
|
586
|
+
] })
|
|
587
|
+
] }) }),
|
|
588
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusBadge, { status: customer.status, type: "customer" })
|
|
589
|
+
] }) }),
|
|
590
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardContent, { className: "pt-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
591
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-3", children: [
|
|
592
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
593
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500 mb-1", children: "Plan" }),
|
|
594
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.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: [
|
|
595
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PlanIcon, { className: "h-3 w-3" }),
|
|
596
|
+
customer.subscription
|
|
597
|
+
] })
|
|
598
|
+
] }),
|
|
599
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
600
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500 mb-1", children: "Revenue" }),
|
|
601
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-sm font-medium text-gray-900 flex items-center gap-1", children: [
|
|
602
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.DollarSign, { className: "h-3 w-3 text-emerald-500" }),
|
|
603
|
+
customer.revenue
|
|
604
|
+
] })
|
|
605
|
+
] })
|
|
606
|
+
] }),
|
|
607
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-3", children: [
|
|
608
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
609
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500 mb-1", children: "Location" }),
|
|
610
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-sm text-gray-700 flex items-center gap-1", children: [
|
|
611
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.MapPin, { className: "h-3 w-3 text-gray-400" }),
|
|
612
|
+
customer.location || "Not specified"
|
|
613
|
+
] })
|
|
614
|
+
] }),
|
|
615
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
616
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500 mb-1", children: "Customer Since" }),
|
|
617
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-sm text-gray-700 flex items-center gap-1", children: [
|
|
618
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Calendar, { className: "h-3 w-3 text-gray-400" }),
|
|
619
|
+
customer.joinDate
|
|
620
|
+
] })
|
|
621
|
+
] })
|
|
622
|
+
] })
|
|
623
|
+
] }) })
|
|
624
|
+
] });
|
|
625
|
+
}
|
|
626
|
+
function CustomerList({ customers, maxItems = 5 }) {
|
|
627
|
+
const displayCustomers = customers.slice(0, maxItems);
|
|
628
|
+
const hasMore = customers.length > maxItems;
|
|
629
|
+
if (customers.length === 0) {
|
|
630
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { className: "py-8 text-center", children: [
|
|
631
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Users, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
|
|
632
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-gray-500", children: "No customers found" })
|
|
633
|
+
] }) });
|
|
634
|
+
}
|
|
635
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
|
|
636
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
|
|
637
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Users, { className: "h-4 w-4" }),
|
|
638
|
+
customers.length,
|
|
639
|
+
" Customer",
|
|
640
|
+
customers.length !== 1 ? "s" : ""
|
|
641
|
+
] }) }),
|
|
642
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { className: "p-0", children: [
|
|
643
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "divide-y divide-gray-100", children: displayCustomers.map((customer) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
644
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center gap-3 min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "min-w-0", children: [
|
|
645
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm font-medium text-gray-900 truncate", children: customer.name }),
|
|
646
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500 truncate", children: customer.email })
|
|
647
|
+
] }) }),
|
|
648
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
649
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `px-2 py-0.5 rounded text-xs font-medium ${getPlanColor(customer.subscription)}`, children: customer.subscription }),
|
|
650
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusBadge, { status: customer.status, type: "customer" })
|
|
651
|
+
] })
|
|
652
|
+
] }) }, customer.id)) }),
|
|
653
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 py-2 bg-gray-50 border-t border-gray-100 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-xs text-gray-500", children: [
|
|
654
|
+
"+",
|
|
655
|
+
customers.length - maxItems,
|
|
656
|
+
" more customer",
|
|
657
|
+
customers.length - maxItems !== 1 ? "s" : ""
|
|
658
|
+
] }) })
|
|
659
|
+
] })
|
|
660
|
+
] });
|
|
661
|
+
}
|
|
662
|
+
function TransactionList({ transactions, maxItems = 5 }) {
|
|
663
|
+
const displayTransactions = transactions.slice(0, maxItems);
|
|
664
|
+
const hasMore = transactions.length > maxItems;
|
|
665
|
+
if (transactions.length === 0) {
|
|
666
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { className: "py-8 text-center", children: [
|
|
667
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.DollarSign, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
|
|
668
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-gray-500", children: "No transactions found" })
|
|
669
|
+
] }) });
|
|
670
|
+
}
|
|
671
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
|
|
672
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
|
|
673
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.DollarSign, { className: "h-4 w-4" }),
|
|
674
|
+
transactions.length,
|
|
675
|
+
" Transaction",
|
|
676
|
+
transactions.length !== 1 ? "s" : ""
|
|
677
|
+
] }) }),
|
|
678
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { className: "p-0", children: [
|
|
679
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "divide-y divide-gray-100", children: displayTransactions.map((tx) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
680
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
681
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.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__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.CheckCircle2, { className: "h-4 w-4 text-emerald-600" }) : tx.status === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.XCircle, { className: "h-4 w-4 text-red-600" }) : tx.status === "refunded" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.RefreshCw, { className: "h-4 w-4 text-slate-600" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Clock, { className: "h-4 w-4 text-amber-600" }) }),
|
|
682
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "min-w-0", children: [
|
|
683
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm font-medium text-gray-900 truncate", children: tx.customer }),
|
|
684
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-xs text-gray-500", children: [
|
|
685
|
+
tx.date,
|
|
686
|
+
tx.decline_reason && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-red-500 ml-2", children: [
|
|
687
|
+
"\xB7 ",
|
|
688
|
+
tx.decline_reason
|
|
689
|
+
] })
|
|
690
|
+
] })
|
|
691
|
+
] })
|
|
692
|
+
] }),
|
|
693
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
694
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.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: [
|
|
695
|
+
tx.status === "refunded" ? "-" : "",
|
|
696
|
+
tx.amount
|
|
697
|
+
] }),
|
|
698
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusBadge, { status: tx.status, type: "transaction" })
|
|
699
|
+
] })
|
|
700
|
+
] }) }, tx.id)) }),
|
|
701
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 py-2 bg-gray-50 border-t border-gray-100 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-xs text-gray-500", children: [
|
|
702
|
+
"+",
|
|
703
|
+
transactions.length - maxItems,
|
|
704
|
+
" more transaction",
|
|
705
|
+
transactions.length - maxItems !== 1 ? "s" : ""
|
|
706
|
+
] }) })
|
|
707
|
+
] })
|
|
708
|
+
] });
|
|
709
|
+
}
|
|
710
|
+
function ApiKeyList({ apiKeys }) {
|
|
711
|
+
if (apiKeys.length === 0) {
|
|
712
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { className: "py-8 text-center", children: [
|
|
713
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Key, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
|
|
714
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-gray-500", children: "No API keys found" })
|
|
715
|
+
] }) });
|
|
716
|
+
}
|
|
717
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
|
|
718
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
|
|
719
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Key, { className: "h-4 w-4" }),
|
|
720
|
+
apiKeys.length,
|
|
721
|
+
" API Key",
|
|
722
|
+
apiKeys.length !== 1 ? "s" : ""
|
|
723
|
+
] }) }),
|
|
724
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardContent, { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "divide-y divide-gray-100", children: apiKeys.map((key, idx) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
725
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "min-w-0", children: [
|
|
726
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm font-medium text-gray-900", children: key.name }),
|
|
727
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-xs text-gray-500 font-mono", children: [
|
|
728
|
+
key.key.slice(0, 12),
|
|
729
|
+
"...",
|
|
730
|
+
key.key.slice(-4)
|
|
731
|
+
] })
|
|
732
|
+
] }),
|
|
733
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-xs text-gray-500", children: [
|
|
734
|
+
"Last used: ",
|
|
735
|
+
key.lastUsed
|
|
736
|
+
] })
|
|
737
|
+
] }) }, idx)) }) })
|
|
738
|
+
] });
|
|
739
|
+
}
|
|
740
|
+
function SessionList({ sessions }) {
|
|
741
|
+
const getDeviceIcon = (device) => {
|
|
742
|
+
const deviceLower = device.toLowerCase();
|
|
743
|
+
if (deviceLower.includes("mobile") || deviceLower.includes("iphone") || deviceLower.includes("android")) {
|
|
744
|
+
return import_lucide_react3.Smartphone;
|
|
745
|
+
}
|
|
746
|
+
return import_lucide_react3.Laptop;
|
|
747
|
+
};
|
|
748
|
+
if (sessions.length === 0) {
|
|
749
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { className: "py-8 text-center", children: [
|
|
750
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Shield, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
|
|
751
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-gray-500", children: "No active sessions" })
|
|
752
|
+
] }) });
|
|
753
|
+
}
|
|
754
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
|
|
755
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
|
|
756
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Shield, { className: "h-4 w-4" }),
|
|
757
|
+
sessions.length,
|
|
758
|
+
" Active Session",
|
|
759
|
+
sessions.length !== 1 ? "s" : ""
|
|
760
|
+
] }) }),
|
|
761
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardContent, { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "divide-y divide-gray-100", children: sessions.map((session, idx) => {
|
|
762
|
+
const DeviceIcon = getDeviceIcon(session.device);
|
|
763
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
764
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
765
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "p-2 rounded-lg bg-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DeviceIcon, { className: "h-4 w-4 text-gray-600" }) }),
|
|
766
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
767
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm font-medium text-gray-900", children: session.device }),
|
|
768
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-xs text-gray-500 flex items-center gap-1", children: [
|
|
769
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.MapPin, { className: "h-3 w-3" }),
|
|
770
|
+
session.location
|
|
771
|
+
] })
|
|
772
|
+
] })
|
|
773
|
+
] }),
|
|
774
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500", children: session.time })
|
|
775
|
+
] }) }, idx);
|
|
776
|
+
}) }) })
|
|
777
|
+
] });
|
|
778
|
+
}
|
|
779
|
+
function DashboardStatsCard({ stats }) {
|
|
780
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
|
|
781
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
|
|
782
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.TrendingUp, { className: "h-4 w-4" }),
|
|
783
|
+
"Dashboard Overview"
|
|
784
|
+
] }) }),
|
|
785
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardContent, { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
786
|
+
stats.revenue && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-3 rounded-lg bg-emerald-50 border border-emerald-100", children: [
|
|
787
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-emerald-600 font-medium mb-1", children: "Revenue" }),
|
|
788
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-lg font-bold text-emerald-700", children: formatCurrency(stats.revenue.total) }),
|
|
789
|
+
stats.revenue.change !== 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: `text-xs ${stats.revenue.change > 0 ? "text-emerald-600" : "text-red-600"}`, children: [
|
|
790
|
+
stats.revenue.change > 0 ? "+" : "",
|
|
791
|
+
stats.revenue.change,
|
|
792
|
+
"% from last period"
|
|
793
|
+
] })
|
|
794
|
+
] }),
|
|
795
|
+
stats.subscriptions && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-3 rounded-lg bg-blue-50 border border-blue-100", children: [
|
|
796
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-blue-600 font-medium mb-1", children: "Subscriptions" }),
|
|
797
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-lg font-bold text-blue-700", children: [
|
|
798
|
+
stats.subscriptions.active,
|
|
799
|
+
" active"
|
|
800
|
+
] }),
|
|
801
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-xs text-blue-600", children: [
|
|
802
|
+
stats.subscriptions.total,
|
|
803
|
+
" total"
|
|
804
|
+
] })
|
|
805
|
+
] }),
|
|
806
|
+
stats.customers && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-3 rounded-lg bg-purple-50 border border-purple-100", children: [
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-purple-600 font-medium mb-1", children: "Customers" }),
|
|
808
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-lg font-bold text-purple-700", children: stats.customers.total }),
|
|
809
|
+
stats.customers.new > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-xs text-purple-600", children: [
|
|
810
|
+
"+",
|
|
811
|
+
stats.customers.new,
|
|
812
|
+
" new this period"
|
|
813
|
+
] })
|
|
814
|
+
] }),
|
|
815
|
+
stats.transactions && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-3 rounded-lg bg-amber-50 border border-amber-100", children: [
|
|
816
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-amber-600 font-medium mb-1", children: "Transactions" }),
|
|
817
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-lg font-bold text-amber-700", children: [
|
|
818
|
+
stats.transactions.successful,
|
|
819
|
+
" successful"
|
|
820
|
+
] }),
|
|
821
|
+
stats.transactions.failed > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-xs text-red-600", children: [
|
|
822
|
+
stats.transactions.failed,
|
|
823
|
+
" failed"
|
|
824
|
+
] })
|
|
825
|
+
] })
|
|
826
|
+
] }) })
|
|
827
|
+
] });
|
|
828
|
+
}
|
|
829
|
+
function TopCustomersList({ customers }) {
|
|
830
|
+
if (customers.length === 0) {
|
|
831
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Card, { className: "border-gray-200 shadow-sm bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardContent, { className: "py-8 text-center", children: [
|
|
832
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Crown, { className: "h-10 w-10 text-gray-300 mx-auto mb-3" }),
|
|
833
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-gray-500", children: "No top customers data" })
|
|
834
|
+
] }) });
|
|
835
|
+
}
|
|
836
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Card, { className: "border-gray-200 shadow-sm bg-white overflow-hidden", children: [
|
|
837
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardHeader, { className: "pb-2 border-b border-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CardTitle, { className: "text-sm font-medium text-gray-700 flex items-center gap-2", children: [
|
|
838
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Crown, { className: "h-4 w-4 text-amber-500" }),
|
|
839
|
+
"Top Customers"
|
|
840
|
+
] }) }),
|
|
841
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardContent, { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "divide-y divide-gray-100", children: customers.map((customer, idx) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 py-3 hover:bg-gray-50 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
842
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
843
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.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 }),
|
|
844
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "min-w-0", children: [
|
|
845
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm font-medium text-gray-900 truncate", children: customer.name }),
|
|
846
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-xs text-gray-500 truncate", children: customer.email })
|
|
847
|
+
] })
|
|
848
|
+
] }),
|
|
849
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm font-semibold text-emerald-600", children: typeof customer.revenue === "number" ? formatCurrency(customer.revenue) : customer.revenue })
|
|
850
|
+
] }) }, idx)) }) })
|
|
851
|
+
] });
|
|
852
|
+
}
|
|
853
|
+
function DataRenderer({ type, data }) {
|
|
854
|
+
if (!data) return null;
|
|
855
|
+
switch (type) {
|
|
856
|
+
case "subscription":
|
|
857
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SubscriptionCard, { subscription: data });
|
|
858
|
+
case "subscriptions":
|
|
859
|
+
const subs = data.subscriptions || data || [];
|
|
860
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SubscriptionList, { subscriptions: Array.isArray(subs) ? subs : [subs] });
|
|
861
|
+
case "customer":
|
|
862
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomerCard, { customer: data.customer || data });
|
|
863
|
+
case "customers":
|
|
864
|
+
const custs = data.customers || data || [];
|
|
865
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomerList, { customers: Array.isArray(custs) ? custs : [custs] });
|
|
866
|
+
case "transactions":
|
|
867
|
+
const txs = data.transactions || data || [];
|
|
868
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TransactionList, { transactions: Array.isArray(txs) ? txs : [txs] });
|
|
869
|
+
case "transaction":
|
|
870
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TransactionList, { transactions: [data], maxItems: 1 });
|
|
871
|
+
case "dashboard_stats":
|
|
872
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DashboardStatsCard, { stats: data });
|
|
873
|
+
case "top_customers":
|
|
874
|
+
const topCustomers = data.customers || data || [];
|
|
875
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TopCustomersList, { customers: Array.isArray(topCustomers) ? topCustomers : [topCustomers] });
|
|
876
|
+
case "api_keys":
|
|
877
|
+
const keys = data.api_keys || data || [];
|
|
878
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ApiKeyList, { apiKeys: Array.isArray(keys) ? keys : [keys] });
|
|
879
|
+
case "sessions":
|
|
880
|
+
const sessions = data.sessions || data || [];
|
|
881
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SessionList, { sessions: Array.isArray(sessions) ? sessions : [sessions] });
|
|
882
|
+
default:
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// src/ChatPanel.tsx
|
|
888
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
889
|
+
var DEFAULT_AGENT_URL = "http://localhost:5002";
|
|
890
|
+
var PANEL_WIDTH = 400;
|
|
891
|
+
function renderMarkdown(text) {
|
|
892
|
+
if (!text) return null;
|
|
893
|
+
const lines = text.split("\n");
|
|
894
|
+
const elements = [];
|
|
895
|
+
let currentList = null;
|
|
896
|
+
let inCodeBlock = false;
|
|
897
|
+
let codeContent = [];
|
|
898
|
+
let codeLanguage = "";
|
|
899
|
+
const processInlineFormatting = (line) => {
|
|
900
|
+
const parts = [];
|
|
901
|
+
let remaining = line;
|
|
902
|
+
let keyIndex = 0;
|
|
903
|
+
while (remaining.length > 0) {
|
|
904
|
+
const codeMatch = remaining.match(/^`([^`]+)`/);
|
|
905
|
+
if (codeMatch) {
|
|
906
|
+
parts.push(
|
|
907
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("code", { className: "bg-gray-100 px-1 py-0.5 rounded text-xs font-mono", children: codeMatch[1] }, keyIndex++)
|
|
908
|
+
);
|
|
909
|
+
remaining = remaining.slice(codeMatch[0].length);
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
const boldMatch = remaining.match(/^\*\*([^*]+)\*\*/);
|
|
913
|
+
if (boldMatch) {
|
|
914
|
+
parts.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: boldMatch[1] }, keyIndex++));
|
|
915
|
+
remaining = remaining.slice(boldMatch[0].length);
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
const italicMatch = remaining.match(/^\*([^*]+)\*/);
|
|
919
|
+
if (italicMatch) {
|
|
920
|
+
parts.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("em", { children: italicMatch[1] }, keyIndex++));
|
|
921
|
+
remaining = remaining.slice(italicMatch[0].length);
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)]+)\)/);
|
|
925
|
+
if (linkMatch) {
|
|
926
|
+
parts.push(
|
|
927
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("a", { href: linkMatch[2], className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: linkMatch[1] }, keyIndex++)
|
|
928
|
+
);
|
|
929
|
+
remaining = remaining.slice(linkMatch[0].length);
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
const nextSpecial = remaining.search(/[`*\[]/);
|
|
933
|
+
if (nextSpecial === -1) {
|
|
934
|
+
parts.push(remaining);
|
|
935
|
+
break;
|
|
936
|
+
} else if (nextSpecial === 0) {
|
|
937
|
+
parts.push(remaining[0]);
|
|
938
|
+
remaining = remaining.slice(1);
|
|
939
|
+
} else {
|
|
940
|
+
parts.push(remaining.slice(0, nextSpecial));
|
|
941
|
+
remaining = remaining.slice(nextSpecial);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return parts.length === 1 ? parts[0] : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: parts });
|
|
945
|
+
};
|
|
946
|
+
const flushList = () => {
|
|
947
|
+
if (currentList) {
|
|
948
|
+
const ListTag = currentList.type === "ul" ? "ul" : "ol";
|
|
949
|
+
elements.push(
|
|
950
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ListTag, { className: `${currentList.type === "ul" ? "list-disc" : "list-decimal"} ml-4 my-1`, children: currentList.items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("li", { className: "ml-2", children: item }, i)) }, elements.length)
|
|
951
|
+
);
|
|
952
|
+
currentList = null;
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
for (let i = 0; i < lines.length; i++) {
|
|
956
|
+
const line = lines[i];
|
|
957
|
+
if (line.startsWith("```")) {
|
|
958
|
+
if (inCodeBlock) {
|
|
959
|
+
elements.push(
|
|
960
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("pre", { className: "bg-gray-100 rounded p-2 my-1 overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("code", { className: "text-xs font-mono", children: codeContent.join("\n") }) }, elements.length)
|
|
961
|
+
);
|
|
962
|
+
inCodeBlock = false;
|
|
963
|
+
codeContent = [];
|
|
964
|
+
codeLanguage = "";
|
|
965
|
+
} else {
|
|
966
|
+
flushList();
|
|
967
|
+
inCodeBlock = true;
|
|
968
|
+
codeLanguage = line.slice(3).trim();
|
|
969
|
+
}
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (inCodeBlock) {
|
|
973
|
+
codeContent.push(line);
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
const headerMatch = line.match(/^(#{1,6})\s+(.+)/);
|
|
977
|
+
if (headerMatch) {
|
|
978
|
+
flushList();
|
|
979
|
+
const level = headerMatch[1].length;
|
|
980
|
+
const content = processInlineFormatting(headerMatch[2]);
|
|
981
|
+
const className = level === 1 ? "text-lg font-bold my-1" : level === 2 ? "text-base font-bold my-1" : "text-sm font-semibold my-1";
|
|
982
|
+
elements.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className, children: content }, elements.length));
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
const ulMatch = line.match(/^[-*]\s+(.+)/);
|
|
986
|
+
if (ulMatch) {
|
|
987
|
+
if (!currentList || currentList.type !== "ul") {
|
|
988
|
+
flushList();
|
|
989
|
+
currentList = { type: "ul", items: [] };
|
|
990
|
+
}
|
|
991
|
+
currentList.items.push(processInlineFormatting(ulMatch[1]));
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
const olMatch = line.match(/^\d+[.)]\s+(.+)/);
|
|
995
|
+
if (olMatch) {
|
|
996
|
+
if (!currentList || currentList.type !== "ol") {
|
|
997
|
+
flushList();
|
|
998
|
+
currentList = { type: "ol", items: [] };
|
|
999
|
+
}
|
|
1000
|
+
currentList.items.push(processInlineFormatting(olMatch[1]));
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
if (line.trim() === "") {
|
|
1004
|
+
flushList();
|
|
1005
|
+
elements.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "h-2" }, elements.length));
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
if (line.match(/^[-*_]{3,}$/)) {
|
|
1009
|
+
flushList();
|
|
1010
|
+
elements.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("hr", { className: "my-2 border-gray-200" }, elements.length));
|
|
1011
|
+
continue;
|
|
1012
|
+
}
|
|
1013
|
+
flushList();
|
|
1014
|
+
elements.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { children: processInlineFormatting(line) }, elements.length));
|
|
1015
|
+
}
|
|
1016
|
+
flushList();
|
|
1017
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: elements });
|
|
1018
|
+
}
|
|
1019
|
+
var defaultStartingQuestions = [
|
|
1020
|
+
{
|
|
1021
|
+
id: "changing-layouts",
|
|
1022
|
+
label: "Changing layouts",
|
|
1023
|
+
prompt: "How can I customize and change the layout of my dashboard?",
|
|
1024
|
+
icon: "layout"
|
|
1025
|
+
},
|
|
1026
|
+
{
|
|
1027
|
+
id: "bulk-uploads",
|
|
1028
|
+
label: "Bulk uploads",
|
|
1029
|
+
prompt: "How do I perform bulk uploads of data using CSV files?",
|
|
1030
|
+
icon: "upload"
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
id: "example-setups",
|
|
1034
|
+
label: "Example setups",
|
|
1035
|
+
prompt: "Can you show me some example configurations and setups?",
|
|
1036
|
+
icon: "setup"
|
|
1037
|
+
}
|
|
1038
|
+
];
|
|
1039
|
+
var folders = [
|
|
1040
|
+
{
|
|
1041
|
+
id: "customers",
|
|
1042
|
+
title: "Customer Management",
|
|
1043
|
+
topics: [
|
|
1044
|
+
{ id: "add-customer", label: "Add a new customer", prompt: "How do I add a new customer to the system and what information is required?" },
|
|
1045
|
+
{ id: "customer-details", label: "View customer details", prompt: "Show me how to view and edit customer information and their payment history." }
|
|
1046
|
+
]
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
id: "payments",
|
|
1050
|
+
title: "Payment Processing",
|
|
1051
|
+
topics: [
|
|
1052
|
+
{ id: "process-payment", label: "Process a payment", prompt: "How do I process a one-time payment for a customer?" },
|
|
1053
|
+
{ id: "refund-payment", label: "Issue a refund", prompt: "Walk me through issuing a refund for a customer payment." }
|
|
1054
|
+
]
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
id: "subscriptions",
|
|
1058
|
+
title: "Subscription Management",
|
|
1059
|
+
topics: [
|
|
1060
|
+
{ id: "create-subscription", label: "Create a subscription", prompt: "How do I set up a new subscription plan for a customer?" },
|
|
1061
|
+
{ id: "update-subscription", label: "Update subscription tier", prompt: "How can I change a customer's subscription plan or upgrade them?" }
|
|
1062
|
+
]
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
id: "billing",
|
|
1066
|
+
title: "Billing",
|
|
1067
|
+
topics: [
|
|
1068
|
+
{ id: "payment-methods", label: "Manage payment methods", prompt: "How do customers add or update their payment methods?" }
|
|
1069
|
+
]
|
|
1070
|
+
}
|
|
1071
|
+
];
|
|
1072
|
+
var guides = {
|
|
1073
|
+
"getting-started": {
|
|
1074
|
+
id: "getting-started",
|
|
1075
|
+
title: "Getting started",
|
|
1076
|
+
steps: [
|
|
1077
|
+
{
|
|
1078
|
+
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.",
|
|
1079
|
+
navigation: { page: "customers" },
|
|
1080
|
+
cursorTarget: {
|
|
1081
|
+
selector: '[data-page="customers"]',
|
|
1082
|
+
offset: { x: 0, y: 0 },
|
|
1083
|
+
onClick: true
|
|
1084
|
+
}
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
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.",
|
|
1088
|
+
navigation: { page: "customers" },
|
|
1089
|
+
cursorTarget: {
|
|
1090
|
+
selector: '[data-testid="add-customer-button"]',
|
|
1091
|
+
offset: { x: 0, y: 0 },
|
|
1092
|
+
onClick: true
|
|
1093
|
+
}
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
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.",
|
|
1097
|
+
navigation: { page: "settings", subtab: "general" },
|
|
1098
|
+
cursorTarget: {
|
|
1099
|
+
selector: '[data-page="settings"]',
|
|
1100
|
+
offset: { x: 0, y: 0 },
|
|
1101
|
+
onClick: true
|
|
1102
|
+
}
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
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.",
|
|
1106
|
+
navigation: { page: "settings", subtab: "general" },
|
|
1107
|
+
cursorTarget: {
|
|
1108
|
+
selector: '[data-testid="company-address-input"]',
|
|
1109
|
+
offset: { x: 0, y: 0 },
|
|
1110
|
+
onClick: false
|
|
1111
|
+
}
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
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.",
|
|
1115
|
+
navigation: { page: "settings", subtab: "api" },
|
|
1116
|
+
cursorTarget: {
|
|
1117
|
+
selector: '[data-settings-tab="api"]',
|
|
1118
|
+
offset: { x: 0, y: 0 },
|
|
1119
|
+
onClick: true
|
|
1120
|
+
}
|
|
1121
|
+
},
|
|
1122
|
+
{
|
|
1123
|
+
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.",
|
|
1124
|
+
navigation: { page: "settings", subtab: "api" },
|
|
1125
|
+
cursorTarget: {
|
|
1126
|
+
selector: '[data-testid="create-api-key-button"]',
|
|
1127
|
+
offset: { x: 0, y: 0 },
|
|
1128
|
+
onClick: true
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
]
|
|
1132
|
+
},
|
|
1133
|
+
"add-api-key": {
|
|
1134
|
+
id: "add-api-key",
|
|
1135
|
+
title: "How to add an API key",
|
|
1136
|
+
steps: [
|
|
1137
|
+
{
|
|
1138
|
+
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.",
|
|
1139
|
+
navigation: { page: "settings", subtab: "general" },
|
|
1140
|
+
cursorTarget: {
|
|
1141
|
+
selector: '[data-page="settings"]',
|
|
1142
|
+
offset: { x: 0, y: 0 },
|
|
1143
|
+
onClick: false
|
|
1144
|
+
}
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
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.",
|
|
1148
|
+
navigation: { page: "settings", subtab: "api" },
|
|
1149
|
+
cursorTarget: {
|
|
1150
|
+
selector: '[data-settings-tab="api"]',
|
|
1151
|
+
offset: { x: 0, y: 0 },
|
|
1152
|
+
onClick: true
|
|
1153
|
+
}
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
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.",
|
|
1157
|
+
navigation: { page: "settings", subtab: "api" },
|
|
1158
|
+
cursorTarget: {
|
|
1159
|
+
selector: '[data-testid="create-api-key-button"]',
|
|
1160
|
+
offset: { x: 0, y: 0 },
|
|
1161
|
+
onClick: true
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
]
|
|
1165
|
+
},
|
|
1166
|
+
"refund-payment": {
|
|
1167
|
+
id: "refund-payment",
|
|
1168
|
+
title: "How to refund a payment",
|
|
1169
|
+
steps: [
|
|
1170
|
+
{
|
|
1171
|
+
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.",
|
|
1172
|
+
navigation: { page: "dashboard" },
|
|
1173
|
+
cursorTarget: {
|
|
1174
|
+
selector: '[data-page="dashboard"]',
|
|
1175
|
+
offset: { x: 0, y: 0 },
|
|
1176
|
+
onClick: true
|
|
1177
|
+
}
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
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.",
|
|
1181
|
+
navigation: { page: "dashboard" },
|
|
1182
|
+
cursorTarget: {
|
|
1183
|
+
selector: '[data-testid="recent-activity-card"]',
|
|
1184
|
+
offset: { x: 0, y: 0 },
|
|
1185
|
+
onClick: false
|
|
1186
|
+
}
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
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.",
|
|
1190
|
+
navigation: { page: "dashboard" },
|
|
1191
|
+
cursorTarget: {
|
|
1192
|
+
selector: '[data-testid="transaction-menu-button"]',
|
|
1193
|
+
offset: { x: 0, y: 0 },
|
|
1194
|
+
onClick: true
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
]
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
var initialMessages = [];
|
|
1201
|
+
function ChatPanel({
|
|
1202
|
+
isOpen = true,
|
|
1203
|
+
onClose,
|
|
1204
|
+
onBack,
|
|
1205
|
+
onNavigate,
|
|
1206
|
+
onActionComplete,
|
|
1207
|
+
currentPage,
|
|
1208
|
+
agentUrl = DEFAULT_AGENT_URL,
|
|
1209
|
+
startingQuestions: startingQuestionsProp,
|
|
1210
|
+
startingQuestionsEndpoint
|
|
1211
|
+
} = {}) {
|
|
1212
|
+
const [messages, setMessages] = React4.useState(initialMessages);
|
|
1213
|
+
const [input, setInput] = React4.useState("");
|
|
1214
|
+
const [sessionId] = React4.useState(() => crypto.randomUUID());
|
|
1215
|
+
const streamIntervals = React4.useRef({});
|
|
1216
|
+
const isEmpty = messages.length === 0;
|
|
1217
|
+
const [phase, setPhase] = React4.useState("idle");
|
|
1218
|
+
const [progressSteps, setProgressSteps] = React4.useState([]);
|
|
1219
|
+
const phaseTimers = React4.useRef([]);
|
|
1220
|
+
const lastRole = messages.length ? messages[messages.length - 1].role : void 0;
|
|
1221
|
+
const [panelView, setPanelView] = React4.useState("landing");
|
|
1222
|
+
const [currentFolderId, setCurrentFolderId] = React4.useState(void 0);
|
|
1223
|
+
const [startingQuestions, setStartingQuestions] = React4.useState(
|
|
1224
|
+
startingQuestionsProp || defaultStartingQuestions
|
|
1225
|
+
);
|
|
1226
|
+
const [loadingQuestions, setLoadingQuestions] = React4.useState(false);
|
|
1227
|
+
React4.useEffect(() => {
|
|
1228
|
+
if (startingQuestionsEndpoint && !startingQuestionsProp) {
|
|
1229
|
+
setLoadingQuestions(true);
|
|
1230
|
+
fetch(startingQuestionsEndpoint).then((res) => res.json()).then((data) => {
|
|
1231
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
1232
|
+
setStartingQuestions(data);
|
|
1233
|
+
}
|
|
1234
|
+
}).catch((err) => {
|
|
1235
|
+
console.warn("[KiteChat] Failed to fetch starting questions:", err);
|
|
1236
|
+
}).finally(() => setLoadingQuestions(false));
|
|
1237
|
+
}
|
|
1238
|
+
}, [startingQuestionsEndpoint, startingQuestionsProp]);
|
|
1239
|
+
React4.useEffect(() => {
|
|
1240
|
+
if (startingQuestionsProp) {
|
|
1241
|
+
setStartingQuestions(startingQuestionsProp);
|
|
1242
|
+
}
|
|
1243
|
+
}, [startingQuestionsProp]);
|
|
1244
|
+
const [activeGuide, setActiveGuide] = React4.useState(void 0);
|
|
1245
|
+
const activeGuideRef = React4.useRef(void 0);
|
|
1246
|
+
const latestBulkSummaryNavigationRef = React4.useRef(null);
|
|
1247
|
+
const [guideComplete, setGuideComplete] = React4.useState(false);
|
|
1248
|
+
React4.useEffect(() => {
|
|
1249
|
+
;
|
|
1250
|
+
window.resetIntegrationNotification = () => {
|
|
1251
|
+
localStorage.removeItem("gmailNotificationSeen");
|
|
1252
|
+
console.log("Integration notification reset! Click the Integrations tab to see it again.");
|
|
1253
|
+
};
|
|
1254
|
+
const handleIntegrationTabClick = () => {
|
|
1255
|
+
const hasSeenNotification = localStorage.getItem("gmailNotificationSeen");
|
|
1256
|
+
if (!hasSeenNotification) {
|
|
1257
|
+
setPanelView("landing");
|
|
1258
|
+
setCurrentFolderId(void 0);
|
|
1259
|
+
const messageId = Date.now();
|
|
1260
|
+
const notificationText = "I see you're exploring integrations. Let me know if there are any other connections we should add.";
|
|
1261
|
+
const draftMessage = {
|
|
1262
|
+
id: messageId,
|
|
1263
|
+
role: "assistant",
|
|
1264
|
+
kind: "text",
|
|
1265
|
+
content: "",
|
|
1266
|
+
isNotificationMessage: true
|
|
1267
|
+
};
|
|
1268
|
+
setMessages((prev) => [...prev, draftMessage]);
|
|
1269
|
+
streamAssistantMessage(messageId, notificationText);
|
|
1270
|
+
localStorage.setItem("gmailNotificationSeen", "true");
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
window.addEventListener("integrationTabClicked", handleIntegrationTabClick);
|
|
1274
|
+
return () => {
|
|
1275
|
+
window.removeEventListener("integrationTabClicked", handleIntegrationTabClick);
|
|
1276
|
+
};
|
|
1277
|
+
}, []);
|
|
1278
|
+
React4.useEffect(() => {
|
|
1279
|
+
if (activeGuide) {
|
|
1280
|
+
if (!activeGuideRef.current || activeGuideRef.current.id !== activeGuide.id || activeGuideRef.current.stepIndex !== activeGuide.stepIndex) {
|
|
1281
|
+
activeGuideRef.current = activeGuide;
|
|
1282
|
+
}
|
|
1283
|
+
} else {
|
|
1284
|
+
activeGuideRef.current = void 0;
|
|
1285
|
+
}
|
|
1286
|
+
}, [activeGuide]);
|
|
1287
|
+
const [pendingNavigation, setPendingNavigation] = React4.useState(null);
|
|
1288
|
+
const [pendingAction, setPendingAction] = React4.useState(null);
|
|
1289
|
+
const [actionFormData, setActionFormData] = React4.useState({});
|
|
1290
|
+
const messagesEndRef = React4.useRef(null);
|
|
1291
|
+
const messagesContainerRef = React4.useRef(null);
|
|
1292
|
+
const currentStepRef = React4.useRef(null);
|
|
1293
|
+
const { cursorState, moveTo, hide } = useGuideCursor();
|
|
1294
|
+
const [pendingFile, setPendingFile] = React4.useState(null);
|
|
1295
|
+
const [pendingBulkSession, setPendingBulkSession] = React4.useState(null);
|
|
1296
|
+
const pendingBulkSessionRef = React4.useRef(null);
|
|
1297
|
+
const fileInputRef = React4.useRef(null);
|
|
1298
|
+
React4.useEffect(() => {
|
|
1299
|
+
if (!activeGuide || activeGuide.id !== "add-api-key" || activeGuide.stepIndex !== 2) {
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
const checkForDialogOpen = () => {
|
|
1303
|
+
const dialog = document.querySelector('[role="dialog"]');
|
|
1304
|
+
const currentGuide = activeGuideRef.current;
|
|
1305
|
+
if (dialog && currentGuide && currentGuide.id === "add-api-key" && currentGuide.stepIndex === 2) {
|
|
1306
|
+
hide();
|
|
1307
|
+
const id = Date.now() + 1;
|
|
1308
|
+
setMessages((prev) => [
|
|
1309
|
+
...prev,
|
|
1310
|
+
{ id, role: "assistant", kind: "guideComplete", content: "Perfect! The dialog is now open. You can enter a name and create your API key." }
|
|
1311
|
+
]);
|
|
1312
|
+
setActiveGuide(void 0);
|
|
1313
|
+
setGuideComplete(true);
|
|
1314
|
+
}
|
|
1315
|
+
};
|
|
1316
|
+
const interval = setInterval(checkForDialogOpen, 300);
|
|
1317
|
+
return () => clearInterval(interval);
|
|
1318
|
+
}, [activeGuide, hide]);
|
|
1319
|
+
React4.useEffect(() => {
|
|
1320
|
+
return () => {
|
|
1321
|
+
Object.values(streamIntervals.current).forEach((id) => window.clearInterval(id));
|
|
1322
|
+
streamIntervals.current = {};
|
|
1323
|
+
phaseTimers.current.forEach((id) => window.clearTimeout(id));
|
|
1324
|
+
phaseTimers.current = [];
|
|
1325
|
+
};
|
|
1326
|
+
}, []);
|
|
1327
|
+
React4.useEffect(() => {
|
|
1328
|
+
if (activeGuide && messages.length > 0) {
|
|
1329
|
+
const lastMessage = messages[messages.length - 1];
|
|
1330
|
+
if (lastMessage.kind === "guideStep" || lastMessage.kind === "guideComplete") {
|
|
1331
|
+
if (currentStepRef.current && messagesContainerRef.current) {
|
|
1332
|
+
const container = messagesContainerRef.current;
|
|
1333
|
+
const stepElement = currentStepRef.current;
|
|
1334
|
+
const containerRect = container.getBoundingClientRect();
|
|
1335
|
+
const stepRect = stepElement.getBoundingClientRect();
|
|
1336
|
+
const scrollTop = stepRect.top - containerRect.top + container.scrollTop;
|
|
1337
|
+
container.scrollTop = scrollTop;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
} else if (!activeGuide && messages.length > 0) {
|
|
1341
|
+
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
1342
|
+
}
|
|
1343
|
+
}, [messages, phase, activeGuide]);
|
|
1344
|
+
const latestBulkSummaryNavigation = React4.useMemo(() => {
|
|
1345
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1346
|
+
const msg = messages[i];
|
|
1347
|
+
if (msg.kind === "bulkSummary" && msg.bulkSummary?.navigationPage && msg.bulkSummary.successes > 0) {
|
|
1348
|
+
return msg.bulkSummary.navigationPage;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
return null;
|
|
1352
|
+
}, [messages]);
|
|
1353
|
+
React4.useEffect(() => {
|
|
1354
|
+
latestBulkSummaryNavigationRef.current = latestBulkSummaryNavigation;
|
|
1355
|
+
}, [latestBulkSummaryNavigation]);
|
|
1356
|
+
React4.useEffect(() => {
|
|
1357
|
+
pendingBulkSessionRef.current = pendingBulkSession;
|
|
1358
|
+
}, [pendingBulkSession]);
|
|
1359
|
+
React4.useEffect(() => {
|
|
1360
|
+
const handleKeyDown = (e) => {
|
|
1361
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
1362
|
+
const currentBulkSession = pendingBulkSessionRef.current;
|
|
1363
|
+
if (currentBulkSession) {
|
|
1364
|
+
e.preventDefault();
|
|
1365
|
+
e.stopPropagation();
|
|
1366
|
+
confirmBulkOperation(currentBulkSession);
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
if (pendingAction) {
|
|
1370
|
+
e.preventDefault();
|
|
1371
|
+
e.stopPropagation();
|
|
1372
|
+
handleActionSubmit();
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
if (pendingNavigation) {
|
|
1376
|
+
e.preventDefault();
|
|
1377
|
+
e.stopPropagation();
|
|
1378
|
+
handleConfirmNavigation(pendingNavigation);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
const currentBulkNav = latestBulkSummaryNavigationRef.current;
|
|
1382
|
+
console.log("[DEBUG] Keyboard handler - latestBulkSummaryNavigation:", currentBulkNav, "onNavigate:", !!onNavigate);
|
|
1383
|
+
if (currentBulkNav && onNavigate) {
|
|
1384
|
+
console.log("[DEBUG] Navigating via keyboard to:", currentBulkNav.page);
|
|
1385
|
+
e.preventDefault();
|
|
1386
|
+
e.stopPropagation();
|
|
1387
|
+
onNavigate(currentBulkNav.page, currentBulkNav.subtab);
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
if (guideComplete) {
|
|
1391
|
+
e.preventDefault();
|
|
1392
|
+
e.stopPropagation();
|
|
1393
|
+
handleBack();
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
const currentGuide = activeGuideRef.current;
|
|
1397
|
+
if (currentGuide) {
|
|
1398
|
+
e.preventDefault();
|
|
1399
|
+
e.stopPropagation();
|
|
1400
|
+
advanceGuide();
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
};
|
|
1405
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
1406
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1407
|
+
}, [pendingAction, pendingNavigation, activeGuide, guideComplete, onNavigate]);
|
|
1408
|
+
function streamAssistantMessage(messageId, fullText, followups) {
|
|
1409
|
+
const tokens = fullText.split(" ");
|
|
1410
|
+
let i = 0;
|
|
1411
|
+
const intervalId = window.setInterval(() => {
|
|
1412
|
+
i += 1;
|
|
1413
|
+
setMessages(
|
|
1414
|
+
(prev) => prev.map((m) => m.id === messageId ? { ...m, content: tokens.slice(0, i).join(" ") } : m)
|
|
1415
|
+
);
|
|
1416
|
+
if (i >= tokens.length) {
|
|
1417
|
+
window.clearInterval(intervalId);
|
|
1418
|
+
delete streamIntervals.current[messageId];
|
|
1419
|
+
if (followups && followups.length > 0) {
|
|
1420
|
+
setMessages(
|
|
1421
|
+
(prev) => prev.map((m) => m.id === messageId ? { ...m, followups } : m)
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
setPhase("idle");
|
|
1425
|
+
}
|
|
1426
|
+
}, 25);
|
|
1427
|
+
streamIntervals.current[messageId] = intervalId;
|
|
1428
|
+
}
|
|
1429
|
+
function handleFollowupClick(messageId, followup) {
|
|
1430
|
+
setMessages(
|
|
1431
|
+
(prev) => prev.map((m) => m.id === messageId ? { ...m, followupSelected: true } : m)
|
|
1432
|
+
);
|
|
1433
|
+
startChatFlow(followup.label);
|
|
1434
|
+
}
|
|
1435
|
+
function handleBack() {
|
|
1436
|
+
Object.values(streamIntervals.current).forEach((id) => window.clearInterval(id));
|
|
1437
|
+
streamIntervals.current = {};
|
|
1438
|
+
phaseTimers.current.forEach((id) => window.clearTimeout(id));
|
|
1439
|
+
phaseTimers.current = [];
|
|
1440
|
+
hide();
|
|
1441
|
+
setMessages([]);
|
|
1442
|
+
setInput("");
|
|
1443
|
+
setPhase("idle");
|
|
1444
|
+
setPanelView("landing");
|
|
1445
|
+
setCurrentFolderId(void 0);
|
|
1446
|
+
setActiveGuide(void 0);
|
|
1447
|
+
setGuideComplete(false);
|
|
1448
|
+
setPendingNavigation(null);
|
|
1449
|
+
setPendingAction(null);
|
|
1450
|
+
setActionFormData({});
|
|
1451
|
+
if (onBack) onBack();
|
|
1452
|
+
}
|
|
1453
|
+
function openFolder(folderId) {
|
|
1454
|
+
setPanelView("folder");
|
|
1455
|
+
setCurrentFolderId(folderId);
|
|
1456
|
+
}
|
|
1457
|
+
function closeFolder() {
|
|
1458
|
+
setPanelView("landing");
|
|
1459
|
+
setCurrentFolderId(void 0);
|
|
1460
|
+
}
|
|
1461
|
+
function handleSubmit(e) {
|
|
1462
|
+
e.preventDefault();
|
|
1463
|
+
const trimmed = input.trim();
|
|
1464
|
+
if (pendingFile) {
|
|
1465
|
+
if (!trimmed) {
|
|
1466
|
+
const errorMessage = {
|
|
1467
|
+
id: Date.now(),
|
|
1468
|
+
role: "assistant",
|
|
1469
|
+
kind: "text",
|
|
1470
|
+
content: "Please describe what you'd like to do with this CSV file. For example: 'Create customers from this file' or 'Add these subscriptions'."
|
|
1471
|
+
};
|
|
1472
|
+
setMessages((prev) => [...prev, errorMessage]);
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
startBulkUploadFlow(trimmed, pendingFile);
|
|
1476
|
+
setInput("");
|
|
1477
|
+
setPendingFile(null);
|
|
1478
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
if (!trimmed) return;
|
|
1482
|
+
startChatFlow(trimmed);
|
|
1483
|
+
setInput("");
|
|
1484
|
+
}
|
|
1485
|
+
function getPageDisplayName(target) {
|
|
1486
|
+
const pageNames = {
|
|
1487
|
+
dashboard: "dashboard",
|
|
1488
|
+
customers: "customers page",
|
|
1489
|
+
payments: "payments page",
|
|
1490
|
+
settings: "settings page",
|
|
1491
|
+
support: "support page",
|
|
1492
|
+
disputes: "disputes page",
|
|
1493
|
+
radar: "radar page"
|
|
1494
|
+
};
|
|
1495
|
+
if (target.page === "settings" && target.subtab) {
|
|
1496
|
+
const subtabNames = {
|
|
1497
|
+
general: "General",
|
|
1498
|
+
billing: "Billing",
|
|
1499
|
+
security: "Security",
|
|
1500
|
+
api: "API Keys",
|
|
1501
|
+
notifications: "Notifications",
|
|
1502
|
+
integrations: "Integrations"
|
|
1503
|
+
};
|
|
1504
|
+
return `${pageNames.settings} (${subtabNames[target.subtab]} tab)`;
|
|
1505
|
+
}
|
|
1506
|
+
return pageNames[target.page];
|
|
1507
|
+
}
|
|
1508
|
+
function handleConfirmNavigation(target) {
|
|
1509
|
+
if (onNavigate) {
|
|
1510
|
+
onNavigate(target.page, target.subtab);
|
|
1511
|
+
}
|
|
1512
|
+
setPendingNavigation(null);
|
|
1513
|
+
setMessages(
|
|
1514
|
+
(prev) => prev.map(
|
|
1515
|
+
(msg) => msg.kind === "navigationAction" && msg.navigationTarget?.page === target.page && msg.navigationTarget?.subtab === target.subtab ? { ...msg, navigationTarget: void 0 } : msg
|
|
1516
|
+
)
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
function handleActionSubmit() {
|
|
1520
|
+
if (!pendingAction) return;
|
|
1521
|
+
const data = actionFormData;
|
|
1522
|
+
if (pendingAction.type === "updateCompanyInfo") {
|
|
1523
|
+
} else if (pendingAction.type === "addApiKey") {
|
|
1524
|
+
const apiData = data;
|
|
1525
|
+
if (!apiData.name || !apiData.name.trim()) {
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
} else if (pendingAction.type === "addCustomer") {
|
|
1529
|
+
const customerData = data;
|
|
1530
|
+
if (!customerData.name || !customerData.name.trim() || !customerData.email || !customerData.email.trim()) {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
} else if (pendingAction.type === "changePassword") {
|
|
1534
|
+
const passwordData = data;
|
|
1535
|
+
if (!passwordData.currentPassword || !passwordData.newPassword || !passwordData.confirmPassword) {
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
if (passwordData.newPassword !== passwordData.confirmPassword) {
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
} else if (pendingAction.type === "addWebhook") {
|
|
1542
|
+
const webhookData = data;
|
|
1543
|
+
if (!webhookData.url || !webhookData.url.trim()) {
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
} else if (pendingAction.type === "addPaymentMethod") {
|
|
1547
|
+
const paymentData = data;
|
|
1548
|
+
if (!paymentData.cardNumber || !paymentData.expiryDate) {
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
} else if (pendingAction.type === "deleteApiKey") {
|
|
1552
|
+
const apiData = data;
|
|
1553
|
+
if (!apiData.keyId && !apiData.name) {
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
} else if (pendingAction.type === "refundPayment") {
|
|
1557
|
+
const refundData = data;
|
|
1558
|
+
if (!refundData.transactionId && !refundData.customer) {
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
} else if (pendingAction.type === "createSubscription") {
|
|
1562
|
+
const subscriptionData = data;
|
|
1563
|
+
if (!subscriptionData.customerEmail && !subscriptionData.customerId) {
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
} else if (pendingAction.type === "toggleBlockRule" || pendingAction.type === "enableBlockRule" || pendingAction.type === "disableBlockRule") {
|
|
1567
|
+
const blockRuleData = data;
|
|
1568
|
+
if (!blockRuleData.ruleId) {
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
if (onActionComplete) {
|
|
1573
|
+
onActionComplete(pendingAction.type, data);
|
|
1574
|
+
}
|
|
1575
|
+
setMessages(
|
|
1576
|
+
(prev) => prev.map(
|
|
1577
|
+
(msg) => msg.kind === "actionForm" && msg.actionType === pendingAction.type ? { ...msg, isSubmitted: true } : msg
|
|
1578
|
+
)
|
|
1579
|
+
);
|
|
1580
|
+
setPendingAction(null);
|
|
1581
|
+
setActionFormData({});
|
|
1582
|
+
}
|
|
1583
|
+
async function startChatFlow(userText) {
|
|
1584
|
+
phaseTimers.current.forEach((id) => window.clearTimeout(id));
|
|
1585
|
+
phaseTimers.current = [];
|
|
1586
|
+
const lastAssistantMessage = [...messages].reverse().find((m) => m.role === "assistant");
|
|
1587
|
+
const isRespondingToNotification = lastAssistantMessage?.isNotificationMessage === true;
|
|
1588
|
+
const now = Date.now();
|
|
1589
|
+
const userMessage = { id: now, role: "user", content: userText };
|
|
1590
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
1591
|
+
if (isRespondingToNotification) {
|
|
1592
|
+
const thankYouMessageId = Date.now() + 1;
|
|
1593
|
+
const thankYouMessage = {
|
|
1594
|
+
id: thankYouMessageId,
|
|
1595
|
+
role: "assistant",
|
|
1596
|
+
kind: "text",
|
|
1597
|
+
content: ""
|
|
1598
|
+
};
|
|
1599
|
+
setMessages((prev) => [...prev, thankYouMessage]);
|
|
1600
|
+
streamAssistantMessage(thankYouMessageId, "Thank you, your feedback is saved.");
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
setPendingNavigation(null);
|
|
1604
|
+
setPhase("thinking");
|
|
1605
|
+
setProgressSteps([]);
|
|
1606
|
+
let streamCompleted = false;
|
|
1607
|
+
try {
|
|
1608
|
+
const controller = new AbortController();
|
|
1609
|
+
const timeoutId = setTimeout(() => controller.abort(), 6e4);
|
|
1610
|
+
const response = await fetch(`${agentUrl}/chat/stream`, {
|
|
1611
|
+
method: "POST",
|
|
1612
|
+
headers: {
|
|
1613
|
+
"Content-Type": "application/json"
|
|
1614
|
+
},
|
|
1615
|
+
body: JSON.stringify({
|
|
1616
|
+
session_id: sessionId,
|
|
1617
|
+
message: userText,
|
|
1618
|
+
current_page: currentPage || "dashboard"
|
|
1619
|
+
}),
|
|
1620
|
+
signal: controller.signal
|
|
1621
|
+
});
|
|
1622
|
+
clearTimeout(timeoutId);
|
|
1623
|
+
if (!response.ok) {
|
|
1624
|
+
throw new Error(`Agent request failed: ${response.status}`);
|
|
1625
|
+
}
|
|
1626
|
+
const reader = response.body?.getReader();
|
|
1627
|
+
if (!reader) throw new Error("No response body");
|
|
1628
|
+
const decoder = new TextDecoder();
|
|
1629
|
+
let buffer = "";
|
|
1630
|
+
while (true) {
|
|
1631
|
+
const { done, value } = await reader.read();
|
|
1632
|
+
if (done) break;
|
|
1633
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1634
|
+
const events = buffer.split("\n\n");
|
|
1635
|
+
buffer = events.pop() || "";
|
|
1636
|
+
for (const eventStr of events) {
|
|
1637
|
+
if (!eventStr.trim()) continue;
|
|
1638
|
+
const lines = eventStr.split("\n");
|
|
1639
|
+
let eventType = "";
|
|
1640
|
+
let eventData = "";
|
|
1641
|
+
for (const line of lines) {
|
|
1642
|
+
if (line.startsWith("event: ")) {
|
|
1643
|
+
eventType = line.slice(7);
|
|
1644
|
+
} else if (line.startsWith("data: ")) {
|
|
1645
|
+
eventData = line.slice(6);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
if (!eventType || !eventData) continue;
|
|
1649
|
+
try {
|
|
1650
|
+
const data = JSON.parse(eventData);
|
|
1651
|
+
if (eventType === "progress") {
|
|
1652
|
+
if (data.status === "thinking") {
|
|
1653
|
+
setPhase("thinking");
|
|
1654
|
+
setProgressSteps([{ message: data.message, completed: false }]);
|
|
1655
|
+
} else if (data.status === "executing") {
|
|
1656
|
+
setPhase("executing");
|
|
1657
|
+
setProgressSteps((prev) => {
|
|
1658
|
+
const updated = prev.map((step) => ({ ...step, completed: true }));
|
|
1659
|
+
const lastStep = updated[updated.length - 1];
|
|
1660
|
+
if (!lastStep || lastStep.message !== data.message) {
|
|
1661
|
+
updated.push({ message: data.message, completed: false });
|
|
1662
|
+
}
|
|
1663
|
+
return updated;
|
|
1664
|
+
});
|
|
1665
|
+
} else if (data.status === "responding") {
|
|
1666
|
+
setPhase("responding");
|
|
1667
|
+
setProgressSteps((prev) => prev.map((step) => ({ ...step, completed: true })));
|
|
1668
|
+
}
|
|
1669
|
+
} else if (eventType === "response") {
|
|
1670
|
+
setProgressSteps([]);
|
|
1671
|
+
streamCompleted = true;
|
|
1672
|
+
const agentResponse = data;
|
|
1673
|
+
if (agentResponse.action === "navigate" && agentResponse.navigation) {
|
|
1674
|
+
const navTarget = {
|
|
1675
|
+
page: agentResponse.navigation.page,
|
|
1676
|
+
subtab: agentResponse.navigation.subtab
|
|
1677
|
+
};
|
|
1678
|
+
setPendingNavigation(navTarget);
|
|
1679
|
+
const navigationMessage = {
|
|
1680
|
+
id: now + 1,
|
|
1681
|
+
role: "assistant",
|
|
1682
|
+
kind: "navigationAction",
|
|
1683
|
+
content: agentResponse.response || `I can take you to the ${getPageDisplayName(navTarget)}.`,
|
|
1684
|
+
navigationTarget: navTarget
|
|
1685
|
+
};
|
|
1686
|
+
setMessages((prev) => [...prev, navigationMessage]);
|
|
1687
|
+
setPhase("idle");
|
|
1688
|
+
} else if (agentResponse.action === "show_form" && agentResponse.actionType) {
|
|
1689
|
+
setPendingAction({
|
|
1690
|
+
type: agentResponse.actionType,
|
|
1691
|
+
data: agentResponse.actionData || {}
|
|
1692
|
+
});
|
|
1693
|
+
setActionFormData(agentResponse.actionData || {});
|
|
1694
|
+
const actionMessage = {
|
|
1695
|
+
id: now + 1,
|
|
1696
|
+
role: "assistant",
|
|
1697
|
+
kind: "actionForm",
|
|
1698
|
+
content: agentResponse.response || agentResponse.message || "Please fill in the details below:",
|
|
1699
|
+
actionType: agentResponse.actionType,
|
|
1700
|
+
actionData: agentResponse.actionData || {}
|
|
1701
|
+
};
|
|
1702
|
+
setMessages((prev) => [...prev, actionMessage]);
|
|
1703
|
+
setPhase("idle");
|
|
1704
|
+
} else if (agentResponse.action === "suggest_bulk") {
|
|
1705
|
+
const assistantMessageId = now + 1;
|
|
1706
|
+
const assistantMessage = {
|
|
1707
|
+
id: assistantMessageId,
|
|
1708
|
+
role: "assistant",
|
|
1709
|
+
kind: "text",
|
|
1710
|
+
content: ""
|
|
1711
|
+
};
|
|
1712
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
1713
|
+
streamAssistantMessage(assistantMessageId, agentResponse.response || agentResponse.message || "For bulk operations, you can upload a CSV file - just click the \u{1F4CE} button!", agentResponse.followups);
|
|
1714
|
+
streamCompleted = true;
|
|
1715
|
+
} else if (agentResponse.action === "bulk_preview") {
|
|
1716
|
+
setPendingBulkSession(agentResponse.bulk_session_id);
|
|
1717
|
+
const previewMessage = {
|
|
1718
|
+
id: now + 1,
|
|
1719
|
+
role: "assistant",
|
|
1720
|
+
kind: "bulkPreview",
|
|
1721
|
+
content: agentResponse.message || `I found ${agentResponse.csv_data?.rowCount || 0} items. Here's a preview:`,
|
|
1722
|
+
csvData: agentResponse.csv_data ? {
|
|
1723
|
+
rowCount: agentResponse.csv_data.rowCount,
|
|
1724
|
+
columns: agentResponse.csv_data.columns,
|
|
1725
|
+
sampleRows: agentResponse.csv_data.sampleRows,
|
|
1726
|
+
fileName: "From context"
|
|
1727
|
+
} : void 0,
|
|
1728
|
+
suggestedAction: agentResponse.suggested_action,
|
|
1729
|
+
bulkSessionId: agentResponse.bulk_session_id
|
|
1730
|
+
};
|
|
1731
|
+
setMessages((prev) => [...prev, previewMessage]);
|
|
1732
|
+
setPhase("idle");
|
|
1733
|
+
streamCompleted = true;
|
|
1734
|
+
} else if (agentResponse.action === "execute" && agentResponse.executionResult) {
|
|
1735
|
+
const assistantMessageId = now + 1;
|
|
1736
|
+
const assistantMessage = {
|
|
1737
|
+
id: assistantMessageId,
|
|
1738
|
+
role: "assistant",
|
|
1739
|
+
kind: "text",
|
|
1740
|
+
content: "",
|
|
1741
|
+
// Include structured data for rich UI rendering
|
|
1742
|
+
structuredData: agentResponse.structuredData || void 0
|
|
1743
|
+
};
|
|
1744
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
1745
|
+
streamAssistantMessage(assistantMessageId, agentResponse.response, agentResponse.followups);
|
|
1746
|
+
streamCompleted = true;
|
|
1747
|
+
setTimeout(() => {
|
|
1748
|
+
window.dispatchEvent(new CustomEvent("agentActionComplete", {
|
|
1749
|
+
detail: { result: agentResponse.executionResult }
|
|
1750
|
+
}));
|
|
1751
|
+
}, 100);
|
|
1752
|
+
} else {
|
|
1753
|
+
const assistantMessageId = now + 1;
|
|
1754
|
+
const assistantMessage = {
|
|
1755
|
+
id: assistantMessageId,
|
|
1756
|
+
role: "assistant",
|
|
1757
|
+
kind: "text",
|
|
1758
|
+
content: ""
|
|
1759
|
+
};
|
|
1760
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
1761
|
+
streamAssistantMessage(assistantMessageId, agentResponse.response, agentResponse.followups);
|
|
1762
|
+
streamCompleted = true;
|
|
1763
|
+
}
|
|
1764
|
+
} else if (eventType === "error") {
|
|
1765
|
+
setPhase("idle");
|
|
1766
|
+
setProgressSteps([]);
|
|
1767
|
+
streamCompleted = true;
|
|
1768
|
+
const errorMessageId = now + 1;
|
|
1769
|
+
const errorMessage = {
|
|
1770
|
+
id: errorMessageId,
|
|
1771
|
+
role: "assistant",
|
|
1772
|
+
kind: "text",
|
|
1773
|
+
content: ""
|
|
1774
|
+
};
|
|
1775
|
+
setMessages((prev) => [...prev, errorMessage]);
|
|
1776
|
+
streamAssistantMessage(errorMessageId, data.message || "An error occurred processing your request.");
|
|
1777
|
+
} else if (eventType === "done") {
|
|
1778
|
+
setProgressSteps([]);
|
|
1779
|
+
setPhase("idle");
|
|
1780
|
+
streamCompleted = true;
|
|
1781
|
+
}
|
|
1782
|
+
} catch (parseError) {
|
|
1783
|
+
console.error("Failed to parse SSE event:", parseError);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
setProgressSteps([]);
|
|
1788
|
+
if (!streamCompleted) {
|
|
1789
|
+
setPhase("idle");
|
|
1790
|
+
}
|
|
1791
|
+
} catch (error) {
|
|
1792
|
+
console.error("Agent request failed:", error);
|
|
1793
|
+
setProgressSteps([]);
|
|
1794
|
+
const errorMessageId = now + 1;
|
|
1795
|
+
const errorMessage = {
|
|
1796
|
+
id: errorMessageId,
|
|
1797
|
+
role: "assistant",
|
|
1798
|
+
kind: "text",
|
|
1799
|
+
content: ""
|
|
1800
|
+
};
|
|
1801
|
+
setMessages((prev) => [...prev, errorMessage]);
|
|
1802
|
+
streamAssistantMessage(errorMessageId, "I'm having trouble connecting to my backend. Please make sure the agent server is running on port 5002. For simple navigation requests, I can still help you locally.");
|
|
1803
|
+
} finally {
|
|
1804
|
+
setProgressSteps([]);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
async function startBulkUploadFlow(userText, file) {
|
|
1808
|
+
phaseTimers.current.forEach((id) => window.clearTimeout(id));
|
|
1809
|
+
phaseTimers.current = [];
|
|
1810
|
+
const now = Date.now();
|
|
1811
|
+
const userMessage = {
|
|
1812
|
+
id: now,
|
|
1813
|
+
role: "user",
|
|
1814
|
+
content: `\u{1F4CE} ${file.name}
|
|
1815
|
+
|
|
1816
|
+
${userText}`
|
|
1817
|
+
};
|
|
1818
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
1819
|
+
setPhase("thinking");
|
|
1820
|
+
setProgressSteps([{ message: "Analyzing CSV file...", completed: false }]);
|
|
1821
|
+
try {
|
|
1822
|
+
const formData = new FormData();
|
|
1823
|
+
formData.append("file", file);
|
|
1824
|
+
formData.append("message", userText);
|
|
1825
|
+
formData.append("session_id", sessionId);
|
|
1826
|
+
formData.append("current_page", currentPage || "dashboard");
|
|
1827
|
+
const controller = new AbortController();
|
|
1828
|
+
const timeoutId = setTimeout(() => controller.abort(), 12e4);
|
|
1829
|
+
const response = await fetch(`${agentUrl}/chat/bulk/stream`, {
|
|
1830
|
+
method: "POST",
|
|
1831
|
+
body: formData,
|
|
1832
|
+
signal: controller.signal
|
|
1833
|
+
});
|
|
1834
|
+
clearTimeout(timeoutId);
|
|
1835
|
+
if (!response.ok) {
|
|
1836
|
+
throw new Error(`Bulk upload failed: ${response.status}`);
|
|
1837
|
+
}
|
|
1838
|
+
const reader = response.body?.getReader();
|
|
1839
|
+
if (!reader) throw new Error("No response body");
|
|
1840
|
+
const decoder = new TextDecoder();
|
|
1841
|
+
let buffer = "";
|
|
1842
|
+
let bulkSessionId = null;
|
|
1843
|
+
while (true) {
|
|
1844
|
+
const { done, value } = await reader.read();
|
|
1845
|
+
if (done) break;
|
|
1846
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1847
|
+
const events = buffer.split("\n\n");
|
|
1848
|
+
buffer = events.pop() || "";
|
|
1849
|
+
for (const eventStr of events) {
|
|
1850
|
+
if (!eventStr.trim()) continue;
|
|
1851
|
+
const lines = eventStr.split("\n");
|
|
1852
|
+
let eventType = "";
|
|
1853
|
+
let eventData = "";
|
|
1854
|
+
for (const line of lines) {
|
|
1855
|
+
if (line.startsWith("event: ")) {
|
|
1856
|
+
eventType = line.slice(7);
|
|
1857
|
+
} else if (line.startsWith("data: ")) {
|
|
1858
|
+
eventData = line.slice(6);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
if (!eventType || !eventData) continue;
|
|
1862
|
+
try {
|
|
1863
|
+
const data = JSON.parse(eventData);
|
|
1864
|
+
if (eventType === "preview") {
|
|
1865
|
+
setPhase("idle");
|
|
1866
|
+
setProgressSteps([]);
|
|
1867
|
+
bulkSessionId = data.bulk_session_id;
|
|
1868
|
+
setPendingBulkSession(bulkSessionId);
|
|
1869
|
+
const previewMessage = {
|
|
1870
|
+
id: now + 1,
|
|
1871
|
+
role: "assistant",
|
|
1872
|
+
kind: "bulkPreview",
|
|
1873
|
+
content: data.message || `I found ${data.csv_data.rowCount} rows in your CSV. Here's a preview:`,
|
|
1874
|
+
csvData: {
|
|
1875
|
+
rowCount: data.csv_data.rowCount,
|
|
1876
|
+
columns: data.csv_data.columns,
|
|
1877
|
+
sampleRows: data.csv_data.sampleRows,
|
|
1878
|
+
fileName: file.name
|
|
1879
|
+
},
|
|
1880
|
+
suggestedAction: data.suggested_action,
|
|
1881
|
+
bulkSessionId: bulkSessionId || void 0
|
|
1882
|
+
};
|
|
1883
|
+
setMessages((prev) => [...prev, previewMessage]);
|
|
1884
|
+
} else if (eventType === "progress") {
|
|
1885
|
+
setPhase("executing");
|
|
1886
|
+
setProgressSteps([{
|
|
1887
|
+
message: `Processing row ${data.row} of ${data.total}...`,
|
|
1888
|
+
completed: false
|
|
1889
|
+
}]);
|
|
1890
|
+
setMessages((prev) => {
|
|
1891
|
+
const lastMsg = prev[prev.length - 1];
|
|
1892
|
+
if (lastMsg?.kind === "bulkProgress") {
|
|
1893
|
+
return prev.map(
|
|
1894
|
+
(m, i) => i === prev.length - 1 ? { ...m, bulkProgress: {
|
|
1895
|
+
processed: data.row,
|
|
1896
|
+
total: data.total,
|
|
1897
|
+
successes: data.successes || 0,
|
|
1898
|
+
failures: data.failures || 0
|
|
1899
|
+
} } : m
|
|
1900
|
+
);
|
|
1901
|
+
} else {
|
|
1902
|
+
return [...prev, {
|
|
1903
|
+
id: Date.now(),
|
|
1904
|
+
role: "assistant",
|
|
1905
|
+
kind: "bulkProgress",
|
|
1906
|
+
bulkProgress: {
|
|
1907
|
+
processed: data.row,
|
|
1908
|
+
total: data.total,
|
|
1909
|
+
successes: data.successes || 0,
|
|
1910
|
+
failures: data.failures || 0
|
|
1911
|
+
}
|
|
1912
|
+
}];
|
|
1913
|
+
}
|
|
1914
|
+
});
|
|
1915
|
+
} else if (eventType === "summary") {
|
|
1916
|
+
setPhase("idle");
|
|
1917
|
+
setProgressSteps([]);
|
|
1918
|
+
setPendingBulkSession(null);
|
|
1919
|
+
setMessages((prev) => {
|
|
1920
|
+
const filtered = prev.filter((m) => m.kind !== "bulkProgress");
|
|
1921
|
+
return [...filtered, {
|
|
1922
|
+
id: Date.now(),
|
|
1923
|
+
role: "assistant",
|
|
1924
|
+
kind: "bulkSummary",
|
|
1925
|
+
content: data.message,
|
|
1926
|
+
bulkSummary: {
|
|
1927
|
+
total: data.total,
|
|
1928
|
+
successes: data.successes,
|
|
1929
|
+
failures: data.failures || [],
|
|
1930
|
+
navigationPage: data.navigationPage
|
|
1931
|
+
}
|
|
1932
|
+
}];
|
|
1933
|
+
});
|
|
1934
|
+
setTimeout(() => {
|
|
1935
|
+
window.dispatchEvent(new CustomEvent("agentActionComplete", {
|
|
1936
|
+
detail: { result: { bulk: true, total: data.total, successes: data.successes } }
|
|
1937
|
+
}));
|
|
1938
|
+
}, 100);
|
|
1939
|
+
} else if (eventType === "error") {
|
|
1940
|
+
setPhase("idle");
|
|
1941
|
+
setProgressSteps([]);
|
|
1942
|
+
setPendingBulkSession(null);
|
|
1943
|
+
const errorMessage = {
|
|
1944
|
+
id: now + 1,
|
|
1945
|
+
role: "assistant",
|
|
1946
|
+
kind: "text",
|
|
1947
|
+
content: data.message || "An error occurred processing your CSV file."
|
|
1948
|
+
};
|
|
1949
|
+
setMessages((prev) => [...prev, errorMessage]);
|
|
1950
|
+
} else if (eventType === "done") {
|
|
1951
|
+
setPhase("idle");
|
|
1952
|
+
setProgressSteps([]);
|
|
1953
|
+
}
|
|
1954
|
+
} catch (parseError) {
|
|
1955
|
+
console.error("Failed to parse bulk SSE event:", parseError);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
setProgressSteps([]);
|
|
1960
|
+
setPhase("idle");
|
|
1961
|
+
} catch (error) {
|
|
1962
|
+
console.error("Bulk upload failed:", error);
|
|
1963
|
+
setProgressSteps([]);
|
|
1964
|
+
setPhase("idle");
|
|
1965
|
+
const errorMessage = {
|
|
1966
|
+
id: now + 1,
|
|
1967
|
+
role: "assistant",
|
|
1968
|
+
kind: "text",
|
|
1969
|
+
content: "I'm having trouble processing your CSV file. Please make sure the agent server is running on port 5002 and try again."
|
|
1970
|
+
};
|
|
1971
|
+
setMessages((prev) => [...prev, errorMessage]);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
async function confirmBulkOperation(bulkSessionId) {
|
|
1975
|
+
setPhase("executing");
|
|
1976
|
+
setProgressSteps([{ message: "Starting bulk operation...", completed: false }]);
|
|
1977
|
+
try {
|
|
1978
|
+
const response = await fetch(`${agentUrl}/chat/bulk/confirm`, {
|
|
1979
|
+
method: "POST",
|
|
1980
|
+
headers: { "Content-Type": "application/json" },
|
|
1981
|
+
body: JSON.stringify({ bulk_session_id: bulkSessionId })
|
|
1982
|
+
});
|
|
1983
|
+
if (!response.ok) {
|
|
1984
|
+
throw new Error(`Confirm failed: ${response.status}`);
|
|
1985
|
+
}
|
|
1986
|
+
const reader = response.body?.getReader();
|
|
1987
|
+
if (!reader) throw new Error("No response body");
|
|
1988
|
+
const decoder = new TextDecoder();
|
|
1989
|
+
let buffer = "";
|
|
1990
|
+
while (true) {
|
|
1991
|
+
const { done, value } = await reader.read();
|
|
1992
|
+
if (done) break;
|
|
1993
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1994
|
+
const events = buffer.split("\n\n");
|
|
1995
|
+
buffer = events.pop() || "";
|
|
1996
|
+
for (const eventStr of events) {
|
|
1997
|
+
if (!eventStr.trim()) continue;
|
|
1998
|
+
const lines = eventStr.split("\n");
|
|
1999
|
+
let eventType = "";
|
|
2000
|
+
let eventData = "";
|
|
2001
|
+
for (const line of lines) {
|
|
2002
|
+
if (line.startsWith("event: ")) {
|
|
2003
|
+
eventType = line.slice(7);
|
|
2004
|
+
} else if (line.startsWith("data: ")) {
|
|
2005
|
+
eventData = line.slice(6);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
if (!eventType || !eventData) continue;
|
|
2009
|
+
try {
|
|
2010
|
+
const data = JSON.parse(eventData);
|
|
2011
|
+
if (eventType === "progress") {
|
|
2012
|
+
setProgressSteps([{
|
|
2013
|
+
message: `Processing row ${data.row} of ${data.total}...`,
|
|
2014
|
+
completed: false
|
|
2015
|
+
}]);
|
|
2016
|
+
setMessages((prev) => {
|
|
2017
|
+
const lastMsg = prev[prev.length - 1];
|
|
2018
|
+
if (lastMsg?.kind === "bulkProgress") {
|
|
2019
|
+
return prev.map(
|
|
2020
|
+
(m, i) => i === prev.length - 1 ? { ...m, bulkProgress: {
|
|
2021
|
+
processed: data.row,
|
|
2022
|
+
total: data.total,
|
|
2023
|
+
successes: data.successes || 0,
|
|
2024
|
+
failures: data.failures || 0
|
|
2025
|
+
} } : m
|
|
2026
|
+
);
|
|
2027
|
+
} else {
|
|
2028
|
+
return [...prev, {
|
|
2029
|
+
id: Date.now(),
|
|
2030
|
+
role: "assistant",
|
|
2031
|
+
kind: "bulkProgress",
|
|
2032
|
+
bulkProgress: {
|
|
2033
|
+
processed: data.row,
|
|
2034
|
+
total: data.total,
|
|
2035
|
+
successes: data.successes || 0,
|
|
2036
|
+
failures: data.failures || 0
|
|
2037
|
+
}
|
|
2038
|
+
}];
|
|
2039
|
+
}
|
|
2040
|
+
});
|
|
2041
|
+
} else if (eventType === "summary") {
|
|
2042
|
+
console.log("[DEBUG] Received summary event - data:", data);
|
|
2043
|
+
console.log("[DEBUG] navigationPage from backend:", data.navigationPage);
|
|
2044
|
+
setPhase("idle");
|
|
2045
|
+
setProgressSteps([]);
|
|
2046
|
+
setPendingBulkSession(null);
|
|
2047
|
+
setMessages((prev) => {
|
|
2048
|
+
const filtered = prev.filter((m) => m.kind !== "bulkProgress" && m.kind !== "bulkPreview");
|
|
2049
|
+
const newMsg = {
|
|
2050
|
+
id: Date.now(),
|
|
2051
|
+
role: "assistant",
|
|
2052
|
+
kind: "bulkSummary",
|
|
2053
|
+
content: data.message,
|
|
2054
|
+
bulkSummary: {
|
|
2055
|
+
total: data.total,
|
|
2056
|
+
successes: data.successes,
|
|
2057
|
+
failures: data.failures || [],
|
|
2058
|
+
navigationPage: data.navigationPage
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
console.log("[DEBUG] Creating bulkSummary message:", newMsg);
|
|
2062
|
+
return [...filtered, newMsg];
|
|
2063
|
+
});
|
|
2064
|
+
setTimeout(() => {
|
|
2065
|
+
window.dispatchEvent(new CustomEvent("agentActionComplete", {
|
|
2066
|
+
detail: { result: { bulk: true, total: data.total, successes: data.successes } }
|
|
2067
|
+
}));
|
|
2068
|
+
}, 100);
|
|
2069
|
+
} else if (eventType === "error") {
|
|
2070
|
+
setPhase("idle");
|
|
2071
|
+
setProgressSteps([]);
|
|
2072
|
+
setPendingBulkSession(null);
|
|
2073
|
+
const errorMessage = {
|
|
2074
|
+
id: Date.now(),
|
|
2075
|
+
role: "assistant",
|
|
2076
|
+
kind: "text",
|
|
2077
|
+
content: data.message || "An error occurred during bulk processing."
|
|
2078
|
+
};
|
|
2079
|
+
setMessages((prev) => [...prev, errorMessage]);
|
|
2080
|
+
}
|
|
2081
|
+
} catch (parseError) {
|
|
2082
|
+
console.error("Failed to parse confirm SSE event:", parseError);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
} catch (error) {
|
|
2087
|
+
console.error("Bulk confirm failed:", error);
|
|
2088
|
+
setPhase("idle");
|
|
2089
|
+
setProgressSteps([]);
|
|
2090
|
+
setPendingBulkSession(null);
|
|
2091
|
+
const errorMessage = {
|
|
2092
|
+
id: Date.now(),
|
|
2093
|
+
role: "assistant",
|
|
2094
|
+
kind: "text",
|
|
2095
|
+
content: "Failed to start bulk operation. Please try again."
|
|
2096
|
+
};
|
|
2097
|
+
setMessages((prev) => [...prev, errorMessage]);
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
function cancelBulkOperation() {
|
|
2101
|
+
setPendingBulkSession(null);
|
|
2102
|
+
setMessages((prev) => prev.filter((m) => m.kind !== "bulkPreview"));
|
|
2103
|
+
}
|
|
2104
|
+
function sendTopic(prompt) {
|
|
2105
|
+
startChatFlow(prompt);
|
|
2106
|
+
}
|
|
2107
|
+
function appendAssistantText(text) {
|
|
2108
|
+
const id = Date.now() + Math.floor(Math.random() * 1e3);
|
|
2109
|
+
setMessages((prev) => [...prev, { id, role: "assistant", kind: "text", content: text }]);
|
|
2110
|
+
}
|
|
2111
|
+
function startGuide(guideId) {
|
|
2112
|
+
const guide = guides[guideId];
|
|
2113
|
+
if (!guide) return;
|
|
2114
|
+
setPanelView("landing");
|
|
2115
|
+
setCurrentFolderId(void 0);
|
|
2116
|
+
const initialGuideState = { id: guideId, stepIndex: 0 };
|
|
2117
|
+
setActiveGuide(initialGuideState);
|
|
2118
|
+
activeGuideRef.current = initialGuideState;
|
|
2119
|
+
appendAssistantText(`I'll help you with ${guide.title}.`);
|
|
2120
|
+
const firstStep = guide.steps[0];
|
|
2121
|
+
const isNavigationButton = firstStep.cursorTarget?.selector.includes("[data-page=");
|
|
2122
|
+
const isTabButton = firstStep.cursorTarget?.selector.includes("[data-settings-tab=");
|
|
2123
|
+
if (firstStep.cursorTarget && (isNavigationButton || isTabButton)) {
|
|
2124
|
+
const cursorTarget = firstStep.cursorTarget;
|
|
2125
|
+
if (isTabButton && firstStep.navigation && onNavigate) {
|
|
2126
|
+
onNavigate(firstStep.navigation.page, void 0);
|
|
2127
|
+
}
|
|
2128
|
+
const initialDelay = isTabButton ? 800 : 400;
|
|
2129
|
+
setTimeout(() => {
|
|
2130
|
+
const waitForElement = () => {
|
|
2131
|
+
const element = document.querySelector(cursorTarget.selector);
|
|
2132
|
+
if (element && element.offsetParent !== null) {
|
|
2133
|
+
moveTo(cursorTarget);
|
|
2134
|
+
if (cursorTarget.onClick) {
|
|
2135
|
+
setTimeout(() => {
|
|
2136
|
+
element.click();
|
|
2137
|
+
}, 1200);
|
|
2138
|
+
}
|
|
2139
|
+
} else {
|
|
2140
|
+
setTimeout(waitForElement, 200);
|
|
2141
|
+
}
|
|
2142
|
+
};
|
|
2143
|
+
waitForElement();
|
|
2144
|
+
}, initialDelay);
|
|
2145
|
+
} else {
|
|
2146
|
+
if (firstStep.navigation && onNavigate) {
|
|
2147
|
+
onNavigate(firstStep.navigation.page, firstStep.navigation.subtab);
|
|
2148
|
+
}
|
|
2149
|
+
if (firstStep.cursorTarget) {
|
|
2150
|
+
const cursorTarget = firstStep.cursorTarget;
|
|
2151
|
+
const isSettingsTab = cursorTarget.selector.includes("data-settings-tab");
|
|
2152
|
+
const isDialogElement = cursorTarget.selector.includes("dialog") || cursorTarget.selector.includes("api-key-name-input");
|
|
2153
|
+
const initialDelay = isDialogElement ? 700 : isSettingsTab ? 600 : 400;
|
|
2154
|
+
setTimeout(() => {
|
|
2155
|
+
const waitForElement = () => {
|
|
2156
|
+
const element = document.querySelector(cursorTarget.selector);
|
|
2157
|
+
if (element && element.offsetParent !== null) {
|
|
2158
|
+
moveTo(cursorTarget);
|
|
2159
|
+
if (cursorTarget.onClick) {
|
|
2160
|
+
setTimeout(() => {
|
|
2161
|
+
element.click();
|
|
2162
|
+
}, 1e3);
|
|
2163
|
+
}
|
|
2164
|
+
} else if (isSettingsTab || isDialogElement) {
|
|
2165
|
+
setTimeout(waitForElement, 200);
|
|
2166
|
+
} else {
|
|
2167
|
+
moveTo(cursorTarget);
|
|
2168
|
+
if (cursorTarget.onClick) {
|
|
2169
|
+
setTimeout(() => {
|
|
2170
|
+
const el = document.querySelector(cursorTarget.selector);
|
|
2171
|
+
if (el) el.click();
|
|
2172
|
+
}, 1e3);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
};
|
|
2176
|
+
waitForElement();
|
|
2177
|
+
}, initialDelay);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
const draft = {
|
|
2181
|
+
id: Date.now() + 1,
|
|
2182
|
+
role: "assistant",
|
|
2183
|
+
kind: "guideStep",
|
|
2184
|
+
content: "",
|
|
2185
|
+
guideStepIndex: 0
|
|
2186
|
+
// First step is index 0
|
|
2187
|
+
};
|
|
2188
|
+
setMessages((prev) => [...prev, draft]);
|
|
2189
|
+
streamAssistantMessage(draft.id, firstStep.text);
|
|
2190
|
+
}
|
|
2191
|
+
function advanceGuide() {
|
|
2192
|
+
if (!activeGuide) return;
|
|
2193
|
+
const guide = guides[activeGuide.id];
|
|
2194
|
+
if (!guide) return;
|
|
2195
|
+
let currentGuide = activeGuideRef.current;
|
|
2196
|
+
if (!currentGuide || currentGuide.id !== activeGuide.id) {
|
|
2197
|
+
currentGuide = activeGuide;
|
|
2198
|
+
activeGuideRef.current = activeGuide;
|
|
2199
|
+
}
|
|
2200
|
+
const currentStepIndex = currentGuide.stepIndex;
|
|
2201
|
+
const nextIndex = currentStepIndex + 1;
|
|
2202
|
+
if (nextIndex >= guide.steps.length) {
|
|
2203
|
+
hide();
|
|
2204
|
+
const id = Date.now() + 1;
|
|
2205
|
+
setMessages((prev) => [
|
|
2206
|
+
...prev,
|
|
2207
|
+
{ id, role: "assistant", kind: "guideComplete", content: "Guide Complete" }
|
|
2208
|
+
]);
|
|
2209
|
+
setActiveGuide(void 0);
|
|
2210
|
+
setGuideComplete(true);
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
const nextStep = guide.steps[nextIndex];
|
|
2214
|
+
const newGuideState = { id: activeGuide.id, stepIndex: nextIndex };
|
|
2215
|
+
setActiveGuide(newGuideState);
|
|
2216
|
+
activeGuideRef.current = newGuideState;
|
|
2217
|
+
const draft = {
|
|
2218
|
+
id: Date.now() + 1,
|
|
2219
|
+
role: "assistant",
|
|
2220
|
+
kind: "guideStep",
|
|
2221
|
+
content: "",
|
|
2222
|
+
guideStepIndex: nextIndex
|
|
2223
|
+
};
|
|
2224
|
+
setMessages((prev) => [...prev, draft]);
|
|
2225
|
+
const isNavigationButton = nextStep.cursorTarget?.selector.includes("[data-page=");
|
|
2226
|
+
const isTabButton = nextStep.cursorTarget?.selector.includes("[data-settings-tab=");
|
|
2227
|
+
if (nextStep.cursorTarget && (isNavigationButton || isTabButton)) {
|
|
2228
|
+
const cursorTarget = nextStep.cursorTarget;
|
|
2229
|
+
if (isTabButton && nextStep.navigation && onNavigate) {
|
|
2230
|
+
onNavigate(nextStep.navigation.page, void 0);
|
|
2231
|
+
}
|
|
2232
|
+
const initialDelay = isTabButton ? 800 : 400;
|
|
2233
|
+
setTimeout(() => {
|
|
2234
|
+
const currentGuide2 = activeGuideRef.current;
|
|
2235
|
+
if (!currentGuide2 || currentGuide2.id !== guide.id || currentGuide2.stepIndex !== nextIndex) {
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
const waitForElement = () => {
|
|
2239
|
+
const currentGuide3 = activeGuideRef.current;
|
|
2240
|
+
if (!currentGuide3 || currentGuide3.id !== guide.id || currentGuide3.stepIndex !== nextIndex) {
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
const element = document.querySelector(cursorTarget.selector);
|
|
2244
|
+
if (element && element.offsetParent !== null) {
|
|
2245
|
+
moveTo(cursorTarget);
|
|
2246
|
+
if (cursorTarget.onClick) {
|
|
2247
|
+
setTimeout(() => {
|
|
2248
|
+
const currentGuide4 = activeGuideRef.current;
|
|
2249
|
+
if (!currentGuide4 || currentGuide4.id !== guide.id || currentGuide4.stepIndex !== nextIndex) {
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
element.click();
|
|
2253
|
+
}, 1200);
|
|
2254
|
+
}
|
|
2255
|
+
} else {
|
|
2256
|
+
setTimeout(waitForElement, 200);
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2259
|
+
waitForElement();
|
|
2260
|
+
}, initialDelay);
|
|
2261
|
+
} else {
|
|
2262
|
+
if (nextStep.navigation && onNavigate) {
|
|
2263
|
+
onNavigate(nextStep.navigation.page, nextStep.navigation.subtab);
|
|
2264
|
+
}
|
|
2265
|
+
if (nextStep.cursorTarget) {
|
|
2266
|
+
const cursorTarget = nextStep.cursorTarget;
|
|
2267
|
+
const isDialogElement = cursorTarget.selector.includes("dialog") || cursorTarget.selector.includes("api-key-name-input");
|
|
2268
|
+
const isSettingsTab = cursorTarget.selector.includes("data-settings-tab");
|
|
2269
|
+
const navigationSetTab = nextStep.navigation?.subtab && isSettingsTab && cursorTarget.selector.includes(`data-settings-tab="${nextStep.navigation.subtab}"`);
|
|
2270
|
+
const shouldAutoClick = cursorTarget.onClick && !navigationSetTab;
|
|
2271
|
+
const hasNavigation = !!nextStep.navigation;
|
|
2272
|
+
const hasSubtab = !!nextStep.navigation?.subtab;
|
|
2273
|
+
const navigationDelay = hasSubtab ? 700 : hasNavigation ? 400 : 0;
|
|
2274
|
+
const initialDelay = navigationDelay + (isDialogElement ? 700 : isSettingsTab ? 600 : 400);
|
|
2275
|
+
setTimeout(() => {
|
|
2276
|
+
const currentGuide2 = activeGuideRef.current;
|
|
2277
|
+
if (!currentGuide2 || currentGuide2.id !== guide.id || currentGuide2.stepIndex !== nextIndex) {
|
|
2278
|
+
return;
|
|
2279
|
+
}
|
|
2280
|
+
const waitForElement = () => {
|
|
2281
|
+
const currentGuide3 = activeGuideRef.current;
|
|
2282
|
+
if (!currentGuide3 || currentGuide3.id !== guide.id || currentGuide3.stepIndex !== nextIndex) {
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
const element = document.querySelector(cursorTarget.selector);
|
|
2286
|
+
if (element && element.offsetParent !== null) {
|
|
2287
|
+
moveTo(cursorTarget);
|
|
2288
|
+
if (shouldAutoClick) {
|
|
2289
|
+
setTimeout(() => {
|
|
2290
|
+
const currentGuide4 = activeGuideRef.current;
|
|
2291
|
+
if (!currentGuide4 || currentGuide4.id !== guide.id || currentGuide4.stepIndex !== nextIndex) {
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
element.click();
|
|
2295
|
+
}, 1e3);
|
|
2296
|
+
}
|
|
2297
|
+
} else if (isDialogElement || isSettingsTab || cursorTarget.selector.includes("data-testid")) {
|
|
2298
|
+
setTimeout(waitForElement, 200);
|
|
2299
|
+
} else {
|
|
2300
|
+
moveTo(cursorTarget);
|
|
2301
|
+
if (shouldAutoClick) {
|
|
2302
|
+
setTimeout(() => {
|
|
2303
|
+
const currentGuide4 = activeGuideRef.current;
|
|
2304
|
+
if (!currentGuide4 || currentGuide4.id !== guide.id || currentGuide4.stepIndex !== nextIndex) {
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
const el = document.querySelector(cursorTarget.selector);
|
|
2308
|
+
if (el) el.click();
|
|
2309
|
+
}, 1e3);
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
};
|
|
2313
|
+
waitForElement();
|
|
2314
|
+
}, initialDelay);
|
|
2315
|
+
} else {
|
|
2316
|
+
hide();
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
streamAssistantMessage(draft.id, nextStep.text);
|
|
2320
|
+
}
|
|
2321
|
+
function goBackGuide() {
|
|
2322
|
+
if (!activeGuide) return;
|
|
2323
|
+
const guide = guides[activeGuide.id];
|
|
2324
|
+
if (!guide) return;
|
|
2325
|
+
const prevIndex = activeGuide.stepIndex - 1;
|
|
2326
|
+
if (prevIndex >= 0) {
|
|
2327
|
+
setActiveGuide({ id: activeGuide.id, stepIndex: prevIndex });
|
|
2328
|
+
const prevStep = guide.steps[prevIndex];
|
|
2329
|
+
if (prevStep.navigation && onNavigate) {
|
|
2330
|
+
onNavigate(prevStep.navigation.page, prevStep.navigation.subtab);
|
|
2331
|
+
}
|
|
2332
|
+
if (prevStep.cursorTarget) {
|
|
2333
|
+
setTimeout(() => {
|
|
2334
|
+
moveTo(prevStep.cursorTarget);
|
|
2335
|
+
}, 500);
|
|
2336
|
+
} else {
|
|
2337
|
+
hide();
|
|
2338
|
+
}
|
|
2339
|
+
const existingMessages = messages.filter((m) => m.kind !== "guideStep" && m.kind !== "guideComplete");
|
|
2340
|
+
setMessages([
|
|
2341
|
+
...existingMessages,
|
|
2342
|
+
{ id: Date.now(), role: "assistant", kind: "guideStep", content: prevStep.text }
|
|
2343
|
+
]);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2347
|
+
"section",
|
|
2348
|
+
{
|
|
2349
|
+
className: `fixed top-0 right-0 z-40 flex flex-col bg-white border-l border-gray-200 h-full overflow-hidden transition-transform duration-300 ${isOpen ? "translate-x-0" : "translate-x-full"}`,
|
|
2350
|
+
style: { width: `${PANEL_WIDTH}px` },
|
|
2351
|
+
children: [
|
|
2352
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between px-4 py-3 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white shrink-0", children: [
|
|
2353
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: "text-sm font-semibold text-gray-800", children: "Help" }),
|
|
2354
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2355
|
+
Button,
|
|
2356
|
+
{
|
|
2357
|
+
variant: "ghost",
|
|
2358
|
+
size: "sm",
|
|
2359
|
+
className: "h-7 w-7 p-0 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-full",
|
|
2360
|
+
onClick: () => {
|
|
2361
|
+
setMessages([]);
|
|
2362
|
+
setPanelView("landing");
|
|
2363
|
+
setCurrentFolderId(void 0);
|
|
2364
|
+
setActiveGuide(void 0);
|
|
2365
|
+
activeGuideRef.current = void 0;
|
|
2366
|
+
setGuideComplete(false);
|
|
2367
|
+
},
|
|
2368
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.SquarePen, { className: "h-3.5 w-3.5" })
|
|
2369
|
+
}
|
|
2370
|
+
) })
|
|
2371
|
+
] }),
|
|
2372
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: isEmpty ? "grid flex-1 place-items-center transition-all duration-300" : "flex flex-1 flex-col transition-all duration-300 min-h-0 overflow-hidden", children: isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "w-full overflow-y-auto px-4", children: [
|
|
2373
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "py-4 transition-all duration-300", children: [
|
|
2374
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: "text-center text-2xl font-semibold text-gray-900", children: panelView === "folder" ? folders.find((f) => f.id === currentFolderId)?.title || "" : "What can I help with?" }),
|
|
2375
|
+
panelView === "landing" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "mt-1 text-center text-xs text-gray-500", children: "Ask me anything about your account" })
|
|
2376
|
+
] }),
|
|
2377
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "pb-4", children: [
|
|
2378
|
+
panelView === "landing" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
2379
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mb-2 text-[10px] font-semibold uppercase tracking-wider text-gray-400", children: "Quick Start" }),
|
|
2380
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2381
|
+
Button,
|
|
2382
|
+
{
|
|
2383
|
+
type: "button",
|
|
2384
|
+
size: "sm",
|
|
2385
|
+
variant: "secondary",
|
|
2386
|
+
className: "w-full justify-start rounded-xl border border-primary/20 bg-primary/5 px-3 py-2.5 text-xs font-medium text-primary shadow-none transition-all hover:bg-primary/10 hover:border-primary/30 h-auto mb-4",
|
|
2387
|
+
onClick: () => startGuide("getting-started"),
|
|
2388
|
+
children: [
|
|
2389
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Play, { className: "mr-2 h-3.5 w-3.5" }),
|
|
2390
|
+
"Getting started"
|
|
2391
|
+
]
|
|
2392
|
+
}
|
|
2393
|
+
),
|
|
2394
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mb-2 text-[10px] font-semibold uppercase tracking-wider text-gray-400", children: "Suggested Questions" }),
|
|
2395
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-col gap-1", children: loadingQuestions ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex items-center justify-center py-4", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Loader2, { className: "h-4 w-4 animate-spin text-gray-400" }) }) : startingQuestions.map((question, index) => {
|
|
2396
|
+
const iconColors = ["bg-blue-400", "bg-green-400", "bg-purple-400", "bg-orange-400", "bg-pink-400"];
|
|
2397
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2398
|
+
Button,
|
|
2399
|
+
{
|
|
2400
|
+
type: "button",
|
|
2401
|
+
size: "sm",
|
|
2402
|
+
variant: "ghost",
|
|
2403
|
+
className: "w-full justify-start rounded-lg px-3 py-2 text-xs text-gray-700 hover:bg-gray-100 h-auto",
|
|
2404
|
+
onClick: () => sendTopic(question.prompt),
|
|
2405
|
+
children: [
|
|
2406
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: `mr-2 inline-block h-1.5 w-1.5 rounded-full ${iconColors[index % iconColors.length]}` }),
|
|
2407
|
+
question.label
|
|
2408
|
+
]
|
|
2409
|
+
},
|
|
2410
|
+
question.id
|
|
2411
|
+
);
|
|
2412
|
+
}) })
|
|
2413
|
+
] }),
|
|
2414
|
+
panelView === "folder" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
2415
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mb-3 flex items-center gap-2", children: [
|
|
2416
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2417
|
+
Button,
|
|
2418
|
+
{
|
|
2419
|
+
type: "button",
|
|
2420
|
+
size: "icon",
|
|
2421
|
+
variant: "ghost",
|
|
2422
|
+
className: "h-7 w-7 rounded-full hover:bg-gray-100",
|
|
2423
|
+
onClick: closeFolder,
|
|
2424
|
+
"aria-label": "Back to suggestions",
|
|
2425
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.ArrowLeft, { className: "h-4 w-4 text-gray-500" })
|
|
2426
|
+
}
|
|
2427
|
+
),
|
|
2428
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs font-medium uppercase tracking-wide text-gray-400", children: "Topics" })
|
|
2429
|
+
] }),
|
|
2430
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-col gap-1.5", children: folders.find((f) => f.id === currentFolderId)?.topics.map((topic) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2431
|
+
Button,
|
|
2432
|
+
{
|
|
2433
|
+
type: "button",
|
|
2434
|
+
size: "sm",
|
|
2435
|
+
variant: "secondary",
|
|
2436
|
+
className: "justify-start rounded-xl border border-gray-200 bg-gray-50 px-3 py-2.5 text-xs text-gray-700 shadow-none transition-all hover:bg-gray-100 hover:border-gray-300 h-auto",
|
|
2437
|
+
onClick: () => sendTopic(topic.prompt),
|
|
2438
|
+
children: topic.label
|
|
2439
|
+
},
|
|
2440
|
+
topic.id
|
|
2441
|
+
)) })
|
|
2442
|
+
] })
|
|
2443
|
+
] })
|
|
2444
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex-1 min-h-0 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ScrollArea, { ref: messagesContainerRef, className: "h-full", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col gap-2 px-4 py-3", children: [
|
|
2445
|
+
messages.map((message, index) => {
|
|
2446
|
+
const isUser = message.role === "user";
|
|
2447
|
+
const previousRole = index > 0 ? messages[index - 1].role : void 0;
|
|
2448
|
+
const isRoleChange = previousRole !== void 0 && previousRole !== message.role;
|
|
2449
|
+
const currentGuide = activeGuideRef.current || activeGuide;
|
|
2450
|
+
let isCurrentGuideStep = false;
|
|
2451
|
+
if (currentGuide && message.kind === "guideStep") {
|
|
2452
|
+
if (message.guideStepIndex !== void 0) {
|
|
2453
|
+
isCurrentGuideStep = message.guideStepIndex === currentGuide.stepIndex;
|
|
2454
|
+
} else {
|
|
2455
|
+
isCurrentGuideStep = index === messages.length - 1;
|
|
2456
|
+
}
|
|
2457
|
+
} else if (message.kind === "guideComplete") {
|
|
2458
|
+
isCurrentGuideStep = index === messages.length - 1;
|
|
2459
|
+
}
|
|
2460
|
+
if (message.kind === "guideStep" && !isCurrentGuideStep) {
|
|
2461
|
+
return null;
|
|
2462
|
+
}
|
|
2463
|
+
if (isUser) {
|
|
2464
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `flex justify-end ${isRoleChange ? "mt-3" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "max-w-[280px] rounded-2xl rounded-br-md bg-gray-900 px-3.5 py-2.5 text-sm text-white shadow-sm", children: message.content }) }, message.id);
|
|
2465
|
+
}
|
|
2466
|
+
if (message.kind === "searchSummary") {
|
|
2467
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AssistantSearchSummary, { title: message.title ?? "Search results", links: message.links ?? [] }) }, message.id);
|
|
2468
|
+
}
|
|
2469
|
+
if (message.kind === "guideComplete") {
|
|
2470
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2471
|
+
"div",
|
|
2472
|
+
{
|
|
2473
|
+
ref: isCurrentGuideStep ? currentStepRef : null,
|
|
2474
|
+
className: `${isRoleChange ? "mt-3" : ""}`,
|
|
2475
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2 rounded-xl bg-green-50 border border-green-200 px-3 py-2.5 text-sm leading-6", children: [
|
|
2476
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.CheckCircle2, { className: "h-5 w-5 text-green-600 flex-shrink-0" }),
|
|
2477
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-medium text-green-800", children: message.content })
|
|
2478
|
+
] })
|
|
2479
|
+
},
|
|
2480
|
+
message.id
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2483
|
+
if (message.kind === "navigationAction") {
|
|
2484
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: [
|
|
2485
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "whitespace-pre-wrap text-sm leading-6 mb-2 text-gray-700", children: message.content || "" }),
|
|
2486
|
+
message.navigationTarget && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mt-2", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2487
|
+
Button,
|
|
2488
|
+
{
|
|
2489
|
+
type: "button",
|
|
2490
|
+
size: "sm",
|
|
2491
|
+
variant: "secondary",
|
|
2492
|
+
className: "h-8 rounded-xl px-3 text-xs gap-1.5 bg-gray-100 hover:bg-gray-200 border border-gray-200",
|
|
2493
|
+
onClick: () => message.navigationTarget && handleConfirmNavigation(message.navigationTarget),
|
|
2494
|
+
children: [
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Confirm" }),
|
|
2496
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-0.5 text-gray-400", children: [
|
|
2497
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Command, { className: "h-3 w-3" }),
|
|
2498
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.CornerDownLeft, { className: "h-3 w-3" })
|
|
2499
|
+
] })
|
|
2500
|
+
]
|
|
2501
|
+
}
|
|
2502
|
+
) })
|
|
2503
|
+
] }, message.id);
|
|
2504
|
+
}
|
|
2505
|
+
if (message.kind === "actionForm") {
|
|
2506
|
+
const actionType = message.actionType;
|
|
2507
|
+
const formData = Object.keys(actionFormData).length > 0 ? actionFormData : message.actionData || {};
|
|
2508
|
+
if (message.isSubmitted) {
|
|
2509
|
+
let successContent = "";
|
|
2510
|
+
if (actionType === "updateCompanyInfo") {
|
|
2511
|
+
successContent = "Company information has been updated successfully.";
|
|
2512
|
+
} else if (actionType === "addApiKey") {
|
|
2513
|
+
successContent = "API key has been added successfully.";
|
|
2514
|
+
} else if (actionType === "addCustomer") {
|
|
2515
|
+
successContent = "Customer has been added successfully.";
|
|
2516
|
+
} else if (actionType === "enable2FA") {
|
|
2517
|
+
successContent = "Two-factor authentication has been enabled successfully.";
|
|
2518
|
+
} else if (actionType === "disable2FA") {
|
|
2519
|
+
successContent = "Two-factor authentication has been disabled successfully.";
|
|
2520
|
+
} else if (actionType === "changePassword") {
|
|
2521
|
+
successContent = "Your password has been changed successfully.";
|
|
2522
|
+
} else if (actionType === "revokeSession") {
|
|
2523
|
+
successContent = "Session has been revoked successfully.";
|
|
2524
|
+
} else if (actionType === "toggleNotification") {
|
|
2525
|
+
successContent = "Notification preferences have been updated successfully.";
|
|
2526
|
+
} else if (actionType === "connectIntegration") {
|
|
2527
|
+
successContent = "Integration has been connected successfully.";
|
|
2528
|
+
} else if (actionType === "disconnectIntegration") {
|
|
2529
|
+
successContent = "Integration has been disconnected successfully.";
|
|
2530
|
+
} else if (actionType === "addPaymentMethod") {
|
|
2531
|
+
successContent = "Payment method has been added successfully.";
|
|
2532
|
+
} else if (actionType === "removePaymentMethod") {
|
|
2533
|
+
successContent = "Payment method has been removed successfully.";
|
|
2534
|
+
} else if (actionType === "deleteApiKey") {
|
|
2535
|
+
successContent = "API key has been deleted successfully.";
|
|
2536
|
+
} else if (actionType === "addWebhook") {
|
|
2537
|
+
successContent = "Webhook endpoint has been added successfully.";
|
|
2538
|
+
} else if (actionType === "updateCurrency") {
|
|
2539
|
+
successContent = "Currency preference has been updated successfully.";
|
|
2540
|
+
} else if (actionType === "updateTimezone") {
|
|
2541
|
+
successContent = "Timezone has been updated successfully.";
|
|
2542
|
+
} else if (actionType === "refundPayment") {
|
|
2543
|
+
successContent = "Refund has been processed successfully.";
|
|
2544
|
+
} else if (actionType === "exportCertificate") {
|
|
2545
|
+
successContent = "Certificate of Incorporation has been downloaded successfully.";
|
|
2546
|
+
} else if (actionType === "createSubscription") {
|
|
2547
|
+
successContent = "Subscription has been created successfully.";
|
|
2548
|
+
} else if (actionType === "toggleBlockRule" || actionType === "enableBlockRule" || actionType === "disableBlockRule") {
|
|
2549
|
+
successContent = "Block rule has been updated successfully.";
|
|
2550
|
+
} else {
|
|
2551
|
+
successContent = "Action completed successfully.";
|
|
2552
|
+
}
|
|
2553
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "whitespace-pre-wrap text-sm leading-6 text-gray-700", children: successContent }) }, message.id);
|
|
2554
|
+
}
|
|
2555
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `min-w-0 ${isRoleChange ? "mt-3" : ""}`, children: [
|
|
2556
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "whitespace-pre-wrap text-sm leading-6 mb-3 text-gray-700", children: message.content || "" }),
|
|
2557
|
+
actionType === "updateCompanyInfo" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2 bg-gray-50 rounded-lg p-2 border border-gray-200 overflow-hidden", children: [
|
|
2558
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2559
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2560
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Company Name" }),
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2562
|
+
Input,
|
|
2563
|
+
{
|
|
2564
|
+
placeholder: "Acme Corporation",
|
|
2565
|
+
value: formData.companyName || "",
|
|
2566
|
+
onChange: (e) => setActionFormData({ ...actionFormData, companyName: e.target.value }),
|
|
2567
|
+
className: "h-8 text-xs border-gray-200"
|
|
2568
|
+
}
|
|
2569
|
+
)
|
|
2570
|
+
] }),
|
|
2571
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2572
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Email" }),
|
|
2573
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2574
|
+
Input,
|
|
2575
|
+
{
|
|
2576
|
+
type: "email",
|
|
2577
|
+
placeholder: "contact@acme.com",
|
|
2578
|
+
value: formData.email || "",
|
|
2579
|
+
onChange: (e) => setActionFormData({ ...actionFormData, email: e.target.value }),
|
|
2580
|
+
className: "h-8 text-xs border-gray-200"
|
|
2581
|
+
}
|
|
2582
|
+
)
|
|
2583
|
+
] })
|
|
2584
|
+
] }),
|
|
2585
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2586
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Address" }),
|
|
2587
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2588
|
+
Input,
|
|
2589
|
+
{
|
|
2590
|
+
placeholder: "123 Main St, San Francisco, CA",
|
|
2591
|
+
value: formData.address || "",
|
|
2592
|
+
onChange: (e) => setActionFormData({ ...actionFormData, address: e.target.value }),
|
|
2593
|
+
className: "h-8 text-xs border-gray-200"
|
|
2594
|
+
}
|
|
2595
|
+
)
|
|
2596
|
+
] }),
|
|
2597
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2598
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2599
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Phone" }),
|
|
2600
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2601
|
+
Input,
|
|
2602
|
+
{
|
|
2603
|
+
type: "tel",
|
|
2604
|
+
placeholder: "+1 (555) 123-4567",
|
|
2605
|
+
value: formData.phone || "",
|
|
2606
|
+
onChange: (e) => setActionFormData({ ...actionFormData, phone: e.target.value }),
|
|
2607
|
+
className: "h-8 text-xs border-gray-200"
|
|
2608
|
+
}
|
|
2609
|
+
)
|
|
2610
|
+
] }),
|
|
2611
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2612
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Website" }),
|
|
2613
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2614
|
+
Input,
|
|
2615
|
+
{
|
|
2616
|
+
type: "url",
|
|
2617
|
+
placeholder: "https://acme.com",
|
|
2618
|
+
value: formData.website || "",
|
|
2619
|
+
onChange: (e) => setActionFormData({ ...actionFormData, website: e.target.value }),
|
|
2620
|
+
className: "h-8 text-xs border-gray-200"
|
|
2621
|
+
}
|
|
2622
|
+
)
|
|
2623
|
+
] })
|
|
2624
|
+
] })
|
|
2625
|
+
] }),
|
|
2626
|
+
actionType === "addApiKey" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2627
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "API Key Name" }),
|
|
2628
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2629
|
+
Input,
|
|
2630
|
+
{
|
|
2631
|
+
placeholder: "Production Key",
|
|
2632
|
+
value: formData.name || "",
|
|
2633
|
+
onChange: (e) => setActionFormData({ ...actionFormData, name: e.target.value }),
|
|
2634
|
+
className: "h-8 text-xs border-gray-200"
|
|
2635
|
+
}
|
|
2636
|
+
)
|
|
2637
|
+
] }) }),
|
|
2638
|
+
actionType === "addCustomer" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2 bg-gray-50 rounded-lg p-2 border border-gray-200 overflow-hidden", children: [
|
|
2639
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2640
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2641
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Company Name" }),
|
|
2642
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2643
|
+
Input,
|
|
2644
|
+
{
|
|
2645
|
+
placeholder: "Acme Corporation",
|
|
2646
|
+
value: formData.name || "",
|
|
2647
|
+
onChange: (e) => setActionFormData({ ...actionFormData, name: e.target.value }),
|
|
2648
|
+
className: "h-8 text-xs border-gray-200"
|
|
2649
|
+
}
|
|
2650
|
+
)
|
|
2651
|
+
] }),
|
|
2652
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2653
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Email" }),
|
|
2654
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2655
|
+
Input,
|
|
2656
|
+
{
|
|
2657
|
+
type: "email",
|
|
2658
|
+
placeholder: "contact@acme.com",
|
|
2659
|
+
value: formData.email || "",
|
|
2660
|
+
onChange: (e) => setActionFormData({ ...actionFormData, email: e.target.value }),
|
|
2661
|
+
className: "h-8 text-xs border-gray-200"
|
|
2662
|
+
}
|
|
2663
|
+
)
|
|
2664
|
+
] })
|
|
2665
|
+
] }),
|
|
2666
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2667
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Location" }),
|
|
2668
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2669
|
+
Input,
|
|
2670
|
+
{
|
|
2671
|
+
placeholder: "San Francisco, CA",
|
|
2672
|
+
value: formData.location || "",
|
|
2673
|
+
onChange: (e) => setActionFormData({ ...actionFormData, location: e.target.value }),
|
|
2674
|
+
className: "h-8 text-xs border-gray-200"
|
|
2675
|
+
}
|
|
2676
|
+
)
|
|
2677
|
+
] }),
|
|
2678
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2679
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Subscription Tier" }),
|
|
2680
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2681
|
+
"select",
|
|
2682
|
+
{
|
|
2683
|
+
value: formData.subscription || "Starter",
|
|
2684
|
+
onChange: (e) => setActionFormData({ ...actionFormData, subscription: e.target.value }),
|
|
2685
|
+
className: "h-8 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-xs shadow-xs transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none",
|
|
2686
|
+
children: [
|
|
2687
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Starter", children: "Starter" }),
|
|
2688
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Professional", children: "Professional" }),
|
|
2689
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Enterprise", children: "Enterprise" })
|
|
2690
|
+
]
|
|
2691
|
+
}
|
|
2692
|
+
)
|
|
2693
|
+
] })
|
|
2694
|
+
] }),
|
|
2695
|
+
(actionType === "enable2FA" || actionType === "disable2FA") && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-black", children: actionType === "enable2FA" ? "This will enable two-factor authentication for your account. You'll need to set up an authenticator app." : "This will disable two-factor authentication for your account. Your account will be less secure." }) }),
|
|
2696
|
+
actionType === "changePassword" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: [
|
|
2697
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2698
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Current Password" }),
|
|
2699
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2700
|
+
Input,
|
|
2701
|
+
{
|
|
2702
|
+
type: "password",
|
|
2703
|
+
value: formData.currentPassword || "",
|
|
2704
|
+
onChange: (e) => setActionFormData({ ...actionFormData, currentPassword: e.target.value }),
|
|
2705
|
+
className: "h-8 text-xs border-gray-200"
|
|
2706
|
+
}
|
|
2707
|
+
)
|
|
2708
|
+
] }),
|
|
2709
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2710
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "New Password" }),
|
|
2711
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2712
|
+
Input,
|
|
2713
|
+
{
|
|
2714
|
+
type: "password",
|
|
2715
|
+
value: formData.newPassword || "",
|
|
2716
|
+
onChange: (e) => setActionFormData({ ...actionFormData, newPassword: e.target.value }),
|
|
2717
|
+
className: "h-8 text-xs border-gray-200"
|
|
2718
|
+
}
|
|
2719
|
+
)
|
|
2720
|
+
] }),
|
|
2721
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2722
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Confirm New Password" }),
|
|
2723
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2724
|
+
Input,
|
|
2725
|
+
{
|
|
2726
|
+
type: "password",
|
|
2727
|
+
value: formData.confirmPassword || "",
|
|
2728
|
+
onChange: (e) => setActionFormData({ ...actionFormData, confirmPassword: e.target.value }),
|
|
2729
|
+
className: "h-8 text-xs border-gray-200"
|
|
2730
|
+
}
|
|
2731
|
+
)
|
|
2732
|
+
] })
|
|
2733
|
+
] }),
|
|
2734
|
+
actionType === "toggleNotification" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: [
|
|
2735
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2736
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Notification Type" }),
|
|
2737
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2738
|
+
"select",
|
|
2739
|
+
{
|
|
2740
|
+
value: formData.notificationType || "paymentReceived",
|
|
2741
|
+
onChange: (e) => setActionFormData({ ...actionFormData, notificationType: e.target.value }),
|
|
2742
|
+
className: "h-8 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-xs shadow-xs transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none",
|
|
2743
|
+
children: [
|
|
2744
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "paymentReceived", children: "Payment Received" }),
|
|
2745
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "paymentFailed", children: "Payment Failed" }),
|
|
2746
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "invoicePaid", children: "Invoice Paid" }),
|
|
2747
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "monthlySummary", children: "Monthly Summary" }),
|
|
2748
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "productUpdates", children: "Product Updates" })
|
|
2749
|
+
]
|
|
2750
|
+
}
|
|
2751
|
+
)
|
|
2752
|
+
] }),
|
|
2753
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2754
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2755
|
+
"input",
|
|
2756
|
+
{
|
|
2757
|
+
type: "checkbox",
|
|
2758
|
+
checked: formData.enabled !== false,
|
|
2759
|
+
onChange: (e) => setActionFormData({ ...actionFormData, enabled: e.target.checked }),
|
|
2760
|
+
className: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"
|
|
2761
|
+
}
|
|
2762
|
+
),
|
|
2763
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs text-black", children: "Enable this notification" })
|
|
2764
|
+
] })
|
|
2765
|
+
] }),
|
|
2766
|
+
(actionType === "connectIntegration" || actionType === "disconnectIntegration") && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2767
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Integration" }),
|
|
2768
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2769
|
+
"select",
|
|
2770
|
+
{
|
|
2771
|
+
value: formData.integrationName || "Slack",
|
|
2772
|
+
onChange: (e) => setActionFormData({ ...actionFormData, integrationName: e.target.value }),
|
|
2773
|
+
className: "h-8 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-xs shadow-xs transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none",
|
|
2774
|
+
children: [
|
|
2775
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Slack", children: "Slack" }),
|
|
2776
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Zapier", children: "Zapier" }),
|
|
2777
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Webhook", children: "Webhook" })
|
|
2778
|
+
]
|
|
2779
|
+
}
|
|
2780
|
+
)
|
|
2781
|
+
] }) }),
|
|
2782
|
+
actionType === "addPaymentMethod" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: [
|
|
2783
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2784
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Card Number" }),
|
|
2785
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2786
|
+
Input,
|
|
2787
|
+
{
|
|
2788
|
+
placeholder: "1234 5678 9012 3456",
|
|
2789
|
+
value: formData.cardNumber || "",
|
|
2790
|
+
onChange: (e) => setActionFormData({ ...actionFormData, cardNumber: e.target.value }),
|
|
2791
|
+
className: "h-8 text-xs border-gray-200"
|
|
2792
|
+
}
|
|
2793
|
+
)
|
|
2794
|
+
] }),
|
|
2795
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2796
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Expiry Date" }),
|
|
2797
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2798
|
+
Input,
|
|
2799
|
+
{
|
|
2800
|
+
placeholder: "MM/YY",
|
|
2801
|
+
value: formData.expiryDate || "",
|
|
2802
|
+
onChange: (e) => setActionFormData({ ...actionFormData, expiryDate: e.target.value }),
|
|
2803
|
+
className: "h-8 text-xs border-gray-200"
|
|
2804
|
+
}
|
|
2805
|
+
)
|
|
2806
|
+
] })
|
|
2807
|
+
] }),
|
|
2808
|
+
actionType === "removePaymentMethod" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-black", children: "This will remove the default payment method from your account." }) }),
|
|
2809
|
+
actionType === "refundPayment" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: [
|
|
2810
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2811
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Transaction ID or Customer Name" }),
|
|
2812
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2813
|
+
Input,
|
|
2814
|
+
{
|
|
2815
|
+
placeholder: "e.g., txn_1234 or Acme Corp",
|
|
2816
|
+
value: formData.transactionId || formData.customer || "",
|
|
2817
|
+
onChange: (e) => {
|
|
2818
|
+
const value = e.target.value;
|
|
2819
|
+
if (value.startsWith("txn_") || /^\d+$/.test(value)) {
|
|
2820
|
+
setActionFormData({ ...actionFormData, transactionId: value, customer: void 0 });
|
|
2821
|
+
} else {
|
|
2822
|
+
setActionFormData({ ...actionFormData, customer: value, transactionId: void 0 });
|
|
2823
|
+
}
|
|
2824
|
+
},
|
|
2825
|
+
className: "h-8 text-xs border-gray-200"
|
|
2826
|
+
}
|
|
2827
|
+
)
|
|
2828
|
+
] }),
|
|
2829
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2830
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Amount (optional)" }),
|
|
2831
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2832
|
+
Input,
|
|
2833
|
+
{
|
|
2834
|
+
placeholder: "$0.00",
|
|
2835
|
+
value: formData.amount || "",
|
|
2836
|
+
onChange: (e) => setActionFormData({ ...actionFormData, amount: e.target.value }),
|
|
2837
|
+
className: "h-8 text-xs border-gray-200"
|
|
2838
|
+
}
|
|
2839
|
+
)
|
|
2840
|
+
] }),
|
|
2841
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2842
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Reason (optional)" }),
|
|
2843
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2844
|
+
Input,
|
|
2845
|
+
{
|
|
2846
|
+
placeholder: "e.g., Customer request",
|
|
2847
|
+
value: formData.reason || "",
|
|
2848
|
+
onChange: (e) => setActionFormData({ ...actionFormData, reason: e.target.value }),
|
|
2849
|
+
className: "h-8 text-xs border-gray-200"
|
|
2850
|
+
}
|
|
2851
|
+
)
|
|
2852
|
+
] })
|
|
2853
|
+
] }),
|
|
2854
|
+
actionType === "deleteApiKey" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: [
|
|
2855
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2856
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "API Key Name or ID" }),
|
|
2857
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2858
|
+
Input,
|
|
2859
|
+
{
|
|
2860
|
+
placeholder: "Production Key",
|
|
2861
|
+
value: formData.keyId || formData.name || "",
|
|
2862
|
+
onChange: (e) => setActionFormData({ ...actionFormData, keyId: e.target.value, name: e.target.value }),
|
|
2863
|
+
className: "h-8 text-xs border-gray-200"
|
|
2864
|
+
}
|
|
2865
|
+
)
|
|
2866
|
+
] }),
|
|
2867
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-red-600", children: "Warning: This action cannot be undone. The API key will be permanently deleted." })
|
|
2868
|
+
] }),
|
|
2869
|
+
actionType === "addWebhook" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: [
|
|
2870
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2871
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Webhook URL" }),
|
|
2872
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2873
|
+
Input,
|
|
2874
|
+
{
|
|
2875
|
+
type: "url",
|
|
2876
|
+
placeholder: "https://example.com/webhook",
|
|
2877
|
+
value: formData.url || "",
|
|
2878
|
+
onChange: (e) => setActionFormData({ ...actionFormData, url: e.target.value }),
|
|
2879
|
+
className: "h-8 text-xs border-gray-200"
|
|
2880
|
+
}
|
|
2881
|
+
)
|
|
2882
|
+
] }),
|
|
2883
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2884
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Name (optional)" }),
|
|
2885
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2886
|
+
Input,
|
|
2887
|
+
{
|
|
2888
|
+
placeholder: "Production Webhook",
|
|
2889
|
+
value: formData.name || "",
|
|
2890
|
+
onChange: (e) => setActionFormData({ ...actionFormData, name: e.target.value }),
|
|
2891
|
+
className: "h-8 text-xs border-gray-200"
|
|
2892
|
+
}
|
|
2893
|
+
)
|
|
2894
|
+
] })
|
|
2895
|
+
] }),
|
|
2896
|
+
actionType === "updateCurrency" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Currency" }),
|
|
2898
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2899
|
+
"select",
|
|
2900
|
+
{
|
|
2901
|
+
value: formData.currency || "USD",
|
|
2902
|
+
onChange: (e) => setActionFormData({ ...actionFormData, currency: e.target.value }),
|
|
2903
|
+
className: "h-8 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-xs shadow-xs transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none",
|
|
2904
|
+
children: [
|
|
2905
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "USD", children: "USD ($)" }),
|
|
2906
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "EUR", children: "EUR (\u20AC)" }),
|
|
2907
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "GBP", children: "GBP (\xA3)" }),
|
|
2908
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "JPY", children: "JPY (\xA5)" })
|
|
2909
|
+
]
|
|
2910
|
+
}
|
|
2911
|
+
)
|
|
2912
|
+
] }) }),
|
|
2913
|
+
actionType === "updateTimezone" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2914
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Timezone" }),
|
|
2915
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2916
|
+
"select",
|
|
2917
|
+
{
|
|
2918
|
+
value: formData.timezone || "America/Los_Angeles",
|
|
2919
|
+
onChange: (e) => setActionFormData({ ...actionFormData, timezone: e.target.value }),
|
|
2920
|
+
className: "h-8 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-xs shadow-xs transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none",
|
|
2921
|
+
children: [
|
|
2922
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "America/Los_Angeles", children: "Pacific Time (PT)" }),
|
|
2923
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "America/New_York", children: "Eastern Time (ET)" }),
|
|
2924
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Europe/London", children: "GMT" }),
|
|
2925
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Asia/Tokyo", children: "JST" })
|
|
2926
|
+
]
|
|
2927
|
+
}
|
|
2928
|
+
)
|
|
2929
|
+
] }) }),
|
|
2930
|
+
actionType === "revokeSession" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: [
|
|
2931
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2932
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Device/Session" }),
|
|
2933
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2934
|
+
Input,
|
|
2935
|
+
{
|
|
2936
|
+
placeholder: "MacBook Pro",
|
|
2937
|
+
value: formData.device || "",
|
|
2938
|
+
onChange: (e) => setActionFormData({ ...actionFormData, device: e.target.value }),
|
|
2939
|
+
className: "h-8 text-xs border-gray-200"
|
|
2940
|
+
}
|
|
2941
|
+
)
|
|
2942
|
+
] }),
|
|
2943
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-black", children: "This will sign out the device and invalidate its session." })
|
|
2944
|
+
] }),
|
|
2945
|
+
actionType === "createSubscription" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: [
|
|
2946
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2947
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Customer Email" }),
|
|
2948
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2949
|
+
Input,
|
|
2950
|
+
{
|
|
2951
|
+
type: "email",
|
|
2952
|
+
placeholder: "customer@example.com",
|
|
2953
|
+
value: formData.customerEmail || "",
|
|
2954
|
+
onChange: (e) => setActionFormData({ ...actionFormData, customerEmail: e.target.value }),
|
|
2955
|
+
className: "h-8 text-xs border-gray-200"
|
|
2956
|
+
}
|
|
2957
|
+
)
|
|
2958
|
+
] }),
|
|
2959
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2960
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Plan" }),
|
|
2961
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2962
|
+
"select",
|
|
2963
|
+
{
|
|
2964
|
+
value: formData.plan || "Starter",
|
|
2965
|
+
onChange: (e) => setActionFormData({ ...actionFormData, plan: e.target.value }),
|
|
2966
|
+
className: "h-8 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-xs shadow-xs transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none",
|
|
2967
|
+
children: [
|
|
2968
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Starter", children: "Starter ($29/mo)" }),
|
|
2969
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Professional", children: "Professional ($99/mo)" }),
|
|
2970
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "Enterprise", children: "Enterprise ($299/mo)" })
|
|
2971
|
+
]
|
|
2972
|
+
}
|
|
2973
|
+
)
|
|
2974
|
+
] }),
|
|
2975
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2976
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Billing Cycle" }),
|
|
2977
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2978
|
+
"select",
|
|
2979
|
+
{
|
|
2980
|
+
value: formData.billingCycle || "monthly",
|
|
2981
|
+
onChange: (e) => setActionFormData({ ...actionFormData, billingCycle: e.target.value }),
|
|
2982
|
+
className: "h-8 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-xs shadow-xs transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none",
|
|
2983
|
+
children: [
|
|
2984
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "monthly", children: "Monthly" }),
|
|
2985
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "yearly", children: "Yearly (Save 15%)" })
|
|
2986
|
+
]
|
|
2987
|
+
}
|
|
2988
|
+
)
|
|
2989
|
+
] })
|
|
2990
|
+
] }),
|
|
2991
|
+
actionType === "exportCertificate" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: [
|
|
2992
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-black", children: "This will export your certificate of incorporation document." }),
|
|
2993
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
2994
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Format" }),
|
|
2995
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2996
|
+
"select",
|
|
2997
|
+
{
|
|
2998
|
+
value: formData.format || "pdf",
|
|
2999
|
+
onChange: (e) => setActionFormData({ ...actionFormData, format: e.target.value }),
|
|
3000
|
+
className: "h-8 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-xs shadow-xs transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none",
|
|
3001
|
+
children: [
|
|
3002
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "pdf", children: "PDF" }),
|
|
3003
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "json", children: "JSON" })
|
|
3004
|
+
]
|
|
3005
|
+
}
|
|
3006
|
+
)
|
|
3007
|
+
] })
|
|
3008
|
+
] }),
|
|
3009
|
+
(actionType === "toggleBlockRule" || actionType === "enableBlockRule" || actionType === "disableBlockRule") && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3 bg-gray-50 rounded-lg p-3 border border-gray-200", children: [
|
|
3010
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
3011
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { className: "text-xs font-medium text-black mb-1.5 block", children: "Block Rule" }),
|
|
3012
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3013
|
+
"select",
|
|
3014
|
+
{
|
|
3015
|
+
value: formData.ruleId || "rule_1",
|
|
3016
|
+
onChange: (e) => setActionFormData({ ...actionFormData, ruleId: e.target.value }),
|
|
3017
|
+
className: "h-8 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-xs shadow-xs transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none",
|
|
3018
|
+
children: [
|
|
3019
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "rule_1", children: "Block if risk level = 'highest'" }),
|
|
3020
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "rule_2", children: "Block if matches Stripe block lists" }),
|
|
3021
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "rule_3", children: "Block if CVC verification fails" }),
|
|
3022
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "rule_4", children: "Block if Postal code verification fails" })
|
|
3023
|
+
]
|
|
3024
|
+
}
|
|
3025
|
+
)
|
|
3026
|
+
] }),
|
|
3027
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-black", children: actionType === "enableBlockRule" ? "This will enable the selected block rule to actively block matching payments." : actionType === "disableBlockRule" ? "This will disable the selected block rule. Matching payments will no longer be blocked." : "This will toggle the selected block rule's state." })
|
|
3028
|
+
] }),
|
|
3029
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3030
|
+
Button,
|
|
3031
|
+
{
|
|
3032
|
+
type: "button",
|
|
3033
|
+
size: "sm",
|
|
3034
|
+
variant: "secondary",
|
|
3035
|
+
className: "h-8 rounded-xl px-3 text-xs gap-1.5 bg-gray-100 hover:bg-gray-200 border border-gray-200",
|
|
3036
|
+
onClick: handleActionSubmit,
|
|
3037
|
+
children: [
|
|
3038
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Confirm" }),
|
|
3039
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-0.5 text-gray-400", children: [
|
|
3040
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Command, { className: "h-3 w-3" }),
|
|
3041
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.CornerDownLeft, { className: "h-3 w-3" })
|
|
3042
|
+
] })
|
|
3043
|
+
]
|
|
3044
|
+
}
|
|
3045
|
+
) })
|
|
3046
|
+
] }, message.id);
|
|
3047
|
+
}
|
|
3048
|
+
if (message.kind === "bulkPreview" && message.csvData) {
|
|
3049
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: [
|
|
3050
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "whitespace-pre-wrap text-sm leading-6 mb-3 text-gray-700", children: message.content || "" }),
|
|
3051
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bg-gray-50 rounded-lg border border-gray-200 overflow-hidden", children: [
|
|
3052
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bg-gray-100 px-3 py-2 border-b border-gray-200 flex items-center gap-2", children: [
|
|
3053
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.FileSpreadsheet, { className: "h-4 w-4 text-gray-600" }),
|
|
3054
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs font-medium text-gray-700", children: message.csvData.fileName }),
|
|
3055
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-xs text-gray-500", children: [
|
|
3056
|
+
"\u2022 ",
|
|
3057
|
+
message.csvData.rowCount,
|
|
3058
|
+
" rows"
|
|
3059
|
+
] })
|
|
3060
|
+
] }),
|
|
3061
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "px-3 py-2 border-b border-gray-100", children: [
|
|
3062
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "text-[10px] uppercase tracking-wider text-gray-500 mb-1", children: "Columns" }),
|
|
3063
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-wrap gap-1", children: message.csvData.columns.map((col, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs bg-blue-100 text-blue-700 px-1.5 py-0.5 rounded", children: col }, i)) })
|
|
3064
|
+
] }),
|
|
3065
|
+
message.csvData.sampleRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "px-3 py-2", children: [
|
|
3066
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "text-[10px] uppercase tracking-wider text-gray-500 mb-1", children: "Sample Data" }),
|
|
3067
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "space-y-1", children: message.csvData.sampleRows.slice(0, 3).map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "text-xs text-gray-600 bg-white rounded px-2 py-1 border border-gray-100", children: [
|
|
3068
|
+
Object.entries(row).slice(0, 3).map(([key, val], j) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
|
|
3069
|
+
j > 0 && " \u2022 ",
|
|
3070
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-gray-400", children: [
|
|
3071
|
+
key,
|
|
3072
|
+
":"
|
|
3073
|
+
] }),
|
|
3074
|
+
" ",
|
|
3075
|
+
val
|
|
3076
|
+
] }, key)),
|
|
3077
|
+
Object.keys(row).length > 3 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-gray-400", children: " ..." })
|
|
3078
|
+
] }, i)) })
|
|
3079
|
+
] }),
|
|
3080
|
+
message.suggestedAction && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "px-3 py-2 bg-blue-50 border-t border-blue-100", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-xs text-blue-700", children: [
|
|
3081
|
+
"Suggested action: ",
|
|
3082
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: message.suggestedAction.replace(/_/g, " ") })
|
|
3083
|
+
] }) })
|
|
3084
|
+
] }),
|
|
3085
|
+
message.bulkSessionId && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mt-3 flex items-center gap-2", children: [
|
|
3086
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3087
|
+
Button,
|
|
3088
|
+
{
|
|
3089
|
+
type: "button",
|
|
3090
|
+
size: "sm",
|
|
3091
|
+
variant: "secondary",
|
|
3092
|
+
className: "h-8 rounded-xl px-3 text-xs gap-1.5 bg-gray-100 hover:bg-gray-200 border border-gray-200",
|
|
3093
|
+
onClick: () => message.bulkSessionId && confirmBulkOperation(message.bulkSessionId),
|
|
3094
|
+
children: [
|
|
3095
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
|
|
3096
|
+
"Process ",
|
|
3097
|
+
message.csvData.rowCount,
|
|
3098
|
+
" rows"
|
|
3099
|
+
] }),
|
|
3100
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-0.5 text-gray-400", children: [
|
|
3101
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Command, { className: "h-3 w-3" }),
|
|
3102
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.CornerDownLeft, { className: "h-3 w-3" })
|
|
3103
|
+
] })
|
|
3104
|
+
]
|
|
3105
|
+
}
|
|
3106
|
+
),
|
|
3107
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3108
|
+
Button,
|
|
3109
|
+
{
|
|
3110
|
+
type: "button",
|
|
3111
|
+
size: "sm",
|
|
3112
|
+
variant: "ghost",
|
|
3113
|
+
className: "h-8 rounded-xl px-3 text-xs text-gray-500 hover:text-gray-700 hover:bg-gray-100",
|
|
3114
|
+
onClick: cancelBulkOperation,
|
|
3115
|
+
children: "Cancel"
|
|
3116
|
+
}
|
|
3117
|
+
)
|
|
3118
|
+
] })
|
|
3119
|
+
] }, message.id);
|
|
3120
|
+
}
|
|
3121
|
+
if (message.kind === "bulkProgress" && message.bulkProgress) {
|
|
3122
|
+
const { processed, total, successes, failures } = message.bulkProgress;
|
|
3123
|
+
const percentage = Math.round(processed / total * 100);
|
|
3124
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bg-gray-50 rounded-lg border border-gray-200 p-3", children: [
|
|
3125
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
3126
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Loader2, { className: "h-4 w-4 animate-spin text-blue-600" }),
|
|
3127
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-sm font-medium text-gray-700", children: [
|
|
3128
|
+
"Processing... ",
|
|
3129
|
+
processed,
|
|
3130
|
+
" of ",
|
|
3131
|
+
total
|
|
3132
|
+
] })
|
|
3133
|
+
] }),
|
|
3134
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "h-2 bg-gray-200 rounded-full overflow-hidden mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3135
|
+
"div",
|
|
3136
|
+
{
|
|
3137
|
+
className: "h-full bg-blue-600 transition-all duration-300",
|
|
3138
|
+
style: { width: `${percentage}%` }
|
|
3139
|
+
}
|
|
3140
|
+
) }),
|
|
3141
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-3 text-xs text-gray-600", children: [
|
|
3142
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-1", children: [
|
|
3143
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.CheckCircle2, { className: "h-3 w-3 text-green-600" }),
|
|
3144
|
+
successes,
|
|
3145
|
+
" successful"
|
|
3146
|
+
] }),
|
|
3147
|
+
failures > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-1 text-red-600", children: [
|
|
3148
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.X, { className: "h-3 w-3" }),
|
|
3149
|
+
failures,
|
|
3150
|
+
" failed"
|
|
3151
|
+
] })
|
|
3152
|
+
] })
|
|
3153
|
+
] }) }, message.id);
|
|
3154
|
+
}
|
|
3155
|
+
if (message.kind === "bulkSummary" && message.bulkSummary) {
|
|
3156
|
+
const { total, successes, failures, navigationPage } = message.bulkSummary;
|
|
3157
|
+
const hasFailures = failures.length > 0;
|
|
3158
|
+
const pageLabel = navigationPage?.page === "customers" ? "Customers" : navigationPage?.page === "dashboard" ? "Dashboard" : navigationPage?.page === "settings" ? "Settings" : "Results";
|
|
3159
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `${isRoleChange ? "mt-3" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `rounded-lg border p-3 ${hasFailures ? "bg-amber-50 border-amber-200" : "bg-green-50 border-green-200"}`, children: [
|
|
3160
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
3161
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.CheckCircle2, { className: `h-5 w-5 ${hasFailures ? "text-amber-600" : "text-green-600"}` }),
|
|
3162
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-sm font-medium text-gray-800", children: "Bulk operation complete" })
|
|
3163
|
+
] }),
|
|
3164
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "text-sm text-gray-600 mb-2", children: message.content || `Processed ${total} rows: ${successes} successful${hasFailures ? `, ${failures.length} failed` : ""}.` }),
|
|
3165
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-4 text-xs", children: [
|
|
3166
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-1 text-green-700", children: [
|
|
3167
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.CheckCircle2, { className: "h-3 w-3" }),
|
|
3168
|
+
successes,
|
|
3169
|
+
" successful"
|
|
3170
|
+
] }),
|
|
3171
|
+
hasFailures && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-1 text-red-600", children: [
|
|
3172
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.X, { className: "h-3 w-3" }),
|
|
3173
|
+
failures.length,
|
|
3174
|
+
" failed"
|
|
3175
|
+
] })
|
|
3176
|
+
] }),
|
|
3177
|
+
hasFailures && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mt-3 pt-2 border-t border-amber-200", children: [
|
|
3178
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "text-[10px] uppercase tracking-wider text-amber-700 mb-1", children: "Failed Rows" }),
|
|
3179
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-1 max-h-32 overflow-y-auto", children: [
|
|
3180
|
+
failures.slice(0, 5).map((failure, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "text-xs text-red-700 bg-red-50 rounded px-2 py-1", children: [
|
|
3181
|
+
"Row ",
|
|
3182
|
+
failure.row,
|
|
3183
|
+
": ",
|
|
3184
|
+
failure.error || "Unknown error"
|
|
3185
|
+
] }, i)),
|
|
3186
|
+
failures.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "text-xs text-amber-600", children: [
|
|
3187
|
+
"...and ",
|
|
3188
|
+
failures.length - 5,
|
|
3189
|
+
" more"
|
|
3190
|
+
] })
|
|
3191
|
+
] })
|
|
3192
|
+
] }),
|
|
3193
|
+
navigationPage && successes > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mt-3 pt-2 border-t border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3194
|
+
"button",
|
|
3195
|
+
{
|
|
3196
|
+
type: "button",
|
|
3197
|
+
onClick: (e) => {
|
|
3198
|
+
e.preventDefault();
|
|
3199
|
+
e.stopPropagation();
|
|
3200
|
+
console.log("[DEBUG] Button clicked - navigationPage:", navigationPage, "onNavigate:", !!onNavigate);
|
|
3201
|
+
if (onNavigate && navigationPage.page) {
|
|
3202
|
+
console.log("[DEBUG] Calling onNavigate with page:", navigationPage.page);
|
|
3203
|
+
onNavigate(navigationPage.page, navigationPage.subtab);
|
|
3204
|
+
} else {
|
|
3205
|
+
console.log("[DEBUG] Condition failed - onNavigate:", !!onNavigate, "navigationPage.page:", navigationPage.page);
|
|
3206
|
+
}
|
|
3207
|
+
},
|
|
3208
|
+
className: "flex items-center gap-2 text-xs text-gray-500 hover:text-gray-700 transition-colors group cursor-pointer",
|
|
3209
|
+
children: [
|
|
3210
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.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: [
|
|
3211
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Command, { className: "h-2.5 w-2.5" }),
|
|
3212
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "+" }),
|
|
3213
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.CornerDownLeft, { className: "h-2.5 w-2.5" })
|
|
3214
|
+
] }),
|
|
3215
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
|
|
3216
|
+
"View ",
|
|
3217
|
+
pageLabel
|
|
3218
|
+
] })
|
|
3219
|
+
]
|
|
3220
|
+
}
|
|
3221
|
+
) })
|
|
3222
|
+
] }) }, message.id);
|
|
3223
|
+
}
|
|
3224
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(React4.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3225
|
+
"div",
|
|
3226
|
+
{
|
|
3227
|
+
ref: isCurrentGuideStep ? currentStepRef : null,
|
|
3228
|
+
className: `${isRoleChange ? "mt-3" : ""}`,
|
|
3229
|
+
children: [
|
|
3230
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "text-sm leading-6 text-gray-700", children: (() => {
|
|
3231
|
+
const text = message.content || "";
|
|
3232
|
+
if (message.kind === "guideStep") {
|
|
3233
|
+
const m = text.match(/^(Step\s+\d+:)([\s\S]*)/);
|
|
3234
|
+
if (m) {
|
|
3235
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
3236
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: m[1] }),
|
|
3237
|
+
m[2]
|
|
3238
|
+
] });
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
if (message.role === "assistant" && text) {
|
|
3242
|
+
return renderMarkdown(text);
|
|
3243
|
+
}
|
|
3244
|
+
return text || /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "opacity-70", children: "Thinking\u2026" });
|
|
3245
|
+
})() }),
|
|
3246
|
+
message.role === "assistant" && message.structuredData && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3247
|
+
DataRenderer,
|
|
3248
|
+
{
|
|
3249
|
+
type: message.structuredData.type,
|
|
3250
|
+
data: message.structuredData.data
|
|
3251
|
+
}
|
|
3252
|
+
) }),
|
|
3253
|
+
message.role === "assistant" && message.followups && message.followups.length > 0 && !message.followupSelected && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mt-3 flex flex-wrap gap-1.5", children: message.followups.map((followup) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3254
|
+
"button",
|
|
3255
|
+
{
|
|
3256
|
+
type: "button",
|
|
3257
|
+
onClick: () => handleFollowupClick(message.id, followup),
|
|
3258
|
+
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",
|
|
3259
|
+
children: followup.label
|
|
3260
|
+
},
|
|
3261
|
+
followup.id
|
|
3262
|
+
)) })
|
|
3263
|
+
]
|
|
3264
|
+
}
|
|
3265
|
+
) }, message.id);
|
|
3266
|
+
}),
|
|
3267
|
+
(activeGuide || guideComplete) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mt-3 flex items-center gap-2", children: [
|
|
3268
|
+
activeGuide && activeGuide.stepIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3269
|
+
Button,
|
|
3270
|
+
{
|
|
3271
|
+
type: "button",
|
|
3272
|
+
size: "sm",
|
|
3273
|
+
variant: "secondary",
|
|
3274
|
+
className: "h-7 w-7 rounded-full p-0 bg-gray-100 hover:bg-gray-200 border border-gray-200",
|
|
3275
|
+
onClick: goBackGuide,
|
|
3276
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.ArrowLeft, { className: "h-3.5 w-3.5 text-gray-600" })
|
|
3277
|
+
}
|
|
3278
|
+
),
|
|
3279
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
3280
|
+
Button,
|
|
3281
|
+
{
|
|
3282
|
+
type: "button",
|
|
3283
|
+
size: "sm",
|
|
3284
|
+
variant: "secondary",
|
|
3285
|
+
className: "h-8 rounded-xl px-3 text-xs gap-1.5 bg-gray-100 hover:bg-gray-200 border border-gray-200",
|
|
3286
|
+
onClick: guideComplete ? handleBack : advanceGuide,
|
|
3287
|
+
children: [
|
|
3288
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: guideComplete ? "Done" : "Next" }),
|
|
3289
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-0.5 text-gray-400", children: [
|
|
3290
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Command, { className: "h-3 w-3" }),
|
|
3291
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.CornerDownLeft, { className: "h-3 w-3" })
|
|
3292
|
+
] })
|
|
3293
|
+
]
|
|
3294
|
+
}
|
|
3295
|
+
)
|
|
3296
|
+
] }),
|
|
3297
|
+
(phase === "thinking" || phase === "searching" || phase === "executing" || phase === "responding") && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `${lastRole === "user" ? "mt-3" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3298
|
+
AssistantActivity,
|
|
3299
|
+
{
|
|
3300
|
+
phase,
|
|
3301
|
+
progressSteps
|
|
3302
|
+
}
|
|
3303
|
+
) }),
|
|
3304
|
+
!activeGuide && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: messagesEndRef })
|
|
3305
|
+
] }) }) }) }) }),
|
|
3306
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "px-4 py-3 border-t border-gray-100 bg-gray-50/50 shrink-0", children: [
|
|
3307
|
+
pendingFile && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mb-2 flex items-center gap-2 rounded-xl bg-blue-50 border border-blue-200 px-3 py-2", children: [
|
|
3308
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.FileSpreadsheet, { className: "h-4 w-4 text-blue-600" }),
|
|
3309
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs text-blue-700 flex-1 truncate", children: pendingFile.name }),
|
|
3310
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3311
|
+
"button",
|
|
3312
|
+
{
|
|
3313
|
+
type: "button",
|
|
3314
|
+
onClick: () => {
|
|
3315
|
+
setPendingFile(null);
|
|
3316
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
3317
|
+
},
|
|
3318
|
+
className: "text-blue-600 hover:text-blue-800",
|
|
3319
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.X, { className: "h-4 w-4" })
|
|
3320
|
+
}
|
|
3321
|
+
)
|
|
3322
|
+
] }),
|
|
3323
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("form", { onSubmit: handleSubmit, className: "w-full", children: [
|
|
3324
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3325
|
+
"input",
|
|
3326
|
+
{
|
|
3327
|
+
ref: fileInputRef,
|
|
3328
|
+
type: "file",
|
|
3329
|
+
accept: ".csv",
|
|
3330
|
+
className: "hidden",
|
|
3331
|
+
onChange: (e) => {
|
|
3332
|
+
const file = e.target.files?.[0];
|
|
3333
|
+
if (file) {
|
|
3334
|
+
setPendingFile(file);
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
),
|
|
3339
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.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: [
|
|
3340
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3341
|
+
Button,
|
|
3342
|
+
{
|
|
3343
|
+
type: "button",
|
|
3344
|
+
size: "icon",
|
|
3345
|
+
variant: "ghost",
|
|
3346
|
+
onClick: () => fileInputRef.current?.click(),
|
|
3347
|
+
className: "h-8 w-8 rounded-full text-gray-400 hover:text-gray-600 hover:bg-gray-100",
|
|
3348
|
+
title: "Upload CSV for bulk operations",
|
|
3349
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.Paperclip, { className: "h-4 w-4" })
|
|
3350
|
+
}
|
|
3351
|
+
),
|
|
3352
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3353
|
+
Input,
|
|
3354
|
+
{
|
|
3355
|
+
placeholder: pendingFile ? "Describe what to do with this CSV..." : "Ask anything...",
|
|
3356
|
+
value: input,
|
|
3357
|
+
onChange: (e) => setInput(e.target.value),
|
|
3358
|
+
onKeyDown: (e) => {
|
|
3359
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
3360
|
+
const currentBulkSession = pendingBulkSessionRef.current;
|
|
3361
|
+
if (currentBulkSession) {
|
|
3362
|
+
e.preventDefault();
|
|
3363
|
+
e.stopPropagation();
|
|
3364
|
+
confirmBulkOperation(currentBulkSession);
|
|
3365
|
+
return;
|
|
3366
|
+
}
|
|
3367
|
+
if (pendingAction) {
|
|
3368
|
+
e.preventDefault();
|
|
3369
|
+
e.stopPropagation();
|
|
3370
|
+
handleActionSubmit();
|
|
3371
|
+
return;
|
|
3372
|
+
}
|
|
3373
|
+
if (pendingNavigation) {
|
|
3374
|
+
e.preventDefault();
|
|
3375
|
+
e.stopPropagation();
|
|
3376
|
+
handleConfirmNavigation(pendingNavigation);
|
|
3377
|
+
return;
|
|
3378
|
+
}
|
|
3379
|
+
const currentGuide = activeGuideRef.current;
|
|
3380
|
+
if (currentGuide) {
|
|
3381
|
+
e.preventDefault();
|
|
3382
|
+
e.stopPropagation();
|
|
3383
|
+
advanceGuide();
|
|
3384
|
+
return;
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
},
|
|
3388
|
+
className: "flex-1 border-0 bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 text-sm placeholder:text-gray-400"
|
|
3389
|
+
}
|
|
3390
|
+
),
|
|
3391
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3392
|
+
Button,
|
|
3393
|
+
{
|
|
3394
|
+
type: "submit",
|
|
3395
|
+
size: "icon",
|
|
3396
|
+
disabled: !input.trim() && !pendingFile,
|
|
3397
|
+
className: "h-8 w-8 rounded-full bg-gray-900 hover:bg-gray-800 disabled:bg-gray-300",
|
|
3398
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.ArrowUp, { className: "h-4 w-4" })
|
|
3399
|
+
}
|
|
3400
|
+
)
|
|
3401
|
+
] })
|
|
3402
|
+
] })
|
|
3403
|
+
] }),
|
|
3404
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3405
|
+
GuideCursor,
|
|
3406
|
+
{
|
|
3407
|
+
x: cursorState.x,
|
|
3408
|
+
y: cursorState.y,
|
|
3409
|
+
visible: cursorState.visible,
|
|
3410
|
+
onClick: cursorState.onClick
|
|
3411
|
+
}
|
|
3412
|
+
)
|
|
3413
|
+
]
|
|
3414
|
+
}
|
|
3415
|
+
);
|
|
3416
|
+
}
|
|
3417
|
+
function PanelToggle({ isOpen, onClick, className = "" }) {
|
|
3418
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3419
|
+
"button",
|
|
3420
|
+
{
|
|
3421
|
+
type: "button",
|
|
3422
|
+
onClick,
|
|
3423
|
+
className: `fixed top-1/2 z-50 flex items-center justify-center w-6 h-16 bg-gray-100 hover:bg-gray-200 border border-gray-200 border-r-0 rounded-l-lg text-gray-600 hover:text-gray-800 shadow-md transition-all duration-300 ${className}`,
|
|
3424
|
+
"aria-label": isOpen ? "Close help panel" : "Open help panel",
|
|
3425
|
+
style: {
|
|
3426
|
+
right: isOpen ? `${PANEL_WIDTH}px` : "0px",
|
|
3427
|
+
transform: "translateY(-50%)"
|
|
3428
|
+
},
|
|
3429
|
+
children: isOpen ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.ChevronRight, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react4.ChevronLeft, { className: "h-4 w-4" })
|
|
3430
|
+
}
|
|
3431
|
+
);
|
|
3432
|
+
}
|
|
3433
|
+
function ChatPanelWithToggle({
|
|
3434
|
+
onNavigate,
|
|
3435
|
+
onActionComplete,
|
|
3436
|
+
currentPage,
|
|
3437
|
+
agentUrl,
|
|
3438
|
+
startingQuestions,
|
|
3439
|
+
startingQuestionsEndpoint,
|
|
3440
|
+
defaultOpen = false,
|
|
3441
|
+
isOpen: controlledIsOpen,
|
|
3442
|
+
onOpenChange
|
|
3443
|
+
}) {
|
|
3444
|
+
const [internalIsOpen, setInternalIsOpen] = React4.useState(defaultOpen);
|
|
3445
|
+
const isOpen = controlledIsOpen !== void 0 ? controlledIsOpen : internalIsOpen;
|
|
3446
|
+
const setIsOpen = (open) => {
|
|
3447
|
+
if (controlledIsOpen === void 0) {
|
|
3448
|
+
setInternalIsOpen(open);
|
|
3449
|
+
}
|
|
3450
|
+
onOpenChange?.(open);
|
|
3451
|
+
};
|
|
3452
|
+
React4.useEffect(() => {
|
|
3453
|
+
const originalPadding = document.body.style.paddingRight;
|
|
3454
|
+
const originalTransition = document.body.style.transition;
|
|
3455
|
+
document.body.style.transition = "padding-right 0.3s ease";
|
|
3456
|
+
document.body.style.paddingRight = isOpen ? `${PANEL_WIDTH}px` : "0px";
|
|
3457
|
+
return () => {
|
|
3458
|
+
document.body.style.paddingRight = originalPadding;
|
|
3459
|
+
document.body.style.transition = originalTransition;
|
|
3460
|
+
};
|
|
3461
|
+
}, [isOpen]);
|
|
3462
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
3463
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3464
|
+
PanelToggle,
|
|
3465
|
+
{
|
|
3466
|
+
isOpen,
|
|
3467
|
+
onClick: () => setIsOpen(!isOpen)
|
|
3468
|
+
}
|
|
3469
|
+
),
|
|
3470
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3471
|
+
ChatPanel,
|
|
3472
|
+
{
|
|
3473
|
+
isOpen,
|
|
3474
|
+
onClose: () => setIsOpen(false),
|
|
3475
|
+
onNavigate,
|
|
3476
|
+
onActionComplete,
|
|
3477
|
+
currentPage,
|
|
3478
|
+
agentUrl,
|
|
3479
|
+
startingQuestions,
|
|
3480
|
+
startingQuestionsEndpoint
|
|
3481
|
+
}
|
|
3482
|
+
)
|
|
3483
|
+
] });
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3486
|
+
// src/createKiteChat.tsx
|
|
3487
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
3488
|
+
function KiteChatWrapper({
|
|
3489
|
+
initialConfig,
|
|
3490
|
+
onConfigUpdate,
|
|
3491
|
+
onStateUpdate
|
|
3492
|
+
}) {
|
|
3493
|
+
const [config, setConfig] = import_react.default.useState(initialConfig);
|
|
3494
|
+
const [currentPage, setCurrentPage] = import_react.default.useState(initialConfig.currentPage || "dashboard");
|
|
3495
|
+
const [isOpen, setIsOpen] = import_react.default.useState(false);
|
|
3496
|
+
const isOpenRef = import_react.default.useRef(false);
|
|
3497
|
+
import_react.default.useEffect(() => {
|
|
3498
|
+
isOpenRef.current = isOpen;
|
|
3499
|
+
}, [isOpen]);
|
|
3500
|
+
import_react.default.useEffect(() => {
|
|
3501
|
+
onConfigUpdate((newConfig) => {
|
|
3502
|
+
if (newConfig.currentPage !== void 0) {
|
|
3503
|
+
setCurrentPage(newConfig.currentPage);
|
|
3504
|
+
}
|
|
3505
|
+
setConfig((prev) => ({ ...prev, ...newConfig }));
|
|
3506
|
+
});
|
|
3507
|
+
onStateUpdate({
|
|
3508
|
+
setIsOpen: (open) => setIsOpen(open),
|
|
3509
|
+
getIsOpen: () => isOpenRef.current
|
|
3510
|
+
});
|
|
3511
|
+
}, [onConfigUpdate, onStateUpdate]);
|
|
3512
|
+
import_react.default.useEffect(() => {
|
|
3513
|
+
const container = document.getElementById("kite-chat-root");
|
|
3514
|
+
if (!container) return;
|
|
3515
|
+
if (config.theme === "dark") {
|
|
3516
|
+
container.classList.add("dark");
|
|
3517
|
+
} else if (config.theme === "light") {
|
|
3518
|
+
container.classList.remove("dark");
|
|
3519
|
+
} else if (config.theme === "system") {
|
|
3520
|
+
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
3521
|
+
container.classList.toggle("dark", prefersDark);
|
|
3522
|
+
}
|
|
3523
|
+
}, [config.theme]);
|
|
3524
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3525
|
+
ChatPanelWithToggle,
|
|
3526
|
+
{
|
|
3527
|
+
isOpen,
|
|
3528
|
+
onOpenChange: setIsOpen,
|
|
3529
|
+
currentPage,
|
|
3530
|
+
agentUrl: config.agentUrl,
|
|
3531
|
+
onNavigate: (page, subtab) => config.onNavigate?.(page, subtab),
|
|
3532
|
+
onActionComplete: config.onActionComplete,
|
|
3533
|
+
startingQuestions: config.startingQuestions,
|
|
3534
|
+
startingQuestionsEndpoint: config.startingQuestionsEndpoint
|
|
3535
|
+
}
|
|
3536
|
+
);
|
|
3537
|
+
}
|
|
3538
|
+
function createKiteChat(config) {
|
|
3539
|
+
let root = null;
|
|
3540
|
+
let containerElement = null;
|
|
3541
|
+
let configUpdater = null;
|
|
3542
|
+
let stateUpdaters = null;
|
|
3543
|
+
let currentConfig = { ...config };
|
|
3544
|
+
const instance = {
|
|
3545
|
+
mount(container) {
|
|
3546
|
+
if (root) {
|
|
3547
|
+
console.warn("[KiteChat] Already mounted. Call unmount() first.");
|
|
3548
|
+
return;
|
|
3549
|
+
}
|
|
3550
|
+
if (typeof container === "string") {
|
|
3551
|
+
containerElement = document.querySelector(container);
|
|
3552
|
+
if (!containerElement) {
|
|
3553
|
+
console.error(`[KiteChat] Container not found: ${container}`);
|
|
3554
|
+
return;
|
|
3555
|
+
}
|
|
3556
|
+
} else {
|
|
3557
|
+
containerElement = container;
|
|
3558
|
+
}
|
|
3559
|
+
if (!containerElement.id) {
|
|
3560
|
+
containerElement.id = "kite-chat-root";
|
|
3561
|
+
}
|
|
3562
|
+
root = (0, import_client.createRoot)(containerElement);
|
|
3563
|
+
root.render(
|
|
3564
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3565
|
+
KiteChatWrapper,
|
|
3566
|
+
{
|
|
3567
|
+
initialConfig: currentConfig,
|
|
3568
|
+
onConfigUpdate: (updater) => {
|
|
3569
|
+
configUpdater = updater;
|
|
3570
|
+
},
|
|
3571
|
+
onStateUpdate: (updaters) => {
|
|
3572
|
+
stateUpdaters = updaters;
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
)
|
|
3576
|
+
);
|
|
3577
|
+
console.log("[KiteChat] Mounted");
|
|
3578
|
+
},
|
|
3579
|
+
unmount() {
|
|
3580
|
+
if (!root) {
|
|
3581
|
+
console.warn("[KiteChat] Not mounted");
|
|
3582
|
+
return;
|
|
3583
|
+
}
|
|
3584
|
+
root.unmount();
|
|
3585
|
+
root = null;
|
|
3586
|
+
containerElement = null;
|
|
3587
|
+
configUpdater = null;
|
|
3588
|
+
stateUpdaters = null;
|
|
3589
|
+
console.log("[KiteChat] Unmounted");
|
|
3590
|
+
},
|
|
3591
|
+
open() {
|
|
3592
|
+
stateUpdaters?.setIsOpen(true);
|
|
3593
|
+
},
|
|
3594
|
+
close() {
|
|
3595
|
+
stateUpdaters?.setIsOpen(false);
|
|
3596
|
+
},
|
|
3597
|
+
toggle() {
|
|
3598
|
+
const isCurrentlyOpen = stateUpdaters?.getIsOpen() ?? false;
|
|
3599
|
+
stateUpdaters?.setIsOpen(!isCurrentlyOpen);
|
|
3600
|
+
},
|
|
3601
|
+
isOpen() {
|
|
3602
|
+
return stateUpdaters?.getIsOpen() ?? false;
|
|
3603
|
+
},
|
|
3604
|
+
setCurrentPage(page) {
|
|
3605
|
+
currentConfig.currentPage = page;
|
|
3606
|
+
configUpdater?.({ currentPage: page });
|
|
3607
|
+
},
|
|
3608
|
+
updateConfig(newConfig) {
|
|
3609
|
+
currentConfig = { ...currentConfig, ...newConfig };
|
|
3610
|
+
configUpdater?.(newConfig);
|
|
3611
|
+
},
|
|
3612
|
+
isMounted() {
|
|
3613
|
+
return root !== null;
|
|
3614
|
+
}
|
|
3615
|
+
};
|
|
3616
|
+
return instance;
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3619
|
+
// src/auto.ts
|
|
3620
|
+
function mountKiteChat(config) {
|
|
3621
|
+
const { container, ...chatConfig } = config;
|
|
3622
|
+
const instance = createKiteChat(chatConfig);
|
|
3623
|
+
let targetContainer;
|
|
3624
|
+
if (container) {
|
|
3625
|
+
targetContainer = container;
|
|
3626
|
+
} else {
|
|
3627
|
+
const defaultContainer = document.createElement("div");
|
|
3628
|
+
defaultContainer.id = "kite-chat-root";
|
|
3629
|
+
document.body.appendChild(defaultContainer);
|
|
3630
|
+
targetContainer = defaultContainer;
|
|
3631
|
+
}
|
|
3632
|
+
instance.mount(targetContainer);
|
|
3633
|
+
return instance;
|
|
3634
|
+
}
|
|
3635
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3636
|
+
0 && (module.exports = {
|
|
3637
|
+
mountKiteChat
|
|
3638
|
+
});
|