@streamoid/catalogix-chat 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +84 -14
  2. package/dist/index.js +3586 -27
  3. package/package.json +19 -2
package/dist/index.js CHANGED
@@ -1,40 +1,3594 @@
1
1
  // src/CatalogixChat.tsx
2
- import { useState, useEffect } from "react";
3
- import { jsx, jsxs } from "react/jsx-runtime";
4
- function CatalogixChat(props) {
5
- const { uiProps, meta } = props;
6
- const componentName = uiProps?._componentName;
7
- const initialSection = meta?.step || componentName || null;
8
- const [section, setSection] = useState(initialSection);
9
- useEffect(() => {
10
- const metaStep = meta?.step;
11
- if (metaStep && metaStep !== section) {
12
- setSection(metaStep);
2
+ import { useEffect as useEffect7 } from "react";
3
+
4
+ // src/api.ts
5
+ var _config = {
6
+ catalogixBaseUrl: "",
7
+ keplerProxyPrefix: "/kepler-api"
8
+ };
9
+ function configureApi(config) {
10
+ _config = { ..._config, ...config };
11
+ }
12
+ function getApiConfig() {
13
+ return _config;
14
+ }
15
+ async function fetchJson(url, options) {
16
+ const response = await fetch(url, {
17
+ credentials: "include",
18
+ ...options,
19
+ headers: {
20
+ Accept: "application/json",
21
+ ...options?.headers || {}
13
22
  }
14
- }, [meta?.step]);
15
- return /* @__PURE__ */ jsxs(
23
+ });
24
+ if (!response.ok) {
25
+ throw new Error(`API call failed: ${response.status} ${response.statusText}`);
26
+ }
27
+ return response.json();
28
+ }
29
+
30
+ // src/SelectProducts/index.tsx
31
+ import { useCallback as useCallback4, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
32
+ import { CheckCircle, Loader2 as Loader23, Search as Search2 } from "lucide-react";
33
+
34
+ // src/ui/button.tsx
35
+ import { Slot } from "@radix-ui/react-slot";
36
+ import { cva } from "class-variance-authority";
37
+
38
+ // src/ui/utils.ts
39
+ import { clsx } from "clsx";
40
+ import { twMerge } from "tailwind-merge";
41
+ function cn(...inputs) {
42
+ return twMerge(clsx(inputs));
43
+ }
44
+
45
+ // src/ui/button.tsx
46
+ import { jsx } from "react/jsx-runtime";
47
+ var buttonVariants = cva(
48
+ "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",
49
+ {
50
+ variants: {
51
+ variant: {
52
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
53
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
54
+ outline: "border bg-background text-foreground hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
55
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
56
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
57
+ link: "text-primary underline-offset-4 hover:underline"
58
+ },
59
+ size: {
60
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
61
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
62
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
63
+ icon: "size-9 rounded-md"
64
+ }
65
+ },
66
+ defaultVariants: {
67
+ variant: "default",
68
+ size: "default"
69
+ }
70
+ }
71
+ );
72
+ function Button({
73
+ className,
74
+ variant,
75
+ size,
76
+ asChild = false,
77
+ ...props
78
+ }) {
79
+ const Comp = asChild ? Slot : "button";
80
+ return /* @__PURE__ */ jsx(
81
+ Comp,
82
+ {
83
+ "data-slot": "button",
84
+ className: cn(buttonVariants({ variant, size, className })),
85
+ ...props
86
+ }
87
+ );
88
+ }
89
+
90
+ // src/ui/input.tsx
91
+ import { jsx as jsx2 } from "react/jsx-runtime";
92
+ function Input({ className, type, ...props }) {
93
+ return /* @__PURE__ */ jsx2(
94
+ "input",
95
+ {
96
+ type,
97
+ "data-slot": "input",
98
+ className: cn(
99
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border px-3 py-1 text-base bg-input-background 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",
100
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
101
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
102
+ className
103
+ ),
104
+ ...props
105
+ }
106
+ );
107
+ }
108
+
109
+ // src/ui/card.tsx
110
+ import { jsx as jsx3 } from "react/jsx-runtime";
111
+ function Card({ className, ...props }) {
112
+ return /* @__PURE__ */ jsx3(
16
113
  "div",
17
114
  {
18
- style: {
19
- padding: "16px",
20
- fontFamily: "system-ui, sans-serif",
21
- color: "#333"
22
- },
115
+ "data-slot": "card",
116
+ className: cn(
117
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border",
118
+ className
119
+ ),
120
+ ...props
121
+ }
122
+ );
123
+ }
124
+
125
+ // src/SelectProducts/ProductTable.tsx
126
+ import { useCallback as useCallback3, useEffect as useEffect2, useMemo, useRef as useRef3, useState as useState3 } from "react";
127
+ import {
128
+ flexRender,
129
+ getCoreRowModel,
130
+ useReactTable
131
+ } from "@tanstack/react-table";
132
+ import { ChevronLeft, ChevronRight, Package, Layers } from "lucide-react";
133
+
134
+ // src/ui/table.tsx
135
+ import { jsx as jsx4 } from "react/jsx-runtime";
136
+ function Table({ className, ...props }) {
137
+ return /* @__PURE__ */ jsx4(
138
+ "div",
139
+ {
140
+ "data-slot": "table-container",
141
+ className: "relative w-full overflow-x-auto",
142
+ children: /* @__PURE__ */ jsx4(
143
+ "table",
144
+ {
145
+ "data-slot": "table",
146
+ className: cn("w-full caption-bottom text-sm", className),
147
+ ...props
148
+ }
149
+ )
150
+ }
151
+ );
152
+ }
153
+ function TableHeader({ className, ...props }) {
154
+ return /* @__PURE__ */ jsx4(
155
+ "thead",
156
+ {
157
+ "data-slot": "table-header",
158
+ className: cn("[&_tr]:border-b", className),
159
+ ...props
160
+ }
161
+ );
162
+ }
163
+ function TableBody({ className, ...props }) {
164
+ return /* @__PURE__ */ jsx4(
165
+ "tbody",
166
+ {
167
+ "data-slot": "table-body",
168
+ className: cn("[&_tr:last-child]:border-0", className),
169
+ ...props
170
+ }
171
+ );
172
+ }
173
+ function TableRow({ className, ...props }) {
174
+ return /* @__PURE__ */ jsx4(
175
+ "tr",
176
+ {
177
+ "data-slot": "table-row",
178
+ className: cn(
179
+ "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
180
+ className
181
+ ),
182
+ ...props
183
+ }
184
+ );
185
+ }
186
+ function TableHead({ className, ...props }) {
187
+ return /* @__PURE__ */ jsx4(
188
+ "th",
189
+ {
190
+ "data-slot": "table-head",
191
+ className: cn(
192
+ "text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
193
+ className
194
+ ),
195
+ ...props
196
+ }
197
+ );
198
+ }
199
+ function TableCell({ className, ...props }) {
200
+ return /* @__PURE__ */ jsx4(
201
+ "td",
202
+ {
203
+ "data-slot": "table-cell",
204
+ className: cn(
205
+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
206
+ className
207
+ ),
208
+ ...props
209
+ }
210
+ );
211
+ }
212
+
213
+ // src/ui/checkbox.tsx
214
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
215
+ import { CheckIcon } from "lucide-react";
216
+ import { jsx as jsx5 } from "react/jsx-runtime";
217
+ function Checkbox({
218
+ className,
219
+ ...props
220
+ }) {
221
+ return /* @__PURE__ */ jsx5(
222
+ CheckboxPrimitive.Root,
223
+ {
224
+ "data-slot": "checkbox",
225
+ className: cn(
226
+ "peer border border-muted-foreground/40 bg-input-background dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
227
+ className
228
+ ),
229
+ ...props,
230
+ children: /* @__PURE__ */ jsx5(
231
+ CheckboxPrimitive.Indicator,
232
+ {
233
+ "data-slot": "checkbox-indicator",
234
+ className: "flex items-center justify-center text-current transition-none",
235
+ children: /* @__PURE__ */ jsx5(CheckIcon, { className: "size-3.5" })
236
+ }
237
+ )
238
+ }
239
+ );
240
+ }
241
+
242
+ // src/ui/badge.tsx
243
+ import { Slot as Slot2 } from "@radix-ui/react-slot";
244
+ import { cva as cva2 } from "class-variance-authority";
245
+ import { jsx as jsx6 } from "react/jsx-runtime";
246
+ var badgeVariants = cva2(
247
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-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 transition-[color,box-shadow] overflow-hidden",
248
+ {
249
+ variants: {
250
+ variant: {
251
+ default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
252
+ secondary: "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
253
+ destructive: "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
254
+ outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground"
255
+ }
256
+ },
257
+ defaultVariants: {
258
+ variant: "default"
259
+ }
260
+ }
261
+ );
262
+ function Badge({
263
+ className,
264
+ variant,
265
+ asChild = false,
266
+ ...props
267
+ }) {
268
+ const Comp = asChild ? Slot2 : "span";
269
+ return /* @__PURE__ */ jsx6(
270
+ Comp,
271
+ {
272
+ "data-slot": "badge",
273
+ className: cn(badgeVariants({ variant }), className),
274
+ ...props
275
+ }
276
+ );
277
+ }
278
+
279
+ // src/ui/skeleton.tsx
280
+ import { jsx as jsx7 } from "react/jsx-runtime";
281
+ function Skeleton({ className, ...props }) {
282
+ return /* @__PURE__ */ jsx7(
283
+ "div",
284
+ {
285
+ "data-slot": "skeleton",
286
+ className: cn("bg-accent animate-pulse rounded-md", className),
287
+ ...props
288
+ }
289
+ );
290
+ }
291
+
292
+ // src/SelectProducts/ImageCell.tsx
293
+ import { useCallback, useRef, useState } from "react";
294
+ import { createPortal } from "react-dom";
295
+
296
+ // src/ui/dialog.tsx
297
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
298
+ import { XIcon } from "lucide-react";
299
+ import { jsx as jsx8, jsxs } from "react/jsx-runtime";
300
+ function Dialog({
301
+ ...props
302
+ }) {
303
+ return /* @__PURE__ */ jsx8(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
304
+ }
305
+ function DialogPortal({
306
+ ...props
307
+ }) {
308
+ return /* @__PURE__ */ jsx8(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
309
+ }
310
+ function DialogOverlay({
311
+ className,
312
+ ...props
313
+ }) {
314
+ return /* @__PURE__ */ jsx8(
315
+ DialogPrimitive.Overlay,
316
+ {
317
+ "data-slot": "dialog-overlay",
318
+ className: cn(
319
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/70",
320
+ className
321
+ ),
322
+ ...props
323
+ }
324
+ );
325
+ }
326
+ function DialogContent({
327
+ className,
328
+ children,
329
+ ...props
330
+ }) {
331
+ return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
332
+ /* @__PURE__ */ jsx8(DialogOverlay, {}),
333
+ /* @__PURE__ */ jsxs(
334
+ DialogPrimitive.Content,
335
+ {
336
+ "data-slot": "dialog-content",
337
+ className: cn(
338
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
339
+ className
340
+ ),
341
+ ...props,
342
+ children: [
343
+ children,
344
+ /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", children: [
345
+ /* @__PURE__ */ jsx8(XIcon, {}),
346
+ /* @__PURE__ */ jsx8("span", { className: "sr-only", children: "Close" })
347
+ ] })
348
+ ]
349
+ }
350
+ )
351
+ ] });
352
+ }
353
+ function DialogHeader({ className, ...props }) {
354
+ return /* @__PURE__ */ jsx8(
355
+ "div",
356
+ {
357
+ "data-slot": "dialog-header",
358
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
359
+ ...props
360
+ }
361
+ );
362
+ }
363
+ function DialogTitle({
364
+ className,
365
+ ...props
366
+ }) {
367
+ return /* @__PURE__ */ jsx8(
368
+ DialogPrimitive.Title,
369
+ {
370
+ "data-slot": "dialog-title",
371
+ className: cn("text-lg leading-none font-semibold", className),
372
+ ...props
373
+ }
374
+ );
375
+ }
376
+ function DialogDescription({
377
+ className,
378
+ ...props
379
+ }) {
380
+ return /* @__PURE__ */ jsx8(
381
+ DialogPrimitive.Description,
382
+ {
383
+ "data-slot": "dialog-description",
384
+ className: cn("text-muted-foreground text-sm", className),
385
+ ...props
386
+ }
387
+ );
388
+ }
389
+
390
+ // src/SelectProducts/ImageCell.tsx
391
+ import { Fragment, jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
392
+ function thumbnailUrl(src) {
393
+ if (!src) return "";
394
+ const cleaned = src.replace("dl=0", "raw=1").replace("dl=1", "raw=1");
395
+ return cleaned.includes("?") ? `${cleaned}&format=webp&w=120` : `${cleaned}?format=webp&w=120`;
396
+ }
397
+ function fullSizeUrl(src) {
398
+ if (!src) return "";
399
+ const cleaned = src.replace("dl=0", "raw=1").replace("dl=1", "raw=1");
400
+ return cleaned.includes("?") ? `${cleaned}&format=webp&w=600` : `${cleaned}?format=webp&w=600`;
401
+ }
402
+ function ImageCell({ src }) {
403
+ const [hovered, setHovered] = useState(false);
404
+ const [dialogOpen, setDialogOpen] = useState(false);
405
+ const cellRef = useRef(null);
406
+ const [zoomPos, setZoomPos] = useState({ top: 0, left: 0 });
407
+ const handleMouseEnter = useCallback(() => {
408
+ if (!cellRef.current || !src) return;
409
+ const rect = cellRef.current.getBoundingClientRect();
410
+ setZoomPos({
411
+ top: rect.top,
412
+ left: rect.right + 8
413
+ });
414
+ setHovered(true);
415
+ }, [src]);
416
+ const handleMouseLeave = useCallback(() => {
417
+ setHovered(false);
418
+ }, []);
419
+ if (!src) {
420
+ return /* @__PURE__ */ jsx9("div", { className: "flex h-[60px] w-[50px] items-center justify-center rounded bg-muted text-xs text-muted-foreground", children: "No image" });
421
+ }
422
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
423
+ /* @__PURE__ */ jsx9(
424
+ "div",
425
+ {
426
+ ref: cellRef,
427
+ className: "relative cursor-pointer",
428
+ onMouseEnter: handleMouseEnter,
429
+ onMouseLeave: handleMouseLeave,
430
+ onClick: () => setDialogOpen(true),
431
+ children: /* @__PURE__ */ jsx9(
432
+ "img",
433
+ {
434
+ src: thumbnailUrl(src),
435
+ alt: "",
436
+ className: "h-[60px] w-[50px] rounded-md object-cover transition-shadow hover:shadow-md",
437
+ loading: "lazy"
438
+ }
439
+ )
440
+ }
441
+ ),
442
+ hovered && createPortal(
443
+ /* @__PURE__ */ jsx9(
444
+ "div",
445
+ {
446
+ className: "pointer-events-none fixed z-[9999] rounded-lg border bg-background shadow-xl",
447
+ style: {
448
+ top: zoomPos.top,
449
+ left: zoomPos.left,
450
+ maxWidth: 400
451
+ },
452
+ children: /* @__PURE__ */ jsx9(
453
+ "img",
454
+ {
455
+ src: fullSizeUrl(src),
456
+ alt: "",
457
+ className: "max-h-[400px] max-w-[400px] rounded-lg object-contain"
458
+ }
459
+ )
460
+ }
461
+ ),
462
+ document.body
463
+ ),
464
+ /* @__PURE__ */ jsx9(Dialog, { open: dialogOpen, onOpenChange: setDialogOpen, children: /* @__PURE__ */ jsxs2(DialogContent, { className: "max-w-2xl", children: [
465
+ /* @__PURE__ */ jsx9(DialogHeader, { children: /* @__PURE__ */ jsx9(DialogTitle, { children: "Image Preview" }) }),
466
+ /* @__PURE__ */ jsx9("div", { className: "flex items-center justify-center p-2", children: /* @__PURE__ */ jsx9(
467
+ "img",
468
+ {
469
+ src: fullSizeUrl(src),
470
+ alt: "",
471
+ className: "max-h-[70vh] max-w-full rounded-md object-contain"
472
+ }
473
+ ) })
474
+ ] }) })
475
+ ] });
476
+ }
477
+
478
+ // src/SelectProducts/EditableCell.tsx
479
+ import { useCallback as useCallback2, useEffect, useRef as useRef2, useState as useState2 } from "react";
480
+ import { Check, ChevronsUpDown, Pencil, Loader2 } from "lucide-react";
481
+
482
+ // src/ui/popover.tsx
483
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
484
+ import { jsx as jsx10 } from "react/jsx-runtime";
485
+ function Popover({
486
+ ...props
487
+ }) {
488
+ return /* @__PURE__ */ jsx10(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
489
+ }
490
+ function PopoverTrigger({
491
+ ...props
492
+ }) {
493
+ return /* @__PURE__ */ jsx10(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
494
+ }
495
+ function PopoverContent({
496
+ className,
497
+ align = "center",
498
+ sideOffset = 4,
499
+ ...props
500
+ }) {
501
+ return /* @__PURE__ */ jsx10(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx10(
502
+ PopoverPrimitive.Content,
503
+ {
504
+ "data-slot": "popover-content",
505
+ align,
506
+ sideOffset,
507
+ className: cn(
508
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
509
+ className
510
+ ),
511
+ ...props
512
+ }
513
+ ) });
514
+ }
515
+
516
+ // src/ui/command.tsx
517
+ import { Command as CommandPrimitive } from "cmdk";
518
+ import { SearchIcon } from "lucide-react";
519
+ import { jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
520
+ function Command({
521
+ className,
522
+ ...props
523
+ }) {
524
+ return /* @__PURE__ */ jsx11(
525
+ CommandPrimitive,
526
+ {
527
+ "data-slot": "command",
528
+ className: cn(
529
+ "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
530
+ className
531
+ ),
532
+ ...props
533
+ }
534
+ );
535
+ }
536
+ function CommandInput({
537
+ className,
538
+ ...props
539
+ }) {
540
+ return /* @__PURE__ */ jsxs3(
541
+ "div",
542
+ {
543
+ "data-slot": "command-input-wrapper",
544
+ className: "flex h-9 items-center gap-2 border-b px-3",
545
+ children: [
546
+ /* @__PURE__ */ jsx11(SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
547
+ /* @__PURE__ */ jsx11(
548
+ CommandPrimitive.Input,
549
+ {
550
+ "data-slot": "command-input",
551
+ className: cn(
552
+ "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
553
+ className
554
+ ),
555
+ ...props
556
+ }
557
+ )
558
+ ]
559
+ }
560
+ );
561
+ }
562
+ function CommandList({
563
+ className,
564
+ ...props
565
+ }) {
566
+ return /* @__PURE__ */ jsx11(
567
+ CommandPrimitive.List,
568
+ {
569
+ "data-slot": "command-list",
570
+ className: cn(
571
+ "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
572
+ className
573
+ ),
574
+ ...props
575
+ }
576
+ );
577
+ }
578
+ function CommandEmpty({
579
+ ...props
580
+ }) {
581
+ return /* @__PURE__ */ jsx11(
582
+ CommandPrimitive.Empty,
583
+ {
584
+ "data-slot": "command-empty",
585
+ className: "py-6 text-center text-sm",
586
+ ...props
587
+ }
588
+ );
589
+ }
590
+ function CommandGroup({
591
+ className,
592
+ ...props
593
+ }) {
594
+ return /* @__PURE__ */ jsx11(
595
+ CommandPrimitive.Group,
596
+ {
597
+ "data-slot": "command-group",
598
+ className: cn(
599
+ "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
600
+ className
601
+ ),
602
+ ...props
603
+ }
604
+ );
605
+ }
606
+ function CommandItem({
607
+ className,
608
+ ...props
609
+ }) {
610
+ return /* @__PURE__ */ jsx11(
611
+ CommandPrimitive.Item,
612
+ {
613
+ "data-slot": "command-item",
614
+ className: cn(
615
+ "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
616
+ className
617
+ ),
618
+ ...props
619
+ }
620
+ );
621
+ }
622
+
623
+ // src/SelectProducts/utils.ts
624
+ var PRODUCTS_PER_PAGE = 50;
625
+ var DEFAULT_SOURCE_MP = "SMP";
626
+ var DEFAULT_COLUMNS = [
627
+ { name: "Image URL 1", value: "Image URL 1", channel: "SMP" },
628
+ { name: "Image URL 2", value: "Image URL 2", channel: "SMP" },
629
+ { name: "SKU Code", value: "SKU Code", channel: "SMP" },
630
+ { name: "Style Code", value: "Style Code", channel: "SMP" },
631
+ { name: "Category", value: "Category", channel: "SMP" },
632
+ { name: "Department", value: "Department", channel: "SMP" },
633
+ { name: "Status", value: "status", channel: "required" },
634
+ { name: "updated on", value: "modified_ts", channel: "SMP" }
635
+ ];
636
+ var CORE_COLUMN_VALUES = /* @__PURE__ */ new Set([
637
+ "Image URL 1",
638
+ "Image URL 2",
639
+ "SKU Code",
640
+ "Style Code",
641
+ "Category",
642
+ "Department",
643
+ "status",
644
+ "modified_ts"
645
+ ]);
646
+ var NON_IMAGE_KEYS = /* @__PURE__ */ new Set(["image_count"]);
647
+ function buildFromRequiredFields(data) {
648
+ const columns = [];
649
+ const allowedValues = {};
650
+ const smpFields = data?.SMP;
651
+ if (smpFields && typeof smpFields === "object") {
652
+ const templateMap = {};
653
+ for (const [fieldName, fieldDef] of Object.entries(smpFields)) {
654
+ if (fieldName === "product_code" || fieldName === "store_uuid") continue;
655
+ columns.push({
656
+ name: fieldName,
657
+ value: fieldName,
658
+ channel: "SMP"
659
+ });
660
+ templateMap[fieldName] = fieldDef;
661
+ }
662
+ allowedValues.SMP = { _all: templateMap };
663
+ }
664
+ const productFields = data?.required;
665
+ if (productFields && typeof productFields === "object") {
666
+ const productMap = {};
667
+ for (const [fieldName, fieldDef] of Object.entries(productFields)) {
668
+ if (fieldName === "product_code" || fieldName === "store_uuid") continue;
669
+ if ("values" in fieldDef && fieldDef.values) {
670
+ productMap[fieldName] = { values: fieldDef.values };
671
+ }
672
+ }
673
+ if (Object.keys(productMap).length > 0) {
674
+ allowedValues.product = productMap;
675
+ }
676
+ }
677
+ return { columns, allowedValues };
678
+ }
679
+ function buildFieldsFromColumns(columns, sourceMarketplace = DEFAULT_SOURCE_MP) {
680
+ const fields = {};
681
+ for (const col of columns) {
682
+ const channel = col.channel || sourceMarketplace;
683
+ if (!fields[channel]) fields[channel] = [];
684
+ fields[channel].push(col.value, `${col.value}_automated`);
685
+ }
686
+ if (!fields.required) fields.required = [];
687
+ const requiredDefaults = [
688
+ "status",
689
+ "status_automated",
690
+ "assignee_user_id",
691
+ "assignee_user_id_automated",
692
+ "product_uuid",
693
+ "store_uuid"
694
+ ];
695
+ fields.required = Array.from(/* @__PURE__ */ new Set([...fields.required, ...requiredDefaults]));
696
+ return fields;
697
+ }
698
+ function buildFiltersFromFields(fields) {
699
+ const filters = {};
700
+ for (const channel of Object.keys(fields)) {
701
+ filters[channel] = {};
702
+ }
703
+ return filters;
704
+ }
705
+ function formatProducts(groups) {
706
+ const rows = [];
707
+ for (const group of groups) {
708
+ const channels = Object.keys(group.data);
709
+ const variantsByIndex = [];
710
+ for (const channel of channels) {
711
+ const items = group.data[channel];
712
+ if (!items?.length) continue;
713
+ items.forEach((item, idx) => {
714
+ const flat = {};
715
+ for (const [key, val] of Object.entries(item)) {
716
+ if (key.endsWith("_details") || key === "_id" || key === "ov") continue;
717
+ flat[`${key}::${channel}`] = val;
718
+ }
719
+ if (variantsByIndex[idx]) {
720
+ Object.assign(variantsByIndex[idx], flat);
721
+ } else {
722
+ variantsByIndex[idx] = flat;
723
+ }
724
+ });
725
+ }
726
+ for (const variant of variantsByIndex) {
727
+ if (!variant) continue;
728
+ const productCode = variant["product_code::product"] || variant["product_code::SMP"] || "";
729
+ const productUuid = variant["product_uuid::product"] || variant["product_uuid::SMP"] || "";
730
+ const storeUuid = variant["store_uuid::product"] || variant["store_uuid::SMP"] || "";
731
+ const parentCode = variant["parent_code::product"] || group.group_id || "";
732
+ const status = variant["status::product"] || "";
733
+ const template = variant["template::SMP"] || "";
734
+ rows.push({
735
+ ...variant,
736
+ id: `${productCode}==${storeUuid}`,
737
+ product_code: productCode,
738
+ product_uuid: productUuid,
739
+ store_uuid: storeUuid,
740
+ parent_code: parentCode,
741
+ status,
742
+ template
743
+ });
744
+ }
745
+ }
746
+ return rows;
747
+ }
748
+ function formatProductsByStyle(rows) {
749
+ const seen = /* @__PURE__ */ new Map();
750
+ for (const row of rows) {
751
+ const key = row.parent_code || row.product_code;
752
+ if (!seen.has(key)) {
753
+ seen.set(key, { ...row, _variantCount: 1 });
754
+ } else {
755
+ seen.get(key)._variantCount = (seen.get(key)._variantCount || 1) + 1;
756
+ }
757
+ }
758
+ return Array.from(seen.values());
759
+ }
760
+ function getCellType(columnValue, channel, allowedValues, template) {
761
+ const lower = columnValue.toLowerCase();
762
+ if ((lower.includes("image") || lower.includes("url")) && !NON_IMAGE_KEYS.has(lower)) {
763
+ return "image";
764
+ }
765
+ if (lower === "modified_ts" || lower.includes("_ts")) {
766
+ return "timestamp";
767
+ }
768
+ const values = getDropdownValues(allowedValues, template, columnValue, channel);
769
+ if (values && values.length > 0) {
770
+ return "select";
771
+ }
772
+ return "text";
773
+ }
774
+ function getDropdownValues(allowedValues, template, fieldName, channel) {
775
+ if (!allowedValues) return null;
776
+ const apiChannel = channel === "required" ? "product" : channel;
777
+ const channelValues = allowedValues[apiChannel];
778
+ if (!channelValues) return null;
779
+ if (apiChannel === "product") {
780
+ const fieldDef = channelValues[fieldName];
781
+ return fieldDef?.values ?? null;
782
+ }
783
+ if (template && channelValues[template]) {
784
+ const templateValues = channelValues[template];
785
+ const fieldDef = templateValues[fieldName];
786
+ if (fieldDef?.values) return fieldDef.values;
787
+ }
788
+ if (channelValues._all) {
789
+ const allValues = channelValues._all;
790
+ const fieldDef = allValues[fieldName];
791
+ if (fieldDef?.values) return fieldDef.values;
792
+ }
793
+ return null;
794
+ }
795
+ function formatTimestamp(value) {
796
+ if (!value) return "";
797
+ const num = typeof value === "number" ? value : Number(value);
798
+ if (Number.isNaN(num)) return String(value);
799
+ const date = new Date(num * 1e3);
800
+ const timeStr = date.toLocaleTimeString("en-US", {
801
+ hour: "numeric",
802
+ minute: "2-digit",
803
+ hour12: true
804
+ });
805
+ const dateStr = date.toLocaleDateString("en-US", {
806
+ month: "2-digit",
807
+ day: "2-digit"
808
+ });
809
+ return `${timeStr} ${dateStr}`;
810
+ }
811
+ function getCellValue(row, columnValue, channel) {
812
+ const apiChannel = channel === "required" ? "product" : channel;
813
+ const namespacedKey = `${columnValue}::${apiChannel}`;
814
+ if (row[namespacedKey] !== void 0) return row[namespacedKey];
815
+ if (row[columnValue] !== void 0) return row[columnValue];
816
+ return "";
817
+ }
818
+
819
+ // src/SelectProducts/EditableCell.tsx
820
+ import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
821
+ var STATUS_COLORS = {
822
+ active: "bg-emerald-100 text-emerald-800 border-emerald-200",
823
+ inactive: "bg-gray-100 text-gray-600 border-gray-200",
824
+ draft: "bg-amber-100 text-amber-800 border-amber-200",
825
+ new: "bg-sky-100 text-sky-800 border-sky-200",
826
+ problem: "bg-red-100 text-red-800 border-red-200",
827
+ "under review": "bg-violet-100 text-violet-800 border-violet-200"
828
+ };
829
+ function EditableCell({
830
+ value,
831
+ cellType,
832
+ fieldName,
833
+ allowedValues,
834
+ onSave,
835
+ readOnly = false
836
+ }) {
837
+ const displayValue = cellType === "timestamp" ? formatTimestamp(value) : String(value ?? "");
838
+ if (cellType === "timestamp") {
839
+ return /* @__PURE__ */ jsx12("span", { className: "text-sm text-muted-foreground", children: displayValue });
840
+ }
841
+ const isStatusField = fieldName === "status";
842
+ if (readOnly) {
843
+ if (isStatusField && displayValue) {
844
+ return /* @__PURE__ */ jsx12(
845
+ Badge,
846
+ {
847
+ variant: "outline",
848
+ className: cn("text-xs font-medium", STATUS_COLORS[displayValue] ?? ""),
849
+ children: displayValue
850
+ }
851
+ );
852
+ }
853
+ return /* @__PURE__ */ jsx12("span", { className: "text-sm", children: displayValue });
854
+ }
855
+ if (allowedValues && allowedValues.length > 0) {
856
+ return /* @__PURE__ */ jsx12(
857
+ DropdownEditor,
858
+ {
859
+ value: displayValue,
860
+ options: allowedValues,
861
+ onSave,
862
+ isStatus: isStatusField
863
+ }
864
+ );
865
+ }
866
+ return /* @__PURE__ */ jsx12(TextEditor, { value: displayValue, onSave });
867
+ }
868
+ function DropdownEditor({
869
+ value,
870
+ options,
871
+ onSave,
872
+ isStatus
873
+ }) {
874
+ const [open, setOpen] = useState2(false);
875
+ const [saving, setSaving] = useState2(false);
876
+ const handleSelect = useCallback2(
877
+ async (selected) => {
878
+ if (selected === value) {
879
+ setOpen(false);
880
+ return;
881
+ }
882
+ setSaving(true);
883
+ try {
884
+ await onSave(selected);
885
+ } finally {
886
+ setSaving(false);
887
+ setOpen(false);
888
+ }
889
+ },
890
+ [onSave, value]
891
+ );
892
+ const displayNode = isStatus ? /* @__PURE__ */ jsx12(
893
+ Badge,
894
+ {
895
+ variant: "outline",
896
+ className: cn("text-xs font-medium", STATUS_COLORS[value] ?? ""),
897
+ children: value || "\u2014"
898
+ }
899
+ ) : /* @__PURE__ */ jsx12("span", { className: "truncate text-sm", children: value || "\u2014" });
900
+ return /* @__PURE__ */ jsxs4(Popover, { open, onOpenChange: setOpen, children: [
901
+ /* @__PURE__ */ jsxs4(
902
+ PopoverTrigger,
903
+ {
904
+ disabled: saving,
905
+ className: buttonVariants({
906
+ variant: "ghost",
907
+ size: "sm",
908
+ className: "group h-auto w-full justify-between gap-1 px-2 py-1 font-normal"
909
+ }),
910
+ children: [
911
+ saving ? /* @__PURE__ */ jsx12(Loader2, { className: "size-3.5 animate-spin" }) : displayNode,
912
+ /* @__PURE__ */ jsx12(ChevronsUpDown, { className: "size-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-50" })
913
+ ]
914
+ }
915
+ ),
916
+ /* @__PURE__ */ jsx12(PopoverContent, { className: "w-[220px] p-0", align: "start", children: /* @__PURE__ */ jsxs4(Command, { className: "[&_[data-slot=command-input-wrapper]]:h-8 [&_[data-slot=command-input]]:h-7 [&_[data-slot=command-input]]:py-1 [&_[data-slot=command-input]]:pl-1", children: [
917
+ /* @__PURE__ */ jsx12(CommandInput, { placeholder: `Search ${options.length} values...` }),
918
+ /* @__PURE__ */ jsxs4(CommandList, { children: [
919
+ /* @__PURE__ */ jsx12(CommandEmpty, { children: "No match found." }),
920
+ /* @__PURE__ */ jsx12(CommandGroup, { children: options.map((option) => /* @__PURE__ */ jsxs4(
921
+ CommandItem,
922
+ {
923
+ value: option,
924
+ onSelect: () => handleSelect(option),
925
+ children: [
926
+ isStatus ? /* @__PURE__ */ jsx12(
927
+ Badge,
928
+ {
929
+ variant: "outline",
930
+ className: cn(
931
+ "text-xs",
932
+ STATUS_COLORS[option] ?? ""
933
+ ),
934
+ children: option
935
+ }
936
+ ) : option,
937
+ /* @__PURE__ */ jsx12(
938
+ Check,
939
+ {
940
+ className: cn(
941
+ "ml-auto size-3.5",
942
+ value === option ? "opacity-100" : "opacity-0"
943
+ )
944
+ }
945
+ )
946
+ ]
947
+ },
948
+ option
949
+ )) })
950
+ ] })
951
+ ] }) })
952
+ ] });
953
+ }
954
+ function TextEditor({
955
+ value,
956
+ onSave
957
+ }) {
958
+ const [editing, setEditing] = useState2(false);
959
+ const [draft, setDraft] = useState2(value);
960
+ const [saving, setSaving] = useState2(false);
961
+ const inputRef = useRef2(null);
962
+ useEffect(() => {
963
+ setDraft(value);
964
+ }, [value]);
965
+ useEffect(() => {
966
+ if (editing) {
967
+ inputRef.current?.focus();
968
+ inputRef.current?.select();
969
+ }
970
+ }, [editing]);
971
+ const commit = useCallback2(async () => {
972
+ const trimmed = draft.trim();
973
+ if (trimmed === value) {
974
+ setEditing(false);
975
+ return;
976
+ }
977
+ setSaving(true);
978
+ try {
979
+ await onSave(trimmed);
980
+ setEditing(false);
981
+ } catch {
982
+ setDraft(value);
983
+ setEditing(false);
984
+ } finally {
985
+ setSaving(false);
986
+ }
987
+ }, [draft, value, onSave]);
988
+ if (editing) {
989
+ return /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1", children: [
990
+ /* @__PURE__ */ jsx12(
991
+ Input,
992
+ {
993
+ ref: inputRef,
994
+ value: draft,
995
+ onChange: (e) => setDraft(e.target.value),
996
+ onBlur: commit,
997
+ onKeyDown: (e) => {
998
+ if (e.key === "Enter") commit();
999
+ if (e.key === "Escape") {
1000
+ setDraft(value);
1001
+ setEditing(false);
1002
+ }
1003
+ },
1004
+ disabled: saving,
1005
+ className: "h-7 text-sm"
1006
+ }
1007
+ ),
1008
+ saving && /* @__PURE__ */ jsx12(Loader2, { className: "size-3.5 animate-spin text-muted-foreground" })
1009
+ ] });
1010
+ }
1011
+ return /* @__PURE__ */ jsxs4(
1012
+ "button",
1013
+ {
1014
+ type: "button",
1015
+ className: "group flex w-full items-center gap-1.5 rounded px-1 py-0.5 text-left text-sm hover:bg-accent",
1016
+ onClick: () => setEditing(true),
23
1017
  children: [
24
- /* @__PURE__ */ jsxs("p", { style: { fontSize: "14px", color: "#666" }, children: [
25
- /* @__PURE__ */ jsx("strong", { children: "Catalogix" }),
26
- " \u2014 section: ",
27
- /* @__PURE__ */ jsx("code", { children: section })
1018
+ /* @__PURE__ */ jsx12("span", { className: "flex-1 truncate", children: value || "\u2014" }),
1019
+ /* @__PURE__ */ jsx12(Pencil, { className: "size-3 shrink-0 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100" })
1020
+ ]
1021
+ }
1022
+ );
1023
+ }
1024
+
1025
+ // src/SelectProducts/ProductTable.tsx
1026
+ import { jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
1027
+ function ProductTable({
1028
+ products,
1029
+ columns: appColumns,
1030
+ allowedValues,
1031
+ loading,
1032
+ columnVisibility,
1033
+ onColumnVisibilityChange,
1034
+ rowSelection,
1035
+ onRowSelectionChange,
1036
+ onCellEdit,
1037
+ currentPage,
1038
+ totalPages,
1039
+ totalProducts,
1040
+ totalSkus,
1041
+ onPageChange,
1042
+ globalFilter
1043
+ }) {
1044
+ const tanstackColumns = useMemo(() => {
1045
+ const selectCol = {
1046
+ id: "_select",
1047
+ header: ({ table: table2 }) => /* @__PURE__ */ jsx13(
1048
+ Checkbox,
1049
+ {
1050
+ checked: table2.getIsAllPageRowsSelected() || table2.getIsSomePageRowsSelected() && "indeterminate",
1051
+ onCheckedChange: (checked) => table2.toggleAllPageRowsSelected(!!checked),
1052
+ "aria-label": "Select all",
1053
+ className: "translate-y-[2px]"
1054
+ }
1055
+ ),
1056
+ cell: ({ row }) => /* @__PURE__ */ jsx13(
1057
+ Checkbox,
1058
+ {
1059
+ checked: row.getIsSelected(),
1060
+ onCheckedChange: (checked) => row.toggleSelected(!!checked),
1061
+ "aria-label": "Select row",
1062
+ className: "translate-y-[2px]"
1063
+ }
1064
+ ),
1065
+ size: 40,
1066
+ enableHiding: false
1067
+ };
1068
+ const dataCols = appColumns.map(
1069
+ (col) => ({
1070
+ id: col.value,
1071
+ accessorFn: (row) => getCellValue(row, col.value, col.channel),
1072
+ header: () => col.name,
1073
+ cell: ({ row: tableRow }) => {
1074
+ const rowData = tableRow.original;
1075
+ const rawValue = getCellValue(rowData, col.value, col.channel);
1076
+ const template = rowData.template || "";
1077
+ const cellType = getCellType(col.value, col.channel, allowedValues, template);
1078
+ if (cellType === "image") {
1079
+ return /* @__PURE__ */ jsx13(ImageCell, { src: rawValue });
1080
+ }
1081
+ if (col.value === "Style Code") {
1082
+ const count = rowData._variantCount;
1083
+ return /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1.5", children: [
1084
+ /* @__PURE__ */ jsx13("span", { className: "truncate text-sm", children: rawValue || "\u2014" }),
1085
+ count != null && count > 1 && /* @__PURE__ */ jsxs5(Badge, { variant: "secondary", className: "shrink-0 px-1.5 py-0 text-[10px] font-normal", children: [
1086
+ count,
1087
+ " SKUs"
1088
+ ] })
1089
+ ] });
1090
+ }
1091
+ const dropdownVals = getDropdownValues(
1092
+ allowedValues,
1093
+ template,
1094
+ col.value,
1095
+ col.channel
1096
+ );
1097
+ return /* @__PURE__ */ jsx13(
1098
+ EditableCell,
1099
+ {
1100
+ value: rawValue,
1101
+ cellType,
1102
+ fieldName: col.value,
1103
+ allowedValues: dropdownVals,
1104
+ onSave: async (newValue) => {
1105
+ await onCellEdit(rowData, col.value, col.channel, newValue);
1106
+ }
1107
+ }
1108
+ );
1109
+ },
1110
+ size: isImageColumn(col) ? 80 : 180,
1111
+ enableHiding: true
1112
+ })
1113
+ );
1114
+ return [selectCol, ...dataCols];
1115
+ }, [appColumns, allowedValues, onCellEdit]);
1116
+ const filteredProducts = useMemo(() => {
1117
+ if (!globalFilter.trim()) return products;
1118
+ const q = globalFilter.toLowerCase();
1119
+ return products.filter((row) => {
1120
+ for (const col of appColumns) {
1121
+ const val = getCellValue(row, col.value, col.channel);
1122
+ if (val && String(val).toLowerCase().includes(q)) return true;
1123
+ }
1124
+ return false;
1125
+ });
1126
+ }, [products, globalFilter, appColumns]);
1127
+ const table = useReactTable({
1128
+ data: filteredProducts,
1129
+ columns: tanstackColumns,
1130
+ state: {
1131
+ columnVisibility,
1132
+ rowSelection
1133
+ },
1134
+ onColumnVisibilityChange,
1135
+ onRowSelectionChange,
1136
+ getCoreRowModel: getCoreRowModel(),
1137
+ getRowId: (row) => row.id,
1138
+ enableRowSelection: true
1139
+ });
1140
+ const scrollRef = useRef3(null);
1141
+ const [canScrollRight, setCanScrollRight] = useState3(false);
1142
+ const [canScrollDown, setCanScrollDown] = useState3(false);
1143
+ const updateScrollFade = useCallback3(() => {
1144
+ const el = scrollRef.current;
1145
+ if (!el) return;
1146
+ const THRESHOLD = 4;
1147
+ setCanScrollRight(el.scrollWidth - el.scrollLeft - el.clientWidth > THRESHOLD);
1148
+ setCanScrollDown(el.scrollHeight - el.scrollTop - el.clientHeight > THRESHOLD);
1149
+ }, []);
1150
+ useEffect2(() => {
1151
+ const el = scrollRef.current;
1152
+ if (!el) return;
1153
+ updateScrollFade();
1154
+ el.addEventListener("scroll", updateScrollFade, { passive: true });
1155
+ const ro = new ResizeObserver(updateScrollFade);
1156
+ ro.observe(el);
1157
+ return () => {
1158
+ el.removeEventListener("scroll", updateScrollFade);
1159
+ ro.disconnect();
1160
+ };
1161
+ }, [updateScrollFade, filteredProducts]);
1162
+ if (loading) {
1163
+ return /* @__PURE__ */ jsx13("div", { className: "space-y-3 p-4", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx13(Skeleton, { className: "h-14 w-full rounded-md" }, i)) });
1164
+ }
1165
+ return /* @__PURE__ */ jsxs5("div", { className: "flex h-full flex-col", children: [
1166
+ /* @__PURE__ */ jsxs5("div", { className: "relative flex-1 overflow-hidden rounded-md border", children: [
1167
+ /* @__PURE__ */ jsx13(
1168
+ "div",
1169
+ {
1170
+ ref: scrollRef,
1171
+ className: "h-full overflow-auto",
1172
+ children: /* @__PURE__ */ jsxs5(Table, { children: [
1173
+ /* @__PURE__ */ jsx13(TableHeader, { className: "sticky top-0 z-10 bg-background", children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx13(TableRow, { children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx13(
1174
+ TableHead,
1175
+ {
1176
+ style: { width: header.getSize() },
1177
+ className: header.id === "_select" ? "sticky left-0 z-20 bg-background" : "",
1178
+ children: header.isPlaceholder ? null : flexRender(
1179
+ header.column.columnDef.header,
1180
+ header.getContext()
1181
+ )
1182
+ },
1183
+ header.id
1184
+ )) }, headerGroup.id)) }),
1185
+ /* @__PURE__ */ jsx13(TableBody, { children: table.getRowModel().rows.length > 0 ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx13(
1186
+ TableRow,
1187
+ {
1188
+ "data-state": row.getIsSelected() ? "selected" : void 0,
1189
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx13(
1190
+ TableCell,
1191
+ {
1192
+ style: { width: cell.column.getSize() },
1193
+ className: cell.column.id === "_select" ? "sticky left-0 z-10 bg-background" : "",
1194
+ children: flexRender(
1195
+ cell.column.columnDef.cell,
1196
+ cell.getContext()
1197
+ )
1198
+ },
1199
+ cell.id
1200
+ ))
1201
+ },
1202
+ row.id
1203
+ )) : /* @__PURE__ */ jsx13(TableRow, { children: /* @__PURE__ */ jsx13(
1204
+ TableCell,
1205
+ {
1206
+ colSpan: tanstackColumns.length,
1207
+ className: "h-32 text-center text-muted-foreground",
1208
+ children: globalFilter ? "No products match your search." : "No products found."
1209
+ }
1210
+ ) }) })
1211
+ ] })
1212
+ }
1213
+ ),
1214
+ /* @__PURE__ */ jsx13(
1215
+ "div",
1216
+ {
1217
+ className: `pointer-events-none absolute right-0 top-0 z-20 h-full w-10 transition-opacity duration-200 ${canScrollRight ? "opacity-100" : "opacity-0"}`,
1218
+ style: {
1219
+ background: "linear-gradient(to left, rgba(0,0,0,0.08) 0%, rgba(0,0,0,0.03) 40%, transparent 100%)"
1220
+ }
1221
+ }
1222
+ ),
1223
+ /* @__PURE__ */ jsx13(
1224
+ "div",
1225
+ {
1226
+ className: `pointer-events-none absolute bottom-0 left-0 z-20 h-10 w-full transition-opacity duration-200 ${canScrollDown ? "opacity-100" : "opacity-0"}`,
1227
+ style: {
1228
+ background: "linear-gradient(to top, rgba(0,0,0,0.08) 0%, rgba(0,0,0,0.03) 40%, transparent 100%)"
1229
+ }
1230
+ }
1231
+ )
1232
+ ] }),
1233
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between border-t px-3 py-2", children: [
1234
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
1235
+ totalProducts > 0 && /* @__PURE__ */ jsxs5("span", { className: "flex items-center gap-1", children: [
1236
+ /* @__PURE__ */ jsx13(Package, { className: "size-3.5" }),
1237
+ totalProducts.toLocaleString(),
1238
+ " ",
1239
+ totalProducts === 1 ? "style" : "styles"
28
1240
  ] }),
29
- /* @__PURE__ */ jsxs("p", { style: { fontSize: "12px", color: "#999", marginTop: "4px" }, children: [
30
- "This is a placeholder from ",
31
- /* @__PURE__ */ jsx("code", { children: "@streamoid/catalogix-chat" }),
32
- ". Replace with actual catalogix components (CreateStore, SelectProducts, Automations) by importing from the catalogix app source tree."
1241
+ totalSkus > 0 && /* @__PURE__ */ jsxs5("span", { className: "flex items-center gap-1", children: [
1242
+ /* @__PURE__ */ jsx13(Layers, { className: "size-3.5" }),
1243
+ totalSkus.toLocaleString(),
1244
+ " ",
1245
+ totalSkus === 1 ? "SKU" : "SKUs"
1246
+ ] }),
1247
+ table.getFilteredSelectedRowModel().rows.length > 0 && /* @__PURE__ */ jsxs5("span", { className: "font-medium text-foreground", children: [
1248
+ "\xB7 ",
1249
+ table.getFilteredSelectedRowModel().rows.length,
1250
+ " selected"
33
1251
  ] })
1252
+ ] }),
1253
+ totalPages > 0 && /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
1254
+ /* @__PURE__ */ jsxs5(
1255
+ Button,
1256
+ {
1257
+ variant: "outline",
1258
+ size: "sm",
1259
+ onClick: () => onPageChange(currentPage - 1),
1260
+ disabled: currentPage <= 1,
1261
+ className: "h-7 px-2",
1262
+ children: [
1263
+ /* @__PURE__ */ jsx13(ChevronLeft, { className: "size-4" }),
1264
+ "Prev"
1265
+ ]
1266
+ }
1267
+ ),
1268
+ /* @__PURE__ */ jsxs5("span", { className: "min-w-[5rem] text-center text-xs text-muted-foreground", children: [
1269
+ "Page ",
1270
+ currentPage,
1271
+ " of ",
1272
+ totalPages
1273
+ ] }),
1274
+ /* @__PURE__ */ jsxs5(
1275
+ Button,
1276
+ {
1277
+ variant: "outline",
1278
+ size: "sm",
1279
+ onClick: () => onPageChange(currentPage + 1),
1280
+ disabled: currentPage >= totalPages,
1281
+ className: "h-7 px-2",
1282
+ children: [
1283
+ "Next",
1284
+ /* @__PURE__ */ jsx13(ChevronRight, { className: "size-4" })
1285
+ ]
1286
+ }
1287
+ )
1288
+ ] })
1289
+ ] })
1290
+ ] });
1291
+ }
1292
+ function isImageColumn(col) {
1293
+ const lower = col.value.toLowerCase();
1294
+ return lower.includes("image") && !lower.includes("image_count");
1295
+ }
1296
+
1297
+ // src/SelectProducts/ColumnManager.tsx
1298
+ import { useMemo as useMemo2, useState as useState4 } from "react";
1299
+ import { Columns, Loader2 as Loader22, Search } from "lucide-react";
1300
+
1301
+ // src/ui/switch.tsx
1302
+ import * as SwitchPrimitive from "@radix-ui/react-switch";
1303
+ import { jsx as jsx14 } from "react/jsx-runtime";
1304
+ function Switch({
1305
+ className,
1306
+ ...props
1307
+ }) {
1308
+ return /* @__PURE__ */ jsx14(
1309
+ SwitchPrimitive.Root,
1310
+ {
1311
+ "data-slot": "switch",
1312
+ className: cn(
1313
+ "peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-switch-background focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
1314
+ className
1315
+ ),
1316
+ ...props,
1317
+ children: /* @__PURE__ */ jsx14(
1318
+ SwitchPrimitive.Thumb,
1319
+ {
1320
+ "data-slot": "switch-thumb",
1321
+ className: cn(
1322
+ "bg-card dark:data-[state=unchecked]:bg-card-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
1323
+ )
1324
+ }
1325
+ )
1326
+ }
1327
+ );
1328
+ }
1329
+
1330
+ // src/SelectProducts/ColumnManager.tsx
1331
+ import { jsx as jsx15, jsxs as jsxs6 } from "react/jsx-runtime";
1332
+ function ColumnManager({
1333
+ allColumns,
1334
+ columnVisibility,
1335
+ onToggle,
1336
+ loading
1337
+ }) {
1338
+ const [search, setSearch] = useState4("");
1339
+ const [open, setOpen] = useState4(false);
1340
+ const filtered = useMemo2(() => {
1341
+ if (!search.trim()) return allColumns;
1342
+ const q = search.toLowerCase();
1343
+ return allColumns.filter(
1344
+ (col) => col.name.toLowerCase().includes(q) || col.value.toLowerCase().includes(q) || col.channel.toLowerCase().includes(q)
1345
+ );
1346
+ }, [allColumns, search]);
1347
+ const grouped = useMemo2(() => {
1348
+ const map = {};
1349
+ for (const col of filtered) {
1350
+ const ch = col.channel || "other";
1351
+ if (!map[ch]) map[ch] = [];
1352
+ map[ch].push(col);
1353
+ }
1354
+ return map;
1355
+ }, [filtered]);
1356
+ const visibleCount = useMemo2(
1357
+ () => allColumns.filter((col) => columnVisibility[col.value] !== false).length,
1358
+ [allColumns, columnVisibility]
1359
+ );
1360
+ return /* @__PURE__ */ jsxs6(Popover, { open, onOpenChange: setOpen, children: [
1361
+ /* @__PURE__ */ jsxs6(
1362
+ PopoverTrigger,
1363
+ {
1364
+ className: buttonVariants({
1365
+ variant: "outline",
1366
+ size: "sm",
1367
+ className: "gap-1.5"
1368
+ }),
1369
+ children: [
1370
+ /* @__PURE__ */ jsx15(Columns, { className: "size-4" }),
1371
+ "Columns",
1372
+ loading ? /* @__PURE__ */ jsx15(Loader22, { className: "ml-0.5 size-3.5 animate-spin text-muted-foreground" }) : /* @__PURE__ */ jsxs6(Badge, { variant: "secondary", className: "ml-0.5 text-xs font-normal", children: [
1373
+ visibleCount,
1374
+ "/",
1375
+ allColumns.length
1376
+ ] })
1377
+ ]
1378
+ }
1379
+ ),
1380
+ /* @__PURE__ */ jsxs6(
1381
+ PopoverContent,
1382
+ {
1383
+ className: "w-[300px] p-0",
1384
+ align: "end",
1385
+ sideOffset: 4,
1386
+ collisionPadding: 8,
1387
+ children: [
1388
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 border-b px-3 py-2", children: [
1389
+ /* @__PURE__ */ jsx15(Search, { className: "size-3.5 shrink-0 text-muted-foreground" }),
1390
+ /* @__PURE__ */ jsx15(
1391
+ "input",
1392
+ {
1393
+ placeholder: "Search columns...",
1394
+ value: search,
1395
+ onChange: (e) => setSearch(e.target.value),
1396
+ className: "h-6 w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground"
1397
+ }
1398
+ )
1399
+ ] }),
1400
+ /* @__PURE__ */ jsxs6("div", { className: "max-h-[360px] overflow-y-auto overscroll-contain p-2", children: [
1401
+ Object.entries(grouped).map(([channel, cols]) => /* @__PURE__ */ jsxs6("div", { className: "mb-2 last:mb-0", children: [
1402
+ /* @__PURE__ */ jsxs6("div", { className: "sticky top-0 z-10 mb-1 bg-popover px-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: [
1403
+ channel,
1404
+ /* @__PURE__ */ jsxs6("span", { className: "ml-1 font-normal", children: [
1405
+ "(",
1406
+ cols.length,
1407
+ ")"
1408
+ ] })
1409
+ ] }),
1410
+ cols.map((col) => {
1411
+ const isVisible = columnVisibility[col.value] !== false;
1412
+ return /* @__PURE__ */ jsxs6(
1413
+ "label",
1414
+ {
1415
+ className: "flex cursor-pointer items-center justify-between gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent",
1416
+ children: [
1417
+ /* @__PURE__ */ jsx15("span", { className: "min-w-0 truncate", children: col.name }),
1418
+ /* @__PURE__ */ jsx15(
1419
+ Switch,
1420
+ {
1421
+ checked: isVisible,
1422
+ onCheckedChange: () => onToggle(col.value),
1423
+ className: "shrink-0 scale-75"
1424
+ }
1425
+ )
1426
+ ]
1427
+ },
1428
+ col.value
1429
+ );
1430
+ })
1431
+ ] }, channel)),
1432
+ filtered.length === 0 && /* @__PURE__ */ jsx15("p", { className: "py-4 text-center text-sm text-muted-foreground", children: "No columns match your search." })
1433
+ ] })
1434
+ ]
1435
+ }
1436
+ )
1437
+ ] });
1438
+ }
1439
+
1440
+ // src/SelectProducts/api.ts
1441
+ async function fetchRequiredFields(storeId) {
1442
+ const { catalogixBaseUrl } = getApiConfig();
1443
+ const url = `${catalogixBaseUrl}/api/v1/store/${storeId}/product/required`;
1444
+ return fetchJson(url);
1445
+ }
1446
+ async function searchProducts(params) {
1447
+ const { catalogixBaseUrl } = getApiConfig();
1448
+ const url = `${catalogixBaseUrl}/api/v1/store/${params.storeId}/products/search?response_as_list=true&include_result_count=true`;
1449
+ return fetchJson(url, {
1450
+ method: "POST",
1451
+ headers: { "Content-Type": "application/json" },
1452
+ body: JSON.stringify({
1453
+ filters: params.filters,
1454
+ fields: params.fields,
1455
+ start: params.start,
1456
+ rows: params.rows,
1457
+ group: true
1458
+ })
1459
+ });
1460
+ }
1461
+ async function updateProduct({
1462
+ storeId,
1463
+ productCode,
1464
+ productUuid,
1465
+ channel,
1466
+ fieldName,
1467
+ value,
1468
+ userId
1469
+ }) {
1470
+ const { keplerProxyPrefix } = getApiConfig();
1471
+ const metadata = encodeURIComponent(
1472
+ JSON.stringify({ catalogix_user_id: userId })
1473
+ );
1474
+ const url = `${keplerProxyPrefix}/v1/store/${storeId}/products/update?req_metadata=${metadata}`;
1475
+ const body = {
1476
+ [productCode]: {
1477
+ [channel]: {
1478
+ product_uuid: productUuid,
1479
+ [fieldName]: value
1480
+ }
1481
+ }
1482
+ };
1483
+ return fetchJson(url, {
1484
+ method: "POST",
1485
+ headers: { "Content-Type": "application/json" },
1486
+ body: JSON.stringify(body)
1487
+ });
1488
+ }
1489
+
1490
+ // src/SelectProducts/index.tsx
1491
+ import { Fragment as Fragment2, jsx as jsx16, jsxs as jsxs7 } from "react/jsx-runtime";
1492
+ function SelectProducts({
1493
+ workspaceId,
1494
+ storeId,
1495
+ userId,
1496
+ submitted,
1497
+ submittedValues,
1498
+ onConfirmSelection,
1499
+ useProductUuid = true
1500
+ }) {
1501
+ const [loading, setLoading] = useState5(false);
1502
+ const [products, setProducts] = useState5([]);
1503
+ const [allColumns, setAllColumns] = useState5(DEFAULT_COLUMNS);
1504
+ const [allowedValues, setAllowedValues] = useState5({});
1505
+ const [pageCount, setPageCount] = useState5(0);
1506
+ const [currentPage, setCurrentPage] = useState5(1);
1507
+ const [totalProducts, setTotalProducts] = useState5(0);
1508
+ const [totalSkus, setTotalSkus] = useState5(0);
1509
+ const [globalFilter, setGlobalFilter] = useState5("");
1510
+ const [columnVisibility, setColumnVisibility] = useState5({});
1511
+ const [rowSelection, setRowSelection] = useState5({});
1512
+ const [columnsReady, setColumnsReady] = useState5(false);
1513
+ const [isSubmitting, setIsSubmitting] = useState5(false);
1514
+ const [isSubmitted, setIsSubmitted] = useState5(Boolean(submitted));
1515
+ const [localSubmittedValues, setLocalSubmittedValues] = useState5(
1516
+ submittedValues ?? null
1517
+ );
1518
+ const rowSelectionRef = useRef4({});
1519
+ useEffect3(() => {
1520
+ rowSelectionRef.current = rowSelection;
1521
+ }, [rowSelection]);
1522
+ const requiredFieldsFetchedRef = useRef4(null);
1523
+ useEffect3(() => {
1524
+ setIsSubmitted(Boolean(submitted));
1525
+ setLocalSubmittedValues(submittedValues ?? null);
1526
+ }, [submitted, submittedValues]);
1527
+ useEffect3(() => {
1528
+ if (!storeId || requiredFieldsFetchedRef.current === storeId) return;
1529
+ requiredFieldsFetchedRef.current = storeId;
1530
+ (async () => {
1531
+ try {
1532
+ const response = await fetchRequiredFields(storeId);
1533
+ if (response?.status?.code !== 0 || !response.data) return;
1534
+ const { columns: reqCols, allowedValues: reqAv } = buildFromRequiredFields(response.data);
1535
+ const existingValues = new Set(DEFAULT_COLUMNS.map((c) => c.value));
1536
+ const newCols = reqCols.filter((c) => !existingValues.has(c.value));
1537
+ if (newCols.length > 0) {
1538
+ setAllColumns((prev) => {
1539
+ const prevValues = new Set(prev.map((c) => c.value));
1540
+ const toAdd = newCols.filter((c) => !prevValues.has(c.value));
1541
+ return toAdd.length > 0 ? [...prev, ...toAdd] : prev;
1542
+ });
1543
+ setColumnVisibility((prev) => {
1544
+ const next = { ...prev };
1545
+ for (const col of newCols) {
1546
+ if (!CORE_COLUMN_VALUES.has(col.value) && next[col.value] === void 0) {
1547
+ next[col.value] = false;
1548
+ }
1549
+ }
1550
+ return next;
1551
+ });
1552
+ }
1553
+ setAllowedValues((prev) => mergeAllowedValues(prev, reqAv));
1554
+ } catch (error) {
1555
+ console.error("Failed to fetch required fields:", error);
1556
+ } finally {
1557
+ setColumnsReady(true);
1558
+ }
1559
+ })();
1560
+ }, [storeId]);
1561
+ const fetchProducts = useCallback4(
1562
+ async (page = 1) => {
1563
+ if (!storeId) return;
1564
+ const fields = buildFieldsFromColumns(allColumns, DEFAULT_SOURCE_MP);
1565
+ const filters = buildFiltersFromFields(fields);
1566
+ const offset = (page - 1) * PRODUCTS_PER_PAGE;
1567
+ setLoading(true);
1568
+ try {
1569
+ const response = await searchProducts({
1570
+ storeId,
1571
+ fields,
1572
+ filters,
1573
+ start: offset,
1574
+ rows: PRODUCTS_PER_PAGE
1575
+ });
1576
+ if (response?.status?.code === 0) {
1577
+ const groups = Array.isArray(response.data) ? response.data : [];
1578
+ const allRows = formatProducts(groups);
1579
+ setProducts(formatProductsByStyle(allRows));
1580
+ const av = response.allowed_values || response.allowedValues;
1581
+ if (av) {
1582
+ setAllowedValues((prev) => mergeAllowedValues(prev, av));
1583
+ }
1584
+ const stats = response.stats || {};
1585
+ const docCount = Math.max(stats.doc_count ?? 0, 0);
1586
+ const variantCount = Math.max(stats.variant_count ?? 0, 0);
1587
+ const apiPages = Math.max(stats.pages ?? 0, 0);
1588
+ setTotalProducts(docCount);
1589
+ setTotalSkus(variantCount);
1590
+ setPageCount(
1591
+ apiPages > 0 ? apiPages : docCount > 0 ? Math.ceil(docCount / PRODUCTS_PER_PAGE) : 0
1592
+ );
1593
+ } else {
1594
+ setProducts([]);
1595
+ }
1596
+ } catch (error) {
1597
+ console.error("Failed to fetch products:", error);
1598
+ setProducts([]);
1599
+ } finally {
1600
+ setLoading(false);
1601
+ }
1602
+ },
1603
+ [storeId, allColumns]
1604
+ );
1605
+ useEffect3(() => {
1606
+ if (storeId) {
1607
+ fetchProducts(currentPage);
1608
+ }
1609
+ }, [storeId, currentPage, fetchProducts]);
1610
+ const handleCellEdit = useCallback4(
1611
+ async (row, fieldName, channel, newValue) => {
1612
+ const apiChannel = channel === "required" ? "product" : channel;
1613
+ const productCode = row.product_code;
1614
+ const productUuid = row.product_uuid;
1615
+ if (!productCode || !productUuid) {
1616
+ throw new Error("Missing product code or UUID");
1617
+ }
1618
+ await updateProduct({
1619
+ storeId,
1620
+ productCode,
1621
+ productUuid,
1622
+ channel: apiChannel,
1623
+ fieldName,
1624
+ value: newValue,
1625
+ userId: userId || ""
1626
+ });
1627
+ setProducts(
1628
+ (prev) => prev.map((p) => {
1629
+ if (p.id !== row.id) return p;
1630
+ const namespacedKey = `${fieldName}::${apiChannel}`;
1631
+ return { ...p, [namespacedKey]: newValue, ...fieldName === "status" ? { status: newValue } : {} };
1632
+ })
1633
+ );
1634
+ },
1635
+ [storeId, userId]
1636
+ );
1637
+ const handlePageChange = useCallback4((page) => {
1638
+ setCurrentPage(page);
1639
+ setRowSelection({});
1640
+ }, []);
1641
+ const selectedProducts = useMemo3(() => {
1642
+ const selectedIds = Object.keys(rowSelection).filter(
1643
+ (k) => rowSelection[k]
1644
+ );
1645
+ return products.filter((p) => selectedIds.includes(p.id));
1646
+ }, [rowSelection, products]);
1647
+ const hasSelection = selectedProducts.length > 0;
1648
+ const handleConfirmSelection = useCallback4(async () => {
1649
+ if (!onConfirmSelection) return;
1650
+ const productUuids = Array.from(
1651
+ new Set(selectedProducts.map((p) => p.product_uuid).filter(Boolean))
1652
+ );
1653
+ const productCodes = Array.from(
1654
+ new Set(selectedProducts.map((p) => p.product_code).filter(Boolean))
1655
+ );
1656
+ const selectionPayload = useProductUuid ? { product_uuids: productUuids } : { product_codes: productCodes };
1657
+ const resumeValues = {
1658
+ product_uuids: productUuids,
1659
+ product_codes: productCodes,
1660
+ workspaceId,
1661
+ storeId
1662
+ };
1663
+ setIsSubmitting(true);
1664
+ try {
1665
+ const result = await onConfirmSelection(
1666
+ selectedProducts,
1667
+ [],
1668
+ // excludedProducts
1669
+ [],
1670
+ // selectedPageRange
1671
+ {},
1672
+ // filters
1673
+ useProductUuid,
1674
+ selectionPayload,
1675
+ resumeValues
1676
+ );
1677
+ if (result?.submitted) {
1678
+ setIsSubmitted(true);
1679
+ setLocalSubmittedValues(result.submittedValues ?? resumeValues);
1680
+ }
1681
+ } catch (error) {
1682
+ console.error("Failed to submit selection:", error);
1683
+ } finally {
1684
+ setIsSubmitting(false);
1685
+ }
1686
+ }, [
1687
+ onConfirmSelection,
1688
+ selectedProducts,
1689
+ useProductUuid,
1690
+ workspaceId,
1691
+ storeId
1692
+ ]);
1693
+ const handleColumnToggle = useCallback4((columnValue) => {
1694
+ setColumnVisibility((prev) => ({
1695
+ ...prev,
1696
+ [columnValue]: prev[columnValue] === false ? true : false
1697
+ }));
1698
+ }, []);
1699
+ if (isSubmitted) {
1700
+ return /* @__PURE__ */ jsxs7(Card, { className: "p-4", children: [
1701
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 font-semibold text-emerald-700", children: [
1702
+ /* @__PURE__ */ jsx16(CheckCircle, { className: "size-4" }),
1703
+ "Selection submitted"
1704
+ ] }),
1705
+ localSubmittedValues && /* @__PURE__ */ jsxs7("details", { className: "mt-2", children: [
1706
+ /* @__PURE__ */ jsx16("summary", { className: "cursor-pointer text-sm text-muted-foreground", children: "View submitted data" }),
1707
+ /* @__PURE__ */ jsx16("pre", { className: "mt-2 max-h-40 overflow-auto rounded bg-muted p-2 text-xs", children: JSON.stringify(localSubmittedValues, null, 2) })
1708
+ ] })
1709
+ ] });
1710
+ }
1711
+ if (!storeId || !workspaceId) {
1712
+ return /* @__PURE__ */ jsx16(Card, { className: "p-6 text-center text-muted-foreground", children: "Store context missing. Please start from a store selection step." });
1713
+ }
1714
+ if (!loading && products.length === 0) {
1715
+ return /* @__PURE__ */ jsx16(Card, { className: "p-6 text-center text-muted-foreground", children: "No products found for this store." });
1716
+ }
1717
+ return /* @__PURE__ */ jsxs7("div", { className: "flex h-[580px] flex-col gap-3 rounded-lg border border-border bg-card p-3 shadow-sm", children: [
1718
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
1719
+ /* @__PURE__ */ jsxs7("div", { className: "relative flex-1", children: [
1720
+ /* @__PURE__ */ jsx16(Search2, { className: "absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }),
1721
+ /* @__PURE__ */ jsx16(
1722
+ Input,
1723
+ {
1724
+ placeholder: "Search products...",
1725
+ value: globalFilter,
1726
+ onChange: (e) => setGlobalFilter(e.target.value),
1727
+ className: "h-9 pl-9"
1728
+ }
1729
+ )
1730
+ ] }),
1731
+ /* @__PURE__ */ jsx16(
1732
+ ColumnManager,
1733
+ {
1734
+ allColumns,
1735
+ columnVisibility,
1736
+ onToggle: handleColumnToggle,
1737
+ loading: !columnsReady
1738
+ }
1739
+ )
1740
+ ] }),
1741
+ /* @__PURE__ */ jsx16("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx16(
1742
+ ProductTable,
1743
+ {
1744
+ products,
1745
+ columns: allColumns,
1746
+ allowedValues,
1747
+ loading,
1748
+ columnVisibility,
1749
+ onColumnVisibilityChange: setColumnVisibility,
1750
+ rowSelection,
1751
+ onRowSelectionChange: setRowSelection,
1752
+ onCellEdit: handleCellEdit,
1753
+ currentPage,
1754
+ totalPages: pageCount,
1755
+ totalProducts,
1756
+ totalSkus,
1757
+ onPageChange: handlePageChange,
1758
+ globalFilter
1759
+ }
1760
+ ) }),
1761
+ hasSelection && /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between rounded-lg border bg-muted/50 px-4 py-2", children: [
1762
+ /* @__PURE__ */ jsxs7("span", { className: "text-sm font-medium", children: [
1763
+ selectedProducts.length,
1764
+ " product",
1765
+ selectedProducts.length !== 1 ? "s" : "",
1766
+ " selected"
1767
+ ] }),
1768
+ /* @__PURE__ */ jsx16(
1769
+ Button,
1770
+ {
1771
+ onClick: handleConfirmSelection,
1772
+ disabled: isSubmitting,
1773
+ size: "sm",
1774
+ children: isSubmitting ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
1775
+ /* @__PURE__ */ jsx16(Loader23, { className: "size-4 animate-spin" }),
1776
+ "Submitting..."
1777
+ ] }) : "Confirm selection"
1778
+ }
1779
+ )
1780
+ ] })
1781
+ ] });
1782
+ }
1783
+ function mergeAllowedValues(base, incoming) {
1784
+ const result = { ...base };
1785
+ for (const [channel, channelData] of Object.entries(incoming)) {
1786
+ if (!result[channel]) {
1787
+ result[channel] = channelData;
1788
+ } else {
1789
+ result[channel] = { ...result[channel] };
1790
+ for (const [key, value] of Object.entries(channelData)) {
1791
+ if (!result[channel][key]) {
1792
+ result[channel][key] = value;
1793
+ } else {
1794
+ result[channel][key] = { ...result[channel][key], ...value };
1795
+ }
1796
+ }
1797
+ }
1798
+ }
1799
+ return result;
1800
+ }
1801
+
1802
+ // src/CreateStore/index.tsx
1803
+ import { useState as useState6, useEffect as useEffect4, useMemo as useMemo4 } from "react";
1804
+ import { Loader2 as Loader24, ChevronDown, ArrowLeft, Info } from "lucide-react";
1805
+
1806
+ // src/ui/label.tsx
1807
+ import * as LabelPrimitive from "@radix-ui/react-label";
1808
+ import { jsx as jsx17 } from "react/jsx-runtime";
1809
+ function Label({
1810
+ className,
1811
+ ...props
1812
+ }) {
1813
+ return /* @__PURE__ */ jsx17(
1814
+ LabelPrimitive.Root,
1815
+ {
1816
+ "data-slot": "label",
1817
+ className: cn(
1818
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
1819
+ className
1820
+ ),
1821
+ ...props
1822
+ }
1823
+ );
1824
+ }
1825
+
1826
+ // src/ui/select.tsx
1827
+ import * as SelectPrimitive from "@radix-ui/react-select";
1828
+ import {
1829
+ CheckIcon as CheckIcon2,
1830
+ ChevronDownIcon,
1831
+ ChevronUpIcon
1832
+ } from "lucide-react";
1833
+ import { jsx as jsx18, jsxs as jsxs8 } from "react/jsx-runtime";
1834
+ function Select({
1835
+ ...props
1836
+ }) {
1837
+ return /* @__PURE__ */ jsx18(SelectPrimitive.Root, { "data-slot": "select", ...props });
1838
+ }
1839
+ function SelectValue({
1840
+ ...props
1841
+ }) {
1842
+ return /* @__PURE__ */ jsx18(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
1843
+ }
1844
+ function SelectTrigger({
1845
+ className,
1846
+ size = "default",
1847
+ children,
1848
+ ...props
1849
+ }) {
1850
+ return /* @__PURE__ */ jsxs8(
1851
+ SelectPrimitive.Trigger,
1852
+ {
1853
+ "data-slot": "select-trigger",
1854
+ "data-size": size,
1855
+ className: cn(
1856
+ "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-full items-center justify-between gap-2 rounded-md border bg-input-background px-3 py-2 text-sm whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1857
+ className
1858
+ ),
1859
+ ...props,
1860
+ children: [
1861
+ children,
1862
+ /* @__PURE__ */ jsx18(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx18(ChevronDownIcon, { className: "size-4 opacity-50" }) })
1863
+ ]
1864
+ }
1865
+ );
1866
+ }
1867
+ function SelectContent({
1868
+ className,
1869
+ children,
1870
+ position = "popper",
1871
+ ...props
1872
+ }) {
1873
+ return /* @__PURE__ */ jsx18(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs8(
1874
+ SelectPrimitive.Content,
1875
+ {
1876
+ "data-slot": "select-content",
1877
+ className: cn(
1878
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
1879
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
1880
+ className
1881
+ ),
1882
+ position,
1883
+ ...props,
1884
+ children: [
1885
+ /* @__PURE__ */ jsx18(SelectScrollUpButton, {}),
1886
+ /* @__PURE__ */ jsx18(
1887
+ SelectPrimitive.Viewport,
1888
+ {
1889
+ className: cn(
1890
+ "p-1",
1891
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
1892
+ ),
1893
+ children
1894
+ }
1895
+ ),
1896
+ /* @__PURE__ */ jsx18(SelectScrollDownButton, {})
1897
+ ]
1898
+ }
1899
+ ) });
1900
+ }
1901
+ function SelectItem({
1902
+ className,
1903
+ children,
1904
+ ...props
1905
+ }) {
1906
+ return /* @__PURE__ */ jsxs8(
1907
+ SelectPrimitive.Item,
1908
+ {
1909
+ "data-slot": "select-item",
1910
+ className: cn(
1911
+ "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
1912
+ className
1913
+ ),
1914
+ ...props,
1915
+ children: [
1916
+ /* @__PURE__ */ jsx18("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx18(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx18(CheckIcon2, { className: "size-4" }) }) }),
1917
+ /* @__PURE__ */ jsx18(SelectPrimitive.ItemText, { children })
34
1918
  ]
35
1919
  }
36
1920
  );
37
1921
  }
1922
+ function SelectScrollUpButton({
1923
+ className,
1924
+ ...props
1925
+ }) {
1926
+ return /* @__PURE__ */ jsx18(
1927
+ SelectPrimitive.ScrollUpButton,
1928
+ {
1929
+ "data-slot": "select-scroll-up-button",
1930
+ className: cn(
1931
+ "flex cursor-default items-center justify-center py-1",
1932
+ className
1933
+ ),
1934
+ ...props,
1935
+ children: /* @__PURE__ */ jsx18(ChevronUpIcon, { className: "size-4" })
1936
+ }
1937
+ );
1938
+ }
1939
+ function SelectScrollDownButton({
1940
+ className,
1941
+ ...props
1942
+ }) {
1943
+ return /* @__PURE__ */ jsx18(
1944
+ SelectPrimitive.ScrollDownButton,
1945
+ {
1946
+ "data-slot": "select-scroll-down-button",
1947
+ className: cn(
1948
+ "flex cursor-default items-center justify-center py-1",
1949
+ className
1950
+ ),
1951
+ ...props,
1952
+ children: /* @__PURE__ */ jsx18(ChevronDownIcon, { className: "size-4" })
1953
+ }
1954
+ );
1955
+ }
1956
+
1957
+ // src/ui/separator.tsx
1958
+ import * as SeparatorPrimitive from "@radix-ui/react-separator";
1959
+ import { jsx as jsx19 } from "react/jsx-runtime";
1960
+ function Separator2({
1961
+ className,
1962
+ orientation = "horizontal",
1963
+ decorative = true,
1964
+ ...props
1965
+ }) {
1966
+ return /* @__PURE__ */ jsx19(
1967
+ SeparatorPrimitive.Root,
1968
+ {
1969
+ "data-slot": "separator-root",
1970
+ decorative,
1971
+ orientation,
1972
+ className: cn(
1973
+ "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
1974
+ className
1975
+ ),
1976
+ ...props
1977
+ }
1978
+ );
1979
+ }
1980
+
1981
+ // src/CreateStore/api.ts
1982
+ async function getStoreValues() {
1983
+ const { catalogixBaseUrl } = getApiConfig();
1984
+ return fetchJson(`${catalogixBaseUrl}/api/v1/storeValues`);
1985
+ }
1986
+ async function getMarketplaces(region, workspaceId) {
1987
+ const { catalogixBaseUrl } = getApiConfig();
1988
+ return fetchJson(
1989
+ `${catalogixBaseUrl}/api/v1/marketplaces?region=${encodeURIComponent(region)}&workspace=${encodeURIComponent(workspaceId)}`
1990
+ );
1991
+ }
1992
+ async function createStore(payload) {
1993
+ const { catalogixBaseUrl } = getApiConfig();
1994
+ return fetchJson(`${catalogixBaseUrl}/api/v1/store`, {
1995
+ method: "POST",
1996
+ headers: { "Content-Type": "application/json" },
1997
+ body: JSON.stringify(payload)
1998
+ });
1999
+ }
2000
+ async function getStore(workspaceId, storeId) {
2001
+ const { catalogixBaseUrl } = getApiConfig();
2002
+ return fetchJson(
2003
+ `${catalogixBaseUrl}/api/v1/workspace/${encodeURIComponent(workspaceId)}/store/${encodeURIComponent(storeId)}`
2004
+ );
2005
+ }
2006
+
2007
+ // src/CreateStore/index.tsx
2008
+ import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs9 } from "react/jsx-runtime";
2009
+ var REGIONS = [
2010
+ "India",
2011
+ "Middle East (UAE)",
2012
+ "South East Asia",
2013
+ "North America",
2014
+ "Europe",
2015
+ "UK",
2016
+ "Australia",
2017
+ "Japan",
2018
+ "South America",
2019
+ "Africa"
2020
+ ];
2021
+ var WEBSITE_REGEX = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/;
2022
+ function CreateStore({
2023
+ workspaceId,
2024
+ stores = [],
2025
+ initialStoreName = "",
2026
+ useCatalogixTaxonomy = false,
2027
+ shouldRefetchStore = false,
2028
+ onStoreCreated,
2029
+ onCreateError,
2030
+ onClose
2031
+ }) {
2032
+ const [storeName, setStoreName] = useState6(initialStoreName);
2033
+ const [storeType, setStoreType] = useState6([]);
2034
+ const [country, setCountry] = useState6("India");
2035
+ const [channels, setChannels] = useState6([]);
2036
+ const [storeWebsite, setStoreWebsite] = useState6("");
2037
+ const [languages, setLanguages] = useState6([]);
2038
+ const [linkedStoreId, setLinkedStoreId] = useState6(null);
2039
+ const [showAdvanced, setShowAdvanced] = useState6(false);
2040
+ const [loading, setLoading] = useState6(false);
2041
+ const [fetchingValues, setFetchingValues] = useState6(true);
2042
+ const [fetchingChannels, setFetchingChannels] = useState6(false);
2043
+ const [error, setError] = useState6(null);
2044
+ const [storeTypes, setStoreTypes] = useState6([]);
2045
+ const [availableLanguages, setAvailableLanguages] = useState6([]);
2046
+ const [channelOptions, setChannelOptions] = useState6([]);
2047
+ const [showConnectInfo, setShowConnectInfo] = useState6(false);
2048
+ const [channelPopoverOpen, setChannelPopoverOpen] = useState6(false);
2049
+ const [languagePopoverOpen, setLanguagePopoverOpen] = useState6(false);
2050
+ useEffect4(() => {
2051
+ let active = true;
2052
+ getStoreValues().then((res) => {
2053
+ if (!active || !res.data) return;
2054
+ if (res.data.product_sale_type) {
2055
+ setStoreTypes(res.data.product_sale_type);
2056
+ const mp = res.data.product_sale_type.find((t) => t.id === "MP");
2057
+ if (mp) setStoreType(["MP"]);
2058
+ }
2059
+ if (res.data.language) {
2060
+ setAvailableLanguages(res.data.language);
2061
+ setLanguages([res.data.language[0]]);
2062
+ }
2063
+ }).catch(console.error).finally(() => active && setFetchingValues(false));
2064
+ return () => {
2065
+ active = false;
2066
+ };
2067
+ }, []);
2068
+ useEffect4(() => {
2069
+ if (!workspaceId || !country) return;
2070
+ let active = true;
2071
+ setFetchingChannels(true);
2072
+ getMarketplaces(country, workspaceId).then((res) => {
2073
+ if (!active) return;
2074
+ if (res.data && Array.isArray(res.data)) {
2075
+ setChannelOptions(
2076
+ res.data.map((ch) => ({
2077
+ value: ch,
2078
+ label: ch.replace(/_/g, " "),
2079
+ thumbnailUrl: res.details?.[ch.toLowerCase()]?.thumbnail_url
2080
+ })).sort((a, b) => a.label.localeCompare(b.label))
2081
+ );
2082
+ } else {
2083
+ setChannelOptions([]);
2084
+ }
2085
+ }).catch(() => setChannelOptions([])).finally(() => active && setFetchingChannels(false));
2086
+ return () => {
2087
+ active = false;
2088
+ };
2089
+ }, [country, workspaceId]);
2090
+ const hasMP = storeType.includes("MP");
2091
+ const hasD2C = storeType.includes("D2C");
2092
+ const isCreateDisabled = useMemo4(() => {
2093
+ if (!storeName.trim() || !country || storeType.length === 0) return true;
2094
+ if (hasMP && channels.length === 0) return true;
2095
+ if (hasD2C && !storeWebsite.trim()) return true;
2096
+ return false;
2097
+ }, [storeName, country, storeType, channels, storeWebsite, hasMP, hasD2C]);
2098
+ const handleCreate = async () => {
2099
+ if (hasD2C) {
2100
+ const trimmed = storeWebsite.trim();
2101
+ if (!trimmed || !WEBSITE_REGEX.test(trimmed)) {
2102
+ setError("Invalid store website. Use https://www.example.com");
2103
+ return;
2104
+ }
2105
+ }
2106
+ setLoading(true);
2107
+ setError(null);
2108
+ const channelList = channels.map((c) => c.value);
2109
+ if (hasD2C && !channelList.length) {
2110
+ channelList.push("SMP", "streamoid-4");
2111
+ }
2112
+ const payload = {
2113
+ storeName,
2114
+ storeType,
2115
+ country,
2116
+ channels: channelList,
2117
+ languages,
2118
+ storeWebsite,
2119
+ linkedStore: linkedStoreId ?? void 0,
2120
+ selectedWorkspaceId: workspaceId,
2121
+ ...useCatalogixTaxonomy ? { storeTaxonomy: "catalogix" } : {}
2122
+ };
2123
+ try {
2124
+ const res = await createStore(payload);
2125
+ if (res.status?.code === -1) {
2126
+ setError(res.status.message || "Store creation failed");
2127
+ onCreateError?.(res);
2128
+ } else {
2129
+ let finalData = res.data || {};
2130
+ if (shouldRefetchStore && res.data?._id) {
2131
+ const refetched = await getStore(res.data.workspaceId, res.data._id);
2132
+ finalData = { ...finalData, ...refetched.data };
2133
+ }
2134
+ onStoreCreated?.(finalData);
2135
+ onClose?.();
2136
+ }
2137
+ } catch (err) {
2138
+ const msg = err instanceof Error ? err.message : "Something went wrong";
2139
+ setError(msg);
2140
+ onCreateError?.(err);
2141
+ } finally {
2142
+ setLoading(false);
2143
+ }
2144
+ };
2145
+ const toggleChannel = (ch) => {
2146
+ setChannels(
2147
+ (prev) => prev.some((c) => c.value === ch.value) ? prev.filter((c) => c.value !== ch.value) : [...prev, ch]
2148
+ );
2149
+ };
2150
+ const toggleLanguage = (lang) => {
2151
+ setLanguages(
2152
+ (prev) => prev.includes(lang) ? prev.filter((l) => l !== lang) : [...prev, lang]
2153
+ );
2154
+ };
2155
+ if (fetchingValues || fetchingChannels) {
2156
+ return /* @__PURE__ */ jsxs9("div", { className: "space-y-4 p-4", children: [
2157
+ /* @__PURE__ */ jsx20(Skeleton, { className: "h-8 w-48" }),
2158
+ /* @__PURE__ */ jsx20(Skeleton, { className: "h-10 w-full" }),
2159
+ /* @__PURE__ */ jsx20(Skeleton, { className: "h-10 w-full" }),
2160
+ /* @__PURE__ */ jsx20(Skeleton, { className: "h-10 w-full" })
2161
+ ] });
2162
+ }
2163
+ return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-4 p-4", children: [
2164
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
2165
+ onClose && /* @__PURE__ */ jsx20(Button, { variant: "ghost", size: "icon", onClick: onClose, children: /* @__PURE__ */ jsx20(ArrowLeft, { className: "size-4" }) }),
2166
+ /* @__PURE__ */ jsx20("h3", { className: "text-lg font-semibold", children: "Create Store" })
2167
+ ] }),
2168
+ /* @__PURE__ */ jsx20(Separator2, {}),
2169
+ error && /* @__PURE__ */ jsx20("div", { className: "rounded-md border border-destructive/50 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error }),
2170
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
2171
+ /* @__PURE__ */ jsxs9(Label, { children: [
2172
+ "Store Name ",
2173
+ /* @__PURE__ */ jsx20("span", { className: "text-destructive", children: "*" })
2174
+ ] }),
2175
+ /* @__PURE__ */ jsx20(
2176
+ Input,
2177
+ {
2178
+ placeholder: "My Store",
2179
+ value: storeName,
2180
+ onChange: (e) => setStoreName(e.target.value),
2181
+ autoFocus: true
2182
+ }
2183
+ )
2184
+ ] }),
2185
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
2186
+ /* @__PURE__ */ jsxs9(Label, { children: [
2187
+ "Region ",
2188
+ /* @__PURE__ */ jsx20("span", { className: "text-destructive", children: "*" })
2189
+ ] }),
2190
+ /* @__PURE__ */ jsxs9(Select, { value: country, onValueChange: setCountry, children: [
2191
+ /* @__PURE__ */ jsx20(SelectTrigger, { children: /* @__PURE__ */ jsx20(SelectValue, { placeholder: "Select region" }) }),
2192
+ /* @__PURE__ */ jsx20(SelectContent, { children: REGIONS.map((r) => /* @__PURE__ */ jsx20(SelectItem, { value: r, children: r }, r)) })
2193
+ ] })
2194
+ ] }),
2195
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
2196
+ /* @__PURE__ */ jsxs9(Label, { children: [
2197
+ "Business ",
2198
+ /* @__PURE__ */ jsx20("span", { className: "text-destructive", children: "*" })
2199
+ ] }),
2200
+ /* @__PURE__ */ jsx20("div", { className: "flex flex-wrap gap-2", children: storeTypes.map((t) => {
2201
+ const selected = storeType.includes(t.id);
2202
+ return /* @__PURE__ */ jsxs9(
2203
+ "button",
2204
+ {
2205
+ type: "button",
2206
+ onClick: () => {
2207
+ setStoreType(
2208
+ (prev) => prev.includes(t.id) ? prev.filter((x) => x !== t.id) : [...prev, t.id]
2209
+ );
2210
+ if (t.id === "MP" && storeType.includes("MP")) {
2211
+ setChannels([]);
2212
+ }
2213
+ },
2214
+ className: cn(
2215
+ "flex flex-col items-start gap-0.5 rounded-lg border px-3 py-2 text-left text-sm transition-colors",
2216
+ selected ? "border-primary bg-primary/10 text-primary" : "border-border hover:bg-accent"
2217
+ ),
2218
+ children: [
2219
+ /* @__PURE__ */ jsx20("span", { className: "font-medium", children: t.label.replace("(MP)", "").replace("(D2C)", "").trim() }),
2220
+ /* @__PURE__ */ jsx20("span", { className: "text-xs text-muted-foreground", children: t.description })
2221
+ ]
2222
+ },
2223
+ t.id
2224
+ );
2225
+ }) })
2226
+ ] }),
2227
+ hasMP && /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
2228
+ /* @__PURE__ */ jsxs9(Label, { children: [
2229
+ "Channels ",
2230
+ /* @__PURE__ */ jsx20("span", { className: "text-destructive", children: "*" })
2231
+ ] }),
2232
+ /* @__PURE__ */ jsxs9(Popover, { open: channelPopoverOpen, onOpenChange: setChannelPopoverOpen, children: [
2233
+ /* @__PURE__ */ jsx20(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs9(
2234
+ Button,
2235
+ {
2236
+ variant: "outline",
2237
+ className: "w-full justify-between font-normal",
2238
+ children: [
2239
+ channels.length > 0 ? /* @__PURE__ */ jsx20("div", { className: "flex flex-wrap gap-1", children: channels.map((c) => /* @__PURE__ */ jsx20(Badge, { variant: "secondary", className: "text-xs", children: c.label }, c.value)) }) : /* @__PURE__ */ jsx20("span", { className: "text-muted-foreground", children: "Select channels..." }),
2240
+ /* @__PURE__ */ jsx20(ChevronDown, { className: "ml-2 size-4 shrink-0 opacity-50" })
2241
+ ]
2242
+ }
2243
+ ) }),
2244
+ /* @__PURE__ */ jsx20(PopoverContent, { className: "w-[300px] p-0", align: "start", children: /* @__PURE__ */ jsxs9(Command, { children: [
2245
+ /* @__PURE__ */ jsx20(CommandInput, { placeholder: "Search channels..." }),
2246
+ /* @__PURE__ */ jsxs9(CommandList, { children: [
2247
+ /* @__PURE__ */ jsx20(CommandEmpty, { children: "No channels found." }),
2248
+ /* @__PURE__ */ jsx20(CommandGroup, { children: channelOptions.map((ch) => {
2249
+ const selected = channels.some((c) => c.value === ch.value);
2250
+ return /* @__PURE__ */ jsxs9(
2251
+ CommandItem,
2252
+ {
2253
+ value: ch.value,
2254
+ onSelect: () => toggleChannel(ch),
2255
+ children: [
2256
+ /* @__PURE__ */ jsx20(
2257
+ "div",
2258
+ {
2259
+ className: cn(
2260
+ "mr-2 flex size-4 items-center justify-center rounded border",
2261
+ selected ? "border-primary bg-primary text-primary-foreground" : "border-muted-foreground/40"
2262
+ ),
2263
+ children: selected && /* @__PURE__ */ jsx20("span", { className: "text-xs", children: "\u2713" })
2264
+ }
2265
+ ),
2266
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
2267
+ ch.thumbnailUrl && /* @__PURE__ */ jsx20(
2268
+ "img",
2269
+ {
2270
+ src: ch.thumbnailUrl,
2271
+ alt: "",
2272
+ className: "size-5 rounded object-cover"
2273
+ }
2274
+ ),
2275
+ ch.label
2276
+ ] })
2277
+ ]
2278
+ },
2279
+ ch.value
2280
+ );
2281
+ }) })
2282
+ ] })
2283
+ ] }) })
2284
+ ] })
2285
+ ] }),
2286
+ hasD2C && /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
2287
+ /* @__PURE__ */ jsxs9(Label, { children: [
2288
+ "Store Website ",
2289
+ /* @__PURE__ */ jsx20("span", { className: "text-destructive", children: "*" })
2290
+ ] }),
2291
+ /* @__PURE__ */ jsx20(
2292
+ Input,
2293
+ {
2294
+ placeholder: "https://www.example.com",
2295
+ value: storeWebsite,
2296
+ onChange: (e) => setStoreWebsite(e.target.value)
2297
+ }
2298
+ )
2299
+ ] }),
2300
+ /* @__PURE__ */ jsxs9(
2301
+ "button",
2302
+ {
2303
+ type: "button",
2304
+ onClick: () => setShowAdvanced(!showAdvanced),
2305
+ className: "flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground",
2306
+ children: [
2307
+ "Advanced settings",
2308
+ /* @__PURE__ */ jsx20(
2309
+ ChevronDown,
2310
+ {
2311
+ className: cn(
2312
+ "size-4 transition-transform",
2313
+ showAdvanced && "rotate-180"
2314
+ )
2315
+ }
2316
+ )
2317
+ ]
2318
+ }
2319
+ ),
2320
+ showAdvanced && /* @__PURE__ */ jsxs9("div", { className: "space-y-4 rounded-lg border p-3", children: [
2321
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
2322
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1.5", children: [
2323
+ /* @__PURE__ */ jsx20(Label, { children: "Connect to an existing Store" }),
2324
+ /* @__PURE__ */ jsx20(
2325
+ "button",
2326
+ {
2327
+ type: "button",
2328
+ onClick: () => setShowConnectInfo(true),
2329
+ className: "text-muted-foreground hover:text-foreground",
2330
+ children: /* @__PURE__ */ jsx20(Info, { className: "size-3.5" })
2331
+ }
2332
+ )
2333
+ ] }),
2334
+ /* @__PURE__ */ jsxs9(
2335
+ Select,
2336
+ {
2337
+ value: linkedStoreId || "",
2338
+ onValueChange: (v) => setLinkedStoreId(v || null),
2339
+ children: [
2340
+ /* @__PURE__ */ jsx20(SelectTrigger, { children: /* @__PURE__ */ jsx20(SelectValue, { placeholder: "None" }) }),
2341
+ /* @__PURE__ */ jsx20(SelectContent, { children: stores.map((s) => /* @__PURE__ */ jsx20(SelectItem, { value: s._id, children: s.name }, s._id)) })
2342
+ ]
2343
+ }
2344
+ )
2345
+ ] }),
2346
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
2347
+ /* @__PURE__ */ jsxs9(Label, { children: [
2348
+ "Language(s) ",
2349
+ /* @__PURE__ */ jsx20("span", { className: "text-destructive", children: "*" })
2350
+ ] }),
2351
+ /* @__PURE__ */ jsxs9(Popover, { open: languagePopoverOpen, onOpenChange: setLanguagePopoverOpen, children: [
2352
+ /* @__PURE__ */ jsx20(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs9(
2353
+ Button,
2354
+ {
2355
+ variant: "outline",
2356
+ className: "w-full justify-between font-normal",
2357
+ children: [
2358
+ languages.length > 0 ? /* @__PURE__ */ jsx20("div", { className: "flex flex-wrap gap-1", children: languages.map((l) => /* @__PURE__ */ jsx20(Badge, { variant: "secondary", className: "text-xs", children: l }, l)) }) : /* @__PURE__ */ jsx20("span", { className: "text-muted-foreground", children: "Select languages..." }),
2359
+ /* @__PURE__ */ jsx20(ChevronDown, { className: "ml-2 size-4 shrink-0 opacity-50" })
2360
+ ]
2361
+ }
2362
+ ) }),
2363
+ /* @__PURE__ */ jsx20(PopoverContent, { className: "w-[250px] p-0", align: "start", children: /* @__PURE__ */ jsxs9(Command, { children: [
2364
+ /* @__PURE__ */ jsx20(CommandInput, { placeholder: "Search languages..." }),
2365
+ /* @__PURE__ */ jsxs9(CommandList, { children: [
2366
+ /* @__PURE__ */ jsx20(CommandEmpty, { children: "No languages found." }),
2367
+ /* @__PURE__ */ jsx20(CommandGroup, { children: availableLanguages.map((lang) => {
2368
+ const selected = languages.includes(lang);
2369
+ return /* @__PURE__ */ jsxs9(
2370
+ CommandItem,
2371
+ {
2372
+ value: lang,
2373
+ onSelect: () => toggleLanguage(lang),
2374
+ children: [
2375
+ /* @__PURE__ */ jsx20(
2376
+ "div",
2377
+ {
2378
+ className: cn(
2379
+ "mr-2 flex size-4 items-center justify-center rounded border",
2380
+ selected ? "border-primary bg-primary text-primary-foreground" : "border-muted-foreground/40"
2381
+ ),
2382
+ children: selected && /* @__PURE__ */ jsx20("span", { className: "text-xs", children: "\u2713" })
2383
+ }
2384
+ ),
2385
+ lang
2386
+ ]
2387
+ },
2388
+ lang
2389
+ );
2390
+ }) })
2391
+ ] })
2392
+ ] }) })
2393
+ ] })
2394
+ ] })
2395
+ ] }),
2396
+ /* @__PURE__ */ jsx20(Separator2, {}),
2397
+ /* @__PURE__ */ jsx20(
2398
+ Button,
2399
+ {
2400
+ onClick: handleCreate,
2401
+ disabled: isCreateDisabled || loading,
2402
+ className: "w-full",
2403
+ children: loading ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
2404
+ /* @__PURE__ */ jsx20(Loader24, { className: "size-4 animate-spin" }),
2405
+ "Creating..."
2406
+ ] }) : "Create & Proceed"
2407
+ }
2408
+ ),
2409
+ /* @__PURE__ */ jsx20(Dialog, { open: showConnectInfo, onOpenChange: setShowConnectInfo, children: /* @__PURE__ */ jsxs9(DialogContent, { children: [
2410
+ /* @__PURE__ */ jsxs9(DialogHeader, { children: [
2411
+ /* @__PURE__ */ jsx20(DialogTitle, { children: "Connected Stores" }),
2412
+ /* @__PURE__ */ jsx20(DialogDescription, { children: "How store linking works" })
2413
+ ] }),
2414
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-3 text-sm", children: [
2415
+ /* @__PURE__ */ jsx20("p", { children: "This setting allows optimised curation and processing for SMP and image attributes which remain the same across all SKUs under a style code." }),
2416
+ /* @__PURE__ */ jsxs9("div", { children: [
2417
+ /* @__PURE__ */ jsx20("p", { className: "font-medium", children: "What data is linked:" }),
2418
+ /* @__PURE__ */ jsxs9("ul", { className: "ml-4 mt-1 list-disc text-muted-foreground", children: [
2419
+ /* @__PURE__ */ jsx20("li", { children: "Parent-level attributes (category, type, sleeve, etc.)" }),
2420
+ /* @__PURE__ */ jsx20("li", { children: "Image attributes and processing (SMP URLs, pose type)" }),
2421
+ /* @__PURE__ */ jsx20("li", { children: "Related automations (background removal, classifiers)" })
2422
+ ] })
2423
+ ] }),
2424
+ /* @__PURE__ */ jsxs9("div", { children: [
2425
+ /* @__PURE__ */ jsx20("p", { className: "font-medium", children: "What data is NOT linked:" }),
2426
+ /* @__PURE__ */ jsxs9("ul", { className: "ml-4 mt-1 list-disc text-muted-foreground", children: [
2427
+ /* @__PURE__ */ jsx20("li", { children: "Child-level data (MRP, size, etc.)" }),
2428
+ /* @__PURE__ */ jsx20("li", { children: "Marketplace-translated attributes & images" }),
2429
+ /* @__PURE__ */ jsx20("li", { children: "Product status tags, export information" })
2430
+ ] })
2431
+ ] })
2432
+ ] })
2433
+ ] }) })
2434
+ ] });
2435
+ }
2436
+
2437
+ // src/Automations/ProductAutomation.tsx
2438
+ import { useState as useState8, useEffect as useEffect5, useCallback as useCallback6, useRef as useRef5 } from "react";
2439
+ import { ArrowLeft as ArrowLeft2, Search as Search3, Loader2 as Loader26, ChevronDown as ChevronDown2, Play } from "lucide-react";
2440
+
2441
+ // src/Automations/api.ts
2442
+ async function fetchAutomationList(storeId, tag, includeGroupings = false) {
2443
+ const { catalogixBaseUrl } = getApiConfig();
2444
+ const fields = includeGroupings ? "preview,groupings" : "preview";
2445
+ const res = await fetchJson(
2446
+ `${catalogixBaseUrl}/api/v1/fp_orchestrator/${storeId}/automations/list?tag=${tag}&include_fields=${fields}`
2447
+ );
2448
+ return res.data ?? {};
2449
+ }
2450
+ async function fetchAutomationSchema(storeId, automationName, tag) {
2451
+ const { catalogixBaseUrl } = getApiConfig();
2452
+ const res = await fetchJson(
2453
+ `${catalogixBaseUrl}/api/v1/fp_orchestrator/${storeId}/automations/schema?service_name=${automationName}&tag=${tag}`
2454
+ );
2455
+ return res.data?.[automationName] ?? [];
2456
+ }
2457
+ async function fetchDynamicLov(proxyUrl) {
2458
+ const { catalogixBaseUrl } = getApiConfig();
2459
+ return fetchJson(`${catalogixBaseUrl}/api?url=${proxyUrl}`);
2460
+ }
2461
+ async function applyAutomations(storeId, filters, selectedProducts, excludedProducts, selectedPageRange, automationConfigs, userId) {
2462
+ const { catalogixBaseUrl } = getApiConfig();
2463
+ return fetchJson(
2464
+ `${catalogixBaseUrl}/api/v1/store/${storeId}/products/automations`,
2465
+ {
2466
+ method: "POST",
2467
+ headers: { "Content-Type": "application/json" },
2468
+ body: JSON.stringify({
2469
+ filters,
2470
+ selected_products: selectedProducts,
2471
+ excluded_products: excludedProducts,
2472
+ selected_page_range: selectedPageRange,
2473
+ automations: automationConfigs,
2474
+ user_id: userId
2475
+ })
2476
+ }
2477
+ );
2478
+ }
2479
+
2480
+ // src/Automations/utils.ts
2481
+ function replaceFieldsInUrl(url, data) {
2482
+ return url.replace(/\$\((\w+)\)/g, (match, fieldName) => {
2483
+ const value = data[fieldName];
2484
+ return value !== void 0 ? String(value) : match;
2485
+ });
2486
+ }
2487
+ function getAllDependencies(depends, configs, channelsWithVersion) {
2488
+ const dependencies = {};
2489
+ let allSatisfied = true;
2490
+ if (!depends) return { dependencies, allSatisfied };
2491
+ for (const key of Object.keys(depends)) {
2492
+ if (depends[key]) {
2493
+ if (configs[depends[key]] !== void 0) {
2494
+ dependencies[depends[key]] = configs[depends[key]];
2495
+ } else {
2496
+ allSatisfied = false;
2497
+ break;
2498
+ }
2499
+ } else {
2500
+ if ((key === "source_marketplace_ov" || key === "target_marketplace_ov") && channelsWithVersion) {
2501
+ const sourceMp = key.replace("_ov", "");
2502
+ const mpValue = configs[sourceMp];
2503
+ if (mpValue && channelsWithVersion[mpValue]) {
2504
+ dependencies[key] = channelsWithVersion[mpValue];
2505
+ } else {
2506
+ allSatisfied = false;
2507
+ break;
2508
+ }
2509
+ } else {
2510
+ allSatisfied = false;
2511
+ break;
2512
+ }
2513
+ }
2514
+ }
2515
+ return { dependencies, allSatisfied };
2516
+ }
2517
+ function shouldRenderAttribute(conditions, configs) {
2518
+ if (!conditions) return true;
2519
+ for (const { condition, attribute, values } of conditions) {
2520
+ const attrValue = configs[attribute];
2521
+ if (condition === "not_in") {
2522
+ if (attrValue != null && (typeof attrValue === "string" ? attrValue.length : true)) {
2523
+ if (Array.isArray(attrValue)) {
2524
+ if (attrValue.some((v) => values.includes(v))) return false;
2525
+ } else {
2526
+ if (values.includes(attrValue)) return false;
2527
+ }
2528
+ }
2529
+ } else if (condition === "in") {
2530
+ if (attrValue === void 0) return false;
2531
+ const hasLength = typeof attrValue === "boolean" ? true : typeof attrValue === "string" ? attrValue.length > 0 : Array.isArray(attrValue) ? attrValue.length > 0 : true;
2532
+ if (!hasLength) return false;
2533
+ if (Array.isArray(attrValue)) {
2534
+ if (!attrValue.some((v) => values.includes(v))) return false;
2535
+ } else if (typeof attrValue !== "boolean") {
2536
+ if (!values.includes(attrValue)) return false;
2537
+ }
2538
+ }
2539
+ }
2540
+ return true;
2541
+ }
2542
+ function parseLovResponse(response, curl, configs) {
2543
+ try {
2544
+ const pathStr = curl.substring(curl.indexOf("'") + 1, curl.lastIndexOf("'"));
2545
+ const [pathPart, operationPart] = pathStr.split("|");
2546
+ const paths = pathPart.split(".").map((p) => p.trim()).filter(Boolean).map(
2547
+ (p) => p.startsWith("$(") ? String(configs[p.substring(p.indexOf("$(") + 2, p.lastIndexOf(")"))] ?? p) : p
2548
+ );
2549
+ let temp = response;
2550
+ for (const path of paths) {
2551
+ if (temp == null || typeof temp !== "object") return null;
2552
+ temp = temp[path];
2553
+ }
2554
+ if (temp == null) return null;
2555
+ if (operationPart?.trim() === "key" && typeof temp === "object") {
2556
+ return Object.keys(temp);
2557
+ }
2558
+ return Array.isArray(temp) ? temp : null;
2559
+ } catch {
2560
+ return null;
2561
+ }
2562
+ }
2563
+
2564
+ // src/Automations/SchemaFieldRenderer.tsx
2565
+ import { useCallback as useCallback5, useState as useState7 } from "react";
2566
+ import { Check as Check2, ChevronsUpDown as ChevronsUpDown2, Loader2 as Loader25, X } from "lucide-react";
2567
+ import { jsx as jsx21, jsxs as jsxs10 } from "react/jsx-runtime";
2568
+ function SchemaFieldRenderer({
2569
+ schema,
2570
+ configs,
2571
+ options,
2572
+ loadingOptions,
2573
+ onConfigChange
2574
+ }) {
2575
+ return /* @__PURE__ */ jsx21("div", { className: "space-y-4", children: schema.map((item, idx) => {
2576
+ if (!shouldRenderAttribute(item.render_if, configs)) return null;
2577
+ if (item.fieldtype === "object_array" && item.mapping_parameters) {
2578
+ return /* @__PURE__ */ jsx21("div", { className: "space-y-3", children: item.mapping_parameters.map((sub, subIdx) => {
2579
+ const value = configs[item.payload_key]?.[0] != null ? configs[item.payload_key][0]?.[sub.payload_key] : void 0;
2580
+ return /* @__PURE__ */ jsx21(
2581
+ FieldSwitch,
2582
+ {
2583
+ item: sub,
2584
+ value,
2585
+ configs,
2586
+ options,
2587
+ loadingOptions,
2588
+ onConfigChange
2589
+ },
2590
+ `${idx}-${subIdx}`
2591
+ );
2592
+ }) }, idx);
2593
+ }
2594
+ return /* @__PURE__ */ jsx21(
2595
+ FieldSwitch,
2596
+ {
2597
+ item,
2598
+ value: configs[item.payload_key],
2599
+ configs,
2600
+ options,
2601
+ loadingOptions,
2602
+ onConfigChange
2603
+ },
2604
+ idx
2605
+ );
2606
+ }) });
2607
+ }
2608
+ function FieldSwitch({
2609
+ item,
2610
+ value,
2611
+ configs,
2612
+ options,
2613
+ loadingOptions,
2614
+ onConfigChange
2615
+ }) {
2616
+ if (!shouldRenderAttribute(item.render_if, configs)) return null;
2617
+ switch (item.fieldtype) {
2618
+ case "freetext":
2619
+ return /* @__PURE__ */ jsx21(
2620
+ FreetextField,
2621
+ {
2622
+ item,
2623
+ value,
2624
+ onChange: (v) => onConfigChange(item.payload_key, v, item)
2625
+ }
2626
+ );
2627
+ case "freetext_list":
2628
+ return /* @__PURE__ */ jsx21(
2629
+ FreetextListField,
2630
+ {
2631
+ item,
2632
+ value,
2633
+ onChange: (v) => onConfigChange(item.payload_key, v, item)
2634
+ }
2635
+ );
2636
+ case "lov_singleselect":
2637
+ return /* @__PURE__ */ jsx21(
2638
+ SingleSelectField,
2639
+ {
2640
+ item,
2641
+ value,
2642
+ options: options[item.payload_key] ?? item.lov ?? [],
2643
+ loading: !!loadingOptions[item.payload_key],
2644
+ onChange: (v) => onConfigChange(item.payload_key, v, item)
2645
+ }
2646
+ );
2647
+ case "lov_multipleselect":
2648
+ return /* @__PURE__ */ jsx21(
2649
+ MultiSelectField,
2650
+ {
2651
+ item,
2652
+ value,
2653
+ options: options[item.payload_key] ?? item.lov ?? [],
2654
+ loading: !!loadingOptions[item.payload_key],
2655
+ onChange: (v) => onConfigChange(item.payload_key, v, item)
2656
+ }
2657
+ );
2658
+ case "image_singleselect":
2659
+ return /* @__PURE__ */ jsx21(
2660
+ ImageSelectField,
2661
+ {
2662
+ item,
2663
+ value,
2664
+ onChange: (v) => onConfigChange(item.payload_key, v, item)
2665
+ }
2666
+ );
2667
+ case "boolean":
2668
+ return /* @__PURE__ */ jsx21(
2669
+ BooleanField,
2670
+ {
2671
+ item,
2672
+ value,
2673
+ onChange: (v) => onConfigChange(item.payload_key, v, item)
2674
+ }
2675
+ );
2676
+ default:
2677
+ return null;
2678
+ }
2679
+ }
2680
+ function FreetextField({
2681
+ item,
2682
+ value,
2683
+ onChange
2684
+ }) {
2685
+ return /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5", children: [
2686
+ /* @__PURE__ */ jsxs10(Label, { children: [
2687
+ item.title,
2688
+ item.required && /* @__PURE__ */ jsx21("span", { className: "text-destructive", children: " *" })
2689
+ ] }),
2690
+ item.description && /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: item.description }),
2691
+ /* @__PURE__ */ jsx21(
2692
+ Input,
2693
+ {
2694
+ value: value ?? "",
2695
+ onChange: (e) => onChange(e.target.value),
2696
+ type: item.valuetype === "integer" ? "number" : "text",
2697
+ min: item.minimum
2698
+ }
2699
+ )
2700
+ ] });
2701
+ }
2702
+ function FreetextListField({
2703
+ item,
2704
+ value,
2705
+ onChange
2706
+ }) {
2707
+ const [draft, setDraft] = useState7("");
2708
+ const keywords = value ?? [];
2709
+ const addKeyword = () => {
2710
+ const trimmed = draft.trim();
2711
+ if (trimmed && !keywords.includes(trimmed)) {
2712
+ onChange([...keywords, trimmed]);
2713
+ setDraft("");
2714
+ }
2715
+ };
2716
+ return /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5", children: [
2717
+ /* @__PURE__ */ jsxs10(Label, { children: [
2718
+ item.title,
2719
+ item.required && /* @__PURE__ */ jsx21("span", { className: "text-destructive", children: " *" })
2720
+ ] }),
2721
+ item.description && /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: item.description }),
2722
+ /* @__PURE__ */ jsxs10("div", { className: "flex gap-2", children: [
2723
+ /* @__PURE__ */ jsx21(
2724
+ Input,
2725
+ {
2726
+ value: draft,
2727
+ onChange: (e) => setDraft(e.target.value),
2728
+ onKeyDown: (e) => e.key === "Enter" && (e.preventDefault(), addKeyword()),
2729
+ placeholder: "Type and press Enter"
2730
+ }
2731
+ ),
2732
+ /* @__PURE__ */ jsx21(Button, { variant: "outline", size: "sm", onClick: addKeyword, type: "button", children: "Add" })
2733
+ ] }),
2734
+ keywords.length > 0 && /* @__PURE__ */ jsx21("div", { className: "flex flex-wrap gap-1.5 pt-1", children: keywords.map((kw) => /* @__PURE__ */ jsxs10(Badge, { variant: "secondary", className: "gap-1 pr-1", children: [
2735
+ kw,
2736
+ /* @__PURE__ */ jsx21(
2737
+ "button",
2738
+ {
2739
+ type: "button",
2740
+ onClick: () => onChange(keywords.filter((k) => k !== kw)),
2741
+ className: "ml-0.5 rounded-full hover:bg-muted",
2742
+ children: /* @__PURE__ */ jsx21(X, { className: "size-3" })
2743
+ }
2744
+ )
2745
+ ] }, kw)) })
2746
+ ] });
2747
+ }
2748
+ function SingleSelectField({
2749
+ item,
2750
+ value,
2751
+ options: lovOptions,
2752
+ loading,
2753
+ onChange
2754
+ }) {
2755
+ const strOptions = Array.isArray(lovOptions) ? lovOptions.map(String) : [];
2756
+ const strValue = value != null ? String(value) : "";
2757
+ return /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5", children: [
2758
+ /* @__PURE__ */ jsxs10(Label, { children: [
2759
+ item.title,
2760
+ item.required && /* @__PURE__ */ jsx21("span", { className: "text-destructive", children: " *" })
2761
+ ] }),
2762
+ item.description && /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: item.description }),
2763
+ loading ? /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
2764
+ /* @__PURE__ */ jsx21(Loader25, { className: "size-4 animate-spin" }),
2765
+ "Loading options..."
2766
+ ] }) : /* @__PURE__ */ jsxs10(
2767
+ Select,
2768
+ {
2769
+ value: strValue,
2770
+ onValueChange: (v) => onChange(item.valuetype === "integer" ? Number(v) : v),
2771
+ children: [
2772
+ /* @__PURE__ */ jsx21(SelectTrigger, { children: /* @__PURE__ */ jsx21(SelectValue, { placeholder: "Select..." }) }),
2773
+ /* @__PURE__ */ jsx21(SelectContent, { children: strOptions.map((opt) => /* @__PURE__ */ jsx21(SelectItem, { value: opt, children: opt }, opt)) })
2774
+ ]
2775
+ }
2776
+ )
2777
+ ] });
2778
+ }
2779
+ function MultiSelectField({
2780
+ item,
2781
+ value,
2782
+ options: lovOptions,
2783
+ loading,
2784
+ onChange
2785
+ }) {
2786
+ const [open, setOpen] = useState7(false);
2787
+ const selected = value ?? [];
2788
+ const strOptions = Array.isArray(lovOptions) ? lovOptions.map(String) : [];
2789
+ const toggle = useCallback5(
2790
+ (opt) => {
2791
+ onChange(
2792
+ selected.includes(opt) ? selected.filter((s) => s !== opt) : [...selected, opt]
2793
+ );
2794
+ },
2795
+ [selected, onChange]
2796
+ );
2797
+ return /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5", children: [
2798
+ /* @__PURE__ */ jsxs10(Label, { children: [
2799
+ item.title,
2800
+ item.required && /* @__PURE__ */ jsx21("span", { className: "text-destructive", children: " *" })
2801
+ ] }),
2802
+ item.description && /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: item.description }),
2803
+ loading ? /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
2804
+ /* @__PURE__ */ jsx21(Loader25, { className: "size-4 animate-spin" }),
2805
+ "Loading options..."
2806
+ ] }) : /* @__PURE__ */ jsxs10(Popover, { open, onOpenChange: setOpen, children: [
2807
+ /* @__PURE__ */ jsx21(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs10(
2808
+ Button,
2809
+ {
2810
+ variant: "outline",
2811
+ className: "w-full justify-between font-normal",
2812
+ children: [
2813
+ selected.length > 0 ? /* @__PURE__ */ jsx21("div", { className: "flex flex-wrap gap-1", children: selected.map((s) => /* @__PURE__ */ jsx21(Badge, { variant: "secondary", className: "text-xs", children: s }, s)) }) : /* @__PURE__ */ jsx21("span", { className: "text-muted-foreground", children: "Select..." }),
2814
+ /* @__PURE__ */ jsx21(ChevronsUpDown2, { className: "ml-2 size-4 shrink-0 opacity-50" })
2815
+ ]
2816
+ }
2817
+ ) }),
2818
+ /* @__PURE__ */ jsx21(PopoverContent, { className: "w-[260px] p-0", align: "start", children: /* @__PURE__ */ jsxs10(Command, { children: [
2819
+ /* @__PURE__ */ jsx21(CommandInput, { placeholder: "Search..." }),
2820
+ /* @__PURE__ */ jsxs10(CommandList, { children: [
2821
+ /* @__PURE__ */ jsx21(CommandEmpty, { children: "No match." }),
2822
+ /* @__PURE__ */ jsx21(CommandGroup, { children: strOptions.map((opt) => {
2823
+ const isSelected = selected.includes(opt);
2824
+ return /* @__PURE__ */ jsxs10(
2825
+ CommandItem,
2826
+ {
2827
+ value: opt,
2828
+ onSelect: () => toggle(opt),
2829
+ children: [
2830
+ /* @__PURE__ */ jsx21(
2831
+ Check2,
2832
+ {
2833
+ className: cn(
2834
+ "mr-2 size-4",
2835
+ isSelected ? "opacity-100" : "opacity-0"
2836
+ )
2837
+ }
2838
+ ),
2839
+ opt
2840
+ ]
2841
+ },
2842
+ opt
2843
+ );
2844
+ }) })
2845
+ ] })
2846
+ ] }) })
2847
+ ] })
2848
+ ] });
2849
+ }
2850
+ function ImageSelectField({
2851
+ item,
2852
+ value,
2853
+ onChange
2854
+ }) {
2855
+ const images = item.lov ?? [];
2856
+ return /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5", children: [
2857
+ /* @__PURE__ */ jsxs10(Label, { children: [
2858
+ item.title,
2859
+ item.required && /* @__PURE__ */ jsx21("span", { className: "text-destructive", children: " *" })
2860
+ ] }),
2861
+ /* @__PURE__ */ jsx21("div", { className: "flex flex-wrap gap-3", children: images.map((url) => /* @__PURE__ */ jsxs10(
2862
+ "button",
2863
+ {
2864
+ type: "button",
2865
+ onClick: () => onChange(url),
2866
+ className: cn(
2867
+ "relative overflow-hidden rounded-lg border-2 transition-colors",
2868
+ value === url ? "border-primary ring-2 ring-primary/30" : "border-border hover:border-primary/50"
2869
+ ),
2870
+ children: [
2871
+ /* @__PURE__ */ jsx21("img", { src: url, alt: "", className: "size-20 object-cover" }),
2872
+ value === url && /* @__PURE__ */ jsx21("div", { className: "absolute right-1 top-1 rounded-full bg-primary p-0.5 text-primary-foreground", children: /* @__PURE__ */ jsx21(Check2, { className: "size-3" }) })
2873
+ ]
2874
+ },
2875
+ url
2876
+ )) })
2877
+ ] });
2878
+ }
2879
+ function BooleanField({
2880
+ item,
2881
+ value,
2882
+ onChange
2883
+ }) {
2884
+ return /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 py-1", children: [
2885
+ /* @__PURE__ */ jsx21(
2886
+ Checkbox,
2887
+ {
2888
+ checked: !!value,
2889
+ onCheckedChange: (checked) => onChange(!!checked)
2890
+ }
2891
+ ),
2892
+ /* @__PURE__ */ jsxs10(Label, { className: "cursor-pointer", onClick: () => onChange(!value), children: [
2893
+ item.title,
2894
+ item.required && /* @__PURE__ */ jsx21("span", { className: "text-destructive", children: " *" })
2895
+ ] })
2896
+ ] });
2897
+ }
2898
+
2899
+ // src/Automations/ProductAutomation.tsx
2900
+ import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs11 } from "react/jsx-runtime";
2901
+ function ProductAutomation({
2902
+ storeId,
2903
+ workspaceId,
2904
+ channelsWithVersion,
2905
+ selectedProducts,
2906
+ excludedProducts,
2907
+ totalProducts,
2908
+ productsCount: productsCountProp,
2909
+ filters,
2910
+ selectedPageRange,
2911
+ userId,
2912
+ onClose,
2913
+ onSubmit,
2914
+ onAutomationApplied
2915
+ }) {
2916
+ const [loading, setLoading] = useState8(true);
2917
+ const [automationGroups, setAutomationGroups] = useState8({});
2918
+ const [expandedGroups, setExpandedGroups] = useState8([]);
2919
+ const [searchQuery, setSearchQuery] = useState8("");
2920
+ const [selected, setSelected] = useState8(null);
2921
+ const [schema, setSchema] = useState8([]);
2922
+ const [configs, setConfigs] = useState8({});
2923
+ const [options, setOptions] = useState8({});
2924
+ const [loadingOptions, setLoadingOptions] = useState8({});
2925
+ const [schemaLoading, setSchemaLoading] = useState8(false);
2926
+ const [applying, setApplying] = useState8(false);
2927
+ const configsRef = useRef5(configs);
2928
+ configsRef.current = configs;
2929
+ const optionsRef = useRef5(options);
2930
+ optionsRef.current = options;
2931
+ const loadingOptionsRef = useRef5(loadingOptions);
2932
+ loadingOptionsRef.current = loadingOptions;
2933
+ const productsCount = selectedProducts.includes("all") ? excludedProducts.length === 0 ? totalProducts : totalProducts - excludedProducts.length : productsCountProp;
2934
+ useEffect5(() => {
2935
+ setLoading(true);
2936
+ fetchAutomationList(storeId, "product", true).then((data) => {
2937
+ const groups = {};
2938
+ for (const [name, item] of Object.entries(data)) {
2939
+ const itemGroups = item.groupings?.length ? item.groupings : ["Others"];
2940
+ for (const g of itemGroups) {
2941
+ if (!groups[g]) groups[g] = [];
2942
+ groups[g].push({ ...item, automation: name });
2943
+ }
2944
+ }
2945
+ setAutomationGroups(groups);
2946
+ const sorted = Object.keys(groups).sort();
2947
+ if (sorted.length > 0) setExpandedGroups([sorted[0]]);
2948
+ }).catch(console.error).finally(() => setLoading(false));
2949
+ }, [storeId]);
2950
+ useEffect5(() => {
2951
+ if (!selected) {
2952
+ setSchema([]);
2953
+ setConfigs({});
2954
+ setOptions({});
2955
+ setLoadingOptions({});
2956
+ return;
2957
+ }
2958
+ setSchemaLoading(true);
2959
+ fetchAutomationSchema(storeId, selected.automation, "product").then((items) => {
2960
+ setSchema(items);
2961
+ const defaults = {};
2962
+ for (const item of items) {
2963
+ if (item.default !== void 0) defaults[item.payload_key] = item.default;
2964
+ }
2965
+ setConfigs(defaults);
2966
+ configsRef.current = defaults;
2967
+ }).catch(console.error).finally(() => setSchemaLoading(false));
2968
+ }, [selected, storeId]);
2969
+ useEffect5(() => {
2970
+ if (schema.length === 0) return;
2971
+ const flatItems = schema.flatMap((s) => s.mapping_parameters ?? [s]);
2972
+ for (const item of flatItems) {
2973
+ if (!item.curl) continue;
2974
+ if (optionsRef.current[item.payload_key]) continue;
2975
+ const { dependencies, allSatisfied } = getAllDependencies(
2976
+ item.depends,
2977
+ configsRef.current,
2978
+ channelsWithVersion
2979
+ );
2980
+ if (allSatisfied) {
2981
+ const url = replaceFieldsInUrl(
2982
+ item.curl.split(" ")[1],
2983
+ dependencies
2984
+ );
2985
+ setLoadingOptions((prev) => ({ ...prev, [item.payload_key]: true }));
2986
+ fetchDynamicLov(url).then((res) => {
2987
+ const data = parseLovResponse(res, item.curl, configsRef.current);
2988
+ if (data) {
2989
+ setOptions((prev) => ({ ...prev, [item.payload_key]: data }));
2990
+ }
2991
+ }).catch(console.error).finally(
2992
+ () => setLoadingOptions((prev) => ({ ...prev, [item.payload_key]: false }))
2993
+ );
2994
+ }
2995
+ }
2996
+ }, [schema, configs, channelsWithVersion]);
2997
+ const handleConfigChange = useCallback6(
2998
+ (key, value, item) => {
2999
+ setConfigs((prev) => {
3000
+ const parentItem = schema.find(
3001
+ (s) => s.mapping_parameters?.some((m) => m.payload_key === key)
3002
+ );
3003
+ if (parentItem) {
3004
+ const existing = prev[parentItem.payload_key] ?? [{}];
3005
+ const updated = [{ ...existing[0], [key]: value }];
3006
+ return { ...prev, [parentItem.payload_key]: updated };
3007
+ }
3008
+ return { ...prev, [key]: value };
3009
+ });
3010
+ if (item.linked?.length) {
3011
+ for (const linkedKey of item.linked) {
3012
+ setOptions((prev) => ({ ...prev, [linkedKey]: [] }));
3013
+ setConfigs((prev) => {
3014
+ const next = { ...prev };
3015
+ delete next[linkedKey];
3016
+ return next;
3017
+ });
3018
+ }
3019
+ }
3020
+ },
3021
+ [schema]
3022
+ );
3023
+ const isRunAllowed = useCallback6(() => {
3024
+ for (const item of schema) {
3025
+ if (!shouldRenderAttribute(item.render_if, configs)) continue;
3026
+ if (item.required) {
3027
+ const val = configs[item.payload_key];
3028
+ if (val === void 0) return false;
3029
+ if (Array.isArray(val) && val.length === 0) return false;
3030
+ }
3031
+ if (item.minimum != null && Number(configs[item.payload_key]) < item.minimum) {
3032
+ return false;
3033
+ }
3034
+ }
3035
+ return true;
3036
+ }, [schema, configs]);
3037
+ const handleRun = async () => {
3038
+ if (!selected || !isRunAllowed()) return;
3039
+ if (onAutomationApplied) {
3040
+ onAutomationApplied({ [selected.automation]: configs });
3041
+ onClose();
3042
+ return;
3043
+ }
3044
+ setApplying(true);
3045
+ try {
3046
+ const res = await applyAutomations(
3047
+ storeId,
3048
+ filters,
3049
+ selectedProducts,
3050
+ excludedProducts,
3051
+ selectedPageRange,
3052
+ { [selected.automation]: configs },
3053
+ userId
3054
+ );
3055
+ if (res.status?.code === 0) {
3056
+ onSubmit?.({ [selected.automation]: configs });
3057
+ onClose();
3058
+ }
3059
+ } catch (err) {
3060
+ console.error("Apply automation failed:", err);
3061
+ } finally {
3062
+ setApplying(false);
3063
+ }
3064
+ };
3065
+ if (!selected) {
3066
+ const allItems = Object.values(automationGroups).flat();
3067
+ const unique = allItems.filter(
3068
+ (item, idx, arr) => arr.findIndex((a) => a.automation === item.automation) === idx
3069
+ );
3070
+ const filtered = searchQuery ? unique.filter(
3071
+ (a) => a.display_name?.toLowerCase().includes(searchQuery.toLowerCase()) || a.automation.toLowerCase().includes(searchQuery.toLowerCase())
3072
+ ) : null;
3073
+ return /* @__PURE__ */ jsxs11("div", { className: "flex h-full flex-col gap-3 p-3", children: [
3074
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
3075
+ /* @__PURE__ */ jsx22(Button, { variant: "ghost", size: "icon", onClick: onClose, children: /* @__PURE__ */ jsx22(ArrowLeft2, { className: "size-4" }) }),
3076
+ /* @__PURE__ */ jsx22("h3", { className: "text-base font-semibold", children: "Automations" }),
3077
+ /* @__PURE__ */ jsxs11(Badge, { variant: "secondary", children: [
3078
+ productsCount,
3079
+ " products"
3080
+ ] })
3081
+ ] }),
3082
+ /* @__PURE__ */ jsxs11("div", { className: "relative", children: [
3083
+ /* @__PURE__ */ jsx22(Search3, { className: "absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }),
3084
+ /* @__PURE__ */ jsx22(
3085
+ Input,
3086
+ {
3087
+ placeholder: "Search automations...",
3088
+ value: searchQuery,
3089
+ onChange: (e) => setSearchQuery(e.target.value),
3090
+ className: "pl-9"
3091
+ }
3092
+ )
3093
+ ] }),
3094
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-4 border-b px-2 pb-2 text-xs font-medium text-muted-foreground", children: [
3095
+ /* @__PURE__ */ jsx22("span", { className: "flex-[5]", children: "Automation" }),
3096
+ /* @__PURE__ */ jsx22("span", { className: "flex-[1.5] text-center", children: "Est. Time" }),
3097
+ /* @__PURE__ */ jsx22("span", { className: "flex-[1.5] text-center", children: "Credits" })
3098
+ ] }),
3099
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 overflow-y-auto", children: loading ? /* @__PURE__ */ jsx22("div", { className: "space-y-3 p-2", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsx22(Skeleton, { className: "h-14 w-full rounded-md" }, i)) }) : filtered ? filtered.map((item) => /* @__PURE__ */ jsx22(
3100
+ AutomationRow,
3101
+ {
3102
+ item,
3103
+ productsCount,
3104
+ onClick: () => setSelected(item)
3105
+ },
3106
+ item.automation
3107
+ )) : Object.keys(automationGroups).sort().map((group) => /* @__PURE__ */ jsxs11("div", { children: [
3108
+ /* @__PURE__ */ jsxs11(
3109
+ "button",
3110
+ {
3111
+ type: "button",
3112
+ onClick: () => setExpandedGroups(
3113
+ (prev) => prev.includes(group) ? prev.filter((g) => g !== group) : [...prev, group]
3114
+ ),
3115
+ className: "flex w-full items-center justify-between px-2 py-2 text-sm font-semibold text-muted-foreground hover:text-foreground",
3116
+ children: [
3117
+ group,
3118
+ /* @__PURE__ */ jsx22(
3119
+ ChevronDown2,
3120
+ {
3121
+ className: cn(
3122
+ "size-4 transition-transform",
3123
+ expandedGroups.includes(group) && "rotate-180"
3124
+ )
3125
+ }
3126
+ )
3127
+ ]
3128
+ }
3129
+ ),
3130
+ expandedGroups.includes(group) && automationGroups[group].map((item) => /* @__PURE__ */ jsx22(
3131
+ AutomationRow,
3132
+ {
3133
+ item,
3134
+ productsCount,
3135
+ onClick: () => setSelected(item)
3136
+ },
3137
+ item.automation
3138
+ ))
3139
+ ] }, group)) })
3140
+ ] });
3141
+ }
3142
+ return /* @__PURE__ */ jsxs11("div", { className: "flex h-full flex-col gap-3 p-3", children: [
3143
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
3144
+ /* @__PURE__ */ jsx22(Button, { variant: "ghost", size: "icon", onClick: () => setSelected(null), children: /* @__PURE__ */ jsx22(ArrowLeft2, { className: "size-4" }) }),
3145
+ /* @__PURE__ */ jsx22("h3", { className: "text-base font-semibold", children: selected.display_name })
3146
+ ] }),
3147
+ selected.preview && /* @__PURE__ */ jsxs11("div", { className: "space-y-2 rounded-lg bg-muted/50 p-3 text-sm", children: [
3148
+ selected.preview.info_text.map((text, i) => /* @__PURE__ */ jsx22("p", { className: "text-muted-foreground", children: text }, i)),
3149
+ selected.preview.media.length > 0 && /* @__PURE__ */ jsx22("div", { className: "flex flex-wrap gap-2 pt-1", children: selected.preview.media.map(
3150
+ (m, i) => m.type === "image" ? /* @__PURE__ */ jsx22(
3151
+ "img",
3152
+ {
3153
+ src: m.src,
3154
+ alt: "",
3155
+ className: "max-h-32 rounded-md object-contain"
3156
+ },
3157
+ i
3158
+ ) : null
3159
+ ) })
3160
+ ] }),
3161
+ /* @__PURE__ */ jsx22(Separator2, {}),
3162
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 overflow-y-auto", children: schemaLoading ? /* @__PURE__ */ jsx22("div", { className: "space-y-3 p-2", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx22(Skeleton, { className: "h-12 w-full rounded-md" }, i)) }) : /* @__PURE__ */ jsx22(
3163
+ SchemaFieldRenderer,
3164
+ {
3165
+ schema,
3166
+ configs,
3167
+ options,
3168
+ loadingOptions,
3169
+ onConfigChange: handleConfigChange
3170
+ }
3171
+ ) }),
3172
+ /* @__PURE__ */ jsx22(Separator2, {}),
3173
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
3174
+ /* @__PURE__ */ jsx22(
3175
+ Button,
3176
+ {
3177
+ onClick: handleRun,
3178
+ disabled: !isRunAllowed() || applying,
3179
+ className: "flex-1",
3180
+ children: applying ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
3181
+ /* @__PURE__ */ jsx22(Loader26, { className: "size-4 animate-spin" }),
3182
+ "Applying..."
3183
+ ] }) : /* @__PURE__ */ jsxs11(Fragment4, { children: [
3184
+ /* @__PURE__ */ jsx22(Play, { className: "size-4" }),
3185
+ "Run Automation"
3186
+ ] })
3187
+ }
3188
+ ),
3189
+ /* @__PURE__ */ jsx22(Button, { variant: "outline", onClick: () => setSelected(null), children: "Cancel" })
3190
+ ] }),
3191
+ /* @__PURE__ */ jsx22("p", { className: "text-center text-xs text-muted-foreground", children: "By applying, you will override existing automations for the selected products with this new run." })
3192
+ ] });
3193
+ }
3194
+ function AutomationRow({
3195
+ item,
3196
+ productsCount,
3197
+ onClick
3198
+ }) {
3199
+ const estTime = item.estimate_time_saved ? formatTimeSaved(item.estimate_time_saved * productsCount) : "\u2014";
3200
+ const credits = item.credits ? Math.ceil(item.credits * productsCount) : 0;
3201
+ return /* @__PURE__ */ jsxs11(
3202
+ "button",
3203
+ {
3204
+ type: "button",
3205
+ onClick,
3206
+ className: "flex w-full items-center gap-4 rounded-md px-2 py-2.5 text-left hover:bg-accent",
3207
+ children: [
3208
+ /* @__PURE__ */ jsxs11("div", { className: "flex-[5] min-w-0", children: [
3209
+ /* @__PURE__ */ jsx22("div", { className: "text-sm font-medium", children: item.display_name }),
3210
+ item.description && /* @__PURE__ */ jsx22("div", { className: "truncate text-xs text-muted-foreground", children: item.description })
3211
+ ] }),
3212
+ /* @__PURE__ */ jsx22("div", { className: "flex-[1.5] text-center text-xs text-muted-foreground", children: estTime }),
3213
+ /* @__PURE__ */ jsx22("div", { className: "flex-[1.5] text-center text-xs text-muted-foreground", children: credits })
3214
+ ]
3215
+ }
3216
+ );
3217
+ }
3218
+ function formatTimeSaved(totalSeconds) {
3219
+ if (totalSeconds >= 3600) return `${Math.ceil(totalSeconds / 3600)} hrs`;
3220
+ if (totalSeconds >= 60) return `${Math.ceil(totalSeconds / 60)} mins`;
3221
+ return `${totalSeconds} secs`;
3222
+ }
3223
+
3224
+ // src/Automations/StoreAutomation.tsx
3225
+ import { useState as useState9, useEffect as useEffect6, useCallback as useCallback7, useRef as useRef6 } from "react";
3226
+ import { ArrowLeft as ArrowLeft3, Settings } from "lucide-react";
3227
+ import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs12 } from "react/jsx-runtime";
3228
+ function StoreAutomation({
3229
+ storeId,
3230
+ channelsWithVersion,
3231
+ enabledAutomations = {},
3232
+ onSubmit,
3233
+ onClose
3234
+ }) {
3235
+ const [loading, setLoading] = useState9(true);
3236
+ const [automations, setAutomations] = useState9([]);
3237
+ const [selectedConfigs, setSelectedConfigs] = useState9({ ...enabledAutomations });
3238
+ const [selectedAutomation, setSelectedAutomation] = useState9(null);
3239
+ useEffect6(() => {
3240
+ setLoading(true);
3241
+ fetchAutomationList(storeId, "default").then((data) => {
3242
+ setAutomations(
3243
+ Object.entries(data).map(([name, item]) => ({
3244
+ ...item,
3245
+ automation: name,
3246
+ isEnabled: Boolean(enabledAutomations[name])
3247
+ }))
3248
+ );
3249
+ }).catch(console.error).finally(() => setLoading(false));
3250
+ }, [storeId, enabledAutomations]);
3251
+ const toggleAutomation = useCallback7((automationName) => {
3252
+ setAutomations(
3253
+ (prev) => prev.map(
3254
+ (a) => a.automation === automationName ? { ...a, isEnabled: !a.isEnabled } : a
3255
+ )
3256
+ );
3257
+ setSelectedConfigs((prev) => {
3258
+ const next = { ...prev };
3259
+ if (next[automationName]) {
3260
+ delete next[automationName];
3261
+ }
3262
+ return next;
3263
+ });
3264
+ }, []);
3265
+ const handleSave = () => {
3266
+ const enabledOnly = {};
3267
+ for (const a of automations) {
3268
+ if (a.isEnabled && selectedConfigs[a.automation]) {
3269
+ enabledOnly[a.automation] = selectedConfigs[a.automation];
3270
+ }
3271
+ }
3272
+ onSubmit?.({ enabled_automations: enabledOnly });
3273
+ };
3274
+ const handleConfigClose = (automation, newConfigs) => {
3275
+ if (automation.isEnabled) {
3276
+ setSelectedConfigs((prev) => ({
3277
+ ...prev,
3278
+ [automation.automation]: newConfigs
3279
+ }));
3280
+ }
3281
+ setSelectedAutomation(null);
3282
+ };
3283
+ if (selectedAutomation) {
3284
+ return /* @__PURE__ */ jsx23(
3285
+ AutomationConfigEditor,
3286
+ {
3287
+ storeId,
3288
+ automation: selectedAutomation,
3289
+ channelsWithVersion,
3290
+ initialConfigs: selectedConfigs[selectedAutomation.automation],
3291
+ onClose: handleConfigClose,
3292
+ onToggle: () => toggleAutomation(selectedAutomation.automation)
3293
+ }
3294
+ );
3295
+ }
3296
+ return /* @__PURE__ */ jsxs12("div", { className: "flex h-full flex-col gap-3 p-3", children: [
3297
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
3298
+ onClose && /* @__PURE__ */ jsx23(Button, { variant: "ghost", size: "icon", onClick: onClose, children: /* @__PURE__ */ jsx23(ArrowLeft3, { className: "size-4" }) }),
3299
+ /* @__PURE__ */ jsx23("h3", { className: "text-base font-semibold", children: "Default Store Automations" })
3300
+ ] }),
3301
+ /* @__PURE__ */ jsx23(Separator2, {}),
3302
+ loading ? /* @__PURE__ */ jsx23("div", { className: "space-y-3 p-2", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsx23(Skeleton, { className: "h-14 w-full rounded-md" }, i)) }) : /* @__PURE__ */ jsx23("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsxs12(Table, { children: [
3303
+ /* @__PURE__ */ jsx23(TableHeader, { children: /* @__PURE__ */ jsxs12(TableRow, { children: [
3304
+ /* @__PURE__ */ jsx23(TableHead, { children: "Automation" }),
3305
+ /* @__PURE__ */ jsx23(TableHead, { className: "w-24 text-center", children: "Avg. Time" }),
3306
+ /* @__PURE__ */ jsx23(TableHead, { className: "w-28 text-center", children: "Credits/Prd" }),
3307
+ /* @__PURE__ */ jsx23(TableHead, { className: "w-28 text-center", children: "Status" }),
3308
+ /* @__PURE__ */ jsx23(TableHead, { className: "w-12" })
3309
+ ] }) }),
3310
+ /* @__PURE__ */ jsx23(TableBody, { children: automations.map((a) => /* @__PURE__ */ jsxs12(
3311
+ TableRow,
3312
+ {
3313
+ className: "cursor-pointer",
3314
+ onClick: () => setSelectedAutomation(a),
3315
+ children: [
3316
+ /* @__PURE__ */ jsxs12(TableCell, { children: [
3317
+ /* @__PURE__ */ jsx23("div", { className: "text-sm font-medium", children: a.display_name }),
3318
+ a.description && /* @__PURE__ */ jsx23("div", { className: "text-xs text-muted-foreground", children: a.description })
3319
+ ] }),
3320
+ /* @__PURE__ */ jsx23(TableCell, { className: "text-center text-xs", children: a.estimate_run_time ?? "\u2014" }),
3321
+ /* @__PURE__ */ jsx23(TableCell, { className: "text-center text-xs", children: a.credits ?? "\u2014" }),
3322
+ /* @__PURE__ */ jsx23(TableCell, { className: "text-center", children: /* @__PURE__ */ jsxs12(
3323
+ "div",
3324
+ {
3325
+ className: "flex items-center justify-center gap-2",
3326
+ onClick: (e) => e.stopPropagation(),
3327
+ children: [
3328
+ /* @__PURE__ */ jsx23(
3329
+ Switch,
3330
+ {
3331
+ checked: a.isEnabled,
3332
+ onCheckedChange: () => toggleAutomation(a.automation)
3333
+ }
3334
+ ),
3335
+ /* @__PURE__ */ jsx23(
3336
+ Badge,
3337
+ {
3338
+ variant: a.isEnabled ? "default" : "secondary",
3339
+ className: "text-xs",
3340
+ children: a.isEnabled ? "Active" : "Inactive"
3341
+ }
3342
+ )
3343
+ ]
3344
+ }
3345
+ ) }),
3346
+ /* @__PURE__ */ jsx23(TableCell, { children: /* @__PURE__ */ jsx23(Settings, { className: "size-4 text-muted-foreground" }) })
3347
+ ]
3348
+ },
3349
+ a.automation
3350
+ )) })
3351
+ ] }) }),
3352
+ /* @__PURE__ */ jsx23(Separator2, {}),
3353
+ /* @__PURE__ */ jsxs12("div", { className: "flex justify-end gap-2", children: [
3354
+ onClose && /* @__PURE__ */ jsx23(Button, { variant: "outline", onClick: onClose, children: "Cancel" }),
3355
+ /* @__PURE__ */ jsx23(Button, { onClick: handleSave, children: "Save Changes" })
3356
+ ] })
3357
+ ] });
3358
+ }
3359
+ function AutomationConfigEditor({
3360
+ storeId,
3361
+ automation,
3362
+ channelsWithVersion,
3363
+ initialConfigs,
3364
+ onClose,
3365
+ onToggle
3366
+ }) {
3367
+ const [schemaLoading, setSchemaLoading] = useState9(false);
3368
+ const [schema, setSchema] = useState9([]);
3369
+ const [configs, setConfigs] = useState9(
3370
+ initialConfigs ?? {}
3371
+ );
3372
+ const [options, setOptions] = useState9({});
3373
+ const [loadingOptions, setLoadingOptions] = useState9({});
3374
+ const configsRef = useRef6(configs);
3375
+ configsRef.current = configs;
3376
+ const optionsRef = useRef6(options);
3377
+ optionsRef.current = options;
3378
+ useEffect6(() => {
3379
+ if (!automation.isEnabled) return;
3380
+ setSchemaLoading(true);
3381
+ fetchAutomationSchema(storeId, automation.automation, "default").then((items) => {
3382
+ setSchema(items);
3383
+ if (!initialConfigs) {
3384
+ const defaults = {};
3385
+ for (const item of items) {
3386
+ if (item.default !== void 0) defaults[item.payload_key] = item.default;
3387
+ }
3388
+ setConfigs(defaults);
3389
+ configsRef.current = defaults;
3390
+ }
3391
+ }).catch(console.error).finally(() => setSchemaLoading(false));
3392
+ }, [storeId, automation, initialConfigs]);
3393
+ useEffect6(() => {
3394
+ if (schema.length === 0) return;
3395
+ for (const item of schema) {
3396
+ if (!item.curl) continue;
3397
+ if (optionsRef.current[item.payload_key]) continue;
3398
+ const { dependencies, allSatisfied } = getAllDependencies(
3399
+ item.depends,
3400
+ configsRef.current,
3401
+ channelsWithVersion
3402
+ );
3403
+ if (allSatisfied) {
3404
+ const url = replaceFieldsInUrl(item.curl.split(" ")[1], dependencies);
3405
+ setLoadingOptions((prev) => ({ ...prev, [item.payload_key]: true }));
3406
+ fetchDynamicLov(url).then((res) => {
3407
+ const data = parseLovResponse(res, item.curl, configsRef.current);
3408
+ if (data) {
3409
+ setOptions((prev) => ({ ...prev, [item.payload_key]: data }));
3410
+ }
3411
+ }).catch(console.error).finally(
3412
+ () => setLoadingOptions((prev) => ({
3413
+ ...prev,
3414
+ [item.payload_key]: false
3415
+ }))
3416
+ );
3417
+ }
3418
+ }
3419
+ }, [schema, configs, channelsWithVersion]);
3420
+ const handleConfigChange = useCallback7(
3421
+ (key, value, item) => {
3422
+ setConfigs((prev) => ({ ...prev, [key]: value }));
3423
+ if (item.linked?.length) {
3424
+ for (const linkedKey of item.linked) {
3425
+ setOptions((prev) => ({ ...prev, [linkedKey]: [] }));
3426
+ setConfigs((prev) => {
3427
+ const next = { ...prev };
3428
+ delete next[linkedKey];
3429
+ return next;
3430
+ });
3431
+ }
3432
+ }
3433
+ },
3434
+ []
3435
+ );
3436
+ return /* @__PURE__ */ jsxs12("div", { className: "flex h-full flex-col gap-3 p-3", children: [
3437
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
3438
+ /* @__PURE__ */ jsx23(
3439
+ Button,
3440
+ {
3441
+ variant: "ghost",
3442
+ size: "icon",
3443
+ onClick: () => onClose(automation, configs),
3444
+ children: /* @__PURE__ */ jsx23(ArrowLeft3, { className: "size-4" })
3445
+ }
3446
+ ),
3447
+ /* @__PURE__ */ jsx23("h3", { className: "text-base font-semibold", children: automation.display_name })
3448
+ ] }),
3449
+ /* @__PURE__ */ jsxs12("div", { className: "flex gap-4 text-sm", children: [
3450
+ /* @__PURE__ */ jsxs12("div", { className: "text-muted-foreground", children: [
3451
+ "Est. time: ",
3452
+ /* @__PURE__ */ jsx23("span", { className: "text-foreground", children: automation.estimate_run_time ?? "\u2014" })
3453
+ ] }),
3454
+ /* @__PURE__ */ jsxs12("div", { className: "text-muted-foreground", children: [
3455
+ "Credits/prd: ",
3456
+ /* @__PURE__ */ jsx23("span", { className: "text-foreground", children: automation.credits ?? "\u2014" })
3457
+ ] })
3458
+ ] }),
3459
+ /* @__PURE__ */ jsx23(Separator2, {}),
3460
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
3461
+ /* @__PURE__ */ jsx23("span", { className: "text-sm font-medium", children: automation.isEnabled ? "Enabled" : "Disabled" }),
3462
+ /* @__PURE__ */ jsx23(Switch, { checked: automation.isEnabled, onCheckedChange: onToggle })
3463
+ ] }),
3464
+ /* @__PURE__ */ jsx23(Separator2, {}),
3465
+ /* @__PURE__ */ jsx23("div", { className: "flex-1 overflow-y-auto", children: automation.isEnabled ? schemaLoading ? /* @__PURE__ */ jsx23("div", { className: "space-y-3 p-2", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx23(Skeleton, { className: "h-12 w-full rounded-md" }, i)) }) : /* @__PURE__ */ jsx23(
3466
+ SchemaFieldRenderer,
3467
+ {
3468
+ schema,
3469
+ configs,
3470
+ options,
3471
+ loadingOptions,
3472
+ onConfigChange: handleConfigChange
3473
+ }
3474
+ ) : /* @__PURE__ */ jsx23("p", { className: "py-4 text-center text-sm text-muted-foreground", children: "Enable this automation to configure it." }) }),
3475
+ automation.preview && /* @__PURE__ */ jsxs12(Fragment5, { children: [
3476
+ /* @__PURE__ */ jsx23(Separator2, {}),
3477
+ /* @__PURE__ */ jsxs12("div", { className: "space-y-2 rounded-lg bg-muted/50 p-3 text-sm", children: [
3478
+ automation.preview.info_text.map((text, i) => /* @__PURE__ */ jsx23("p", { className: "text-muted-foreground", children: text }, i)),
3479
+ automation.preview.media.length > 0 && /* @__PURE__ */ jsx23("div", { className: "flex flex-wrap gap-2 pt-1", children: automation.preview.media.map(
3480
+ (m, i) => m.type === "image" ? /* @__PURE__ */ jsx23(
3481
+ "img",
3482
+ {
3483
+ src: m.src,
3484
+ alt: "",
3485
+ className: "max-h-24 rounded-md object-contain"
3486
+ },
3487
+ i
3488
+ ) : null
3489
+ ) })
3490
+ ] })
3491
+ ] })
3492
+ ] });
3493
+ }
3494
+
3495
+ // src/CatalogixChat.tsx
3496
+ import { jsx as jsx24, jsxs as jsxs13 } from "react/jsx-runtime";
3497
+ var SECTION_MAP = {
3498
+ "catalogix:create-store": "create_store",
3499
+ create_or_select_store: "create_store",
3500
+ "catalogix:select-products": "select_products",
3501
+ select_products: "select_products",
3502
+ "catalogix:automations": "automations",
3503
+ trigger_automation: "trigger_automation"
3504
+ };
3505
+ function CatalogixChat(props) {
3506
+ const { uiProps, meta, onSubmit, workspaceId, userId } = props;
3507
+ const componentName = uiProps._componentName ?? "";
3508
+ const section = meta?.step || SECTION_MAP[componentName] || componentName;
3509
+ const catalogixBaseUrl = uiProps.catalogixBaseUrl ?? "";
3510
+ const storeId = uiProps.storeId ?? "";
3511
+ const stores = uiProps.stores ?? [];
3512
+ const channelsWithVersion = uiProps.channelsWithVersion ?? void 0;
3513
+ const selectedProducts = uiProps.selectedProducts ?? [];
3514
+ const excludedProducts = uiProps.excludedProducts ?? [];
3515
+ const totalProducts = uiProps.totalProducts ?? 0;
3516
+ const productsCount = uiProps.productsCount ?? 0;
3517
+ const filters = uiProps.filters ?? {};
3518
+ const selectedPageRange = uiProps.selectedPageRange ?? [];
3519
+ const enabledAutomations = uiProps.enabledAutomations ?? {};
3520
+ useEffect7(() => {
3521
+ configureApi({ catalogixBaseUrl });
3522
+ }, [catalogixBaseUrl]);
3523
+ if (section === "select_products") {
3524
+ return /* @__PURE__ */ jsx24(
3525
+ SelectProducts,
3526
+ {
3527
+ workspaceId,
3528
+ storeId,
3529
+ userId,
3530
+ submitted: props.submitted,
3531
+ submittedValues: props.submittedValues,
3532
+ onConfirmSelection: async (_sel, _exc, _range, _filters, _useUuid, _payload, resumeValues) => {
3533
+ onSubmit(resumeValues, "Products selected.");
3534
+ return { submitted: true, submittedValues: resumeValues };
3535
+ }
3536
+ }
3537
+ );
3538
+ }
3539
+ if (section === "create_store") {
3540
+ return /* @__PURE__ */ jsx24(
3541
+ CreateStore,
3542
+ {
3543
+ workspaceId,
3544
+ stores,
3545
+ initialStoreName: uiProps.storeName ?? "",
3546
+ useCatalogixTaxonomy: !!uiProps.useCatalogixTaxonomy,
3547
+ shouldRefetchStore: !!uiProps.shouldRefetchStore,
3548
+ onStoreCreated: (storeData) => onSubmit(storeData, "Store created successfully."),
3549
+ onCreateError: (err) => console.error("[CatalogixChat] CreateStore error:", err)
3550
+ }
3551
+ );
3552
+ }
3553
+ if (section === "trigger_automation") {
3554
+ return /* @__PURE__ */ jsx24(
3555
+ ProductAutomation,
3556
+ {
3557
+ storeId,
3558
+ workspaceId,
3559
+ channelsWithVersion,
3560
+ selectedProducts,
3561
+ excludedProducts,
3562
+ totalProducts,
3563
+ productsCount,
3564
+ filters,
3565
+ selectedPageRange,
3566
+ userId,
3567
+ onClose: () => props.onCancel?.(),
3568
+ onSubmit: (automationConfigs) => onSubmit(automationConfigs, "Automation applied."),
3569
+ onAutomationApplied: (configs) => onSubmit(configs, "Automation applied.")
3570
+ }
3571
+ );
3572
+ }
3573
+ if (section === "automations") {
3574
+ return /* @__PURE__ */ jsx24(
3575
+ StoreAutomation,
3576
+ {
3577
+ storeId,
3578
+ channelsWithVersion,
3579
+ enabledAutomations,
3580
+ onSubmit: (settings) => onSubmit(settings, "Store automations saved."),
3581
+ onClose: () => props.onCancel?.()
3582
+ }
3583
+ );
3584
+ }
3585
+ return /* @__PURE__ */ jsx24("div", { className: "p-4 text-sm text-muted-foreground", children: /* @__PURE__ */ jsxs13("p", { children: [
3586
+ /* @__PURE__ */ jsx24("strong", { children: "Catalogix" }),
3587
+ " \u2014 unknown section:",
3588
+ " ",
3589
+ /* @__PURE__ */ jsx24("code", { children: section || "(none)" })
3590
+ ] }) });
3591
+ }
38
3592
 
39
3593
  // src/manifest.ts
40
3594
  var catalogixManifest = [
@@ -82,5 +3636,10 @@ var catalogixManifest = [
82
3636
  ];
83
3637
  export {
84
3638
  CatalogixChat,
85
- catalogixManifest
3639
+ CreateStore,
3640
+ ProductAutomation,
3641
+ SelectProducts,
3642
+ StoreAutomation,
3643
+ catalogixManifest,
3644
+ configureApi
86
3645
  };