@copilotz/admin 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -43,13 +43,13 @@ __export(index_exports, {
43
43
  module.exports = __toCommonJS(index_exports);
44
44
 
45
45
  // src/CopilotzAdmin.tsx
46
- var import_react2 = require("react");
46
+ var import_react3 = require("react");
47
47
 
48
48
  // src/config.ts
49
49
  var defaultAdminConfig = {
50
50
  branding: {
51
51
  title: "Copilotz Admin",
52
- subtitle: "Read-only operational visibility for Copilotz clients",
52
+ subtitle: "Operational visibility for Copilotz clients",
53
53
  logo: null,
54
54
  actions: null
55
55
  },
@@ -83,24 +83,33 @@ var defaultAdminConfig = {
83
83
  scopeScoped: "Scoped",
84
84
  configured: "Configured",
85
85
  unconfigured: "Observed only",
86
- noResults: "No results"
86
+ noResults: "No results",
87
+ eventsTitle: "Events",
88
+ dashboardTitle: "Dashboard"
87
89
  },
88
90
  features: {
89
91
  showOverview: true,
90
92
  showActivity: true,
91
93
  showThreads: true,
92
94
  showParticipants: true,
93
- showAgents: true
95
+ showAgents: true,
96
+ showEvents: false
94
97
  },
95
98
  ui: {
96
99
  compact: false,
97
100
  maxActivityBars: 18
98
101
  },
102
+ sidebar: {
103
+ defaultOpen: true,
104
+ collapsible: "icon"
105
+ },
99
106
  baseUrl: "",
100
107
  getRequestHeaders: async () => ({}),
101
108
  namespace: "",
102
109
  initialRange: "7d",
103
- initialInterval: "day"
110
+ initialInterval: "day",
111
+ defaultPage: "dashboard",
112
+ onNavigate: null
104
113
  };
105
114
  function mergeAdminConfig(_baseConfig, userConfig) {
106
115
  if (!userConfig) return defaultAdminConfig;
@@ -121,11 +130,17 @@ function mergeAdminConfig(_baseConfig, userConfig) {
121
130
  ...defaultAdminConfig.ui,
122
131
  ...userConfig.ui
123
132
  },
133
+ sidebar: {
134
+ ...defaultAdminConfig.sidebar,
135
+ ...userConfig.sidebar
136
+ },
124
137
  baseUrl: userConfig.baseUrl ?? defaultAdminConfig.baseUrl,
125
138
  getRequestHeaders: userConfig.getRequestHeaders ?? defaultAdminConfig.getRequestHeaders,
126
139
  namespace: userConfig.namespace ?? defaultAdminConfig.namespace,
127
140
  initialRange: userConfig.initialRange ?? defaultAdminConfig.initialRange,
128
- initialInterval: userConfig.initialInterval ?? defaultAdminConfig.initialInterval
141
+ initialInterval: userConfig.initialInterval ?? defaultAdminConfig.initialInterval,
142
+ defaultPage: userConfig.defaultPage ?? defaultAdminConfig.defaultPage,
143
+ onNavigate: userConfig.onNavigate ?? defaultAdminConfig.onNavigate
129
144
  };
130
145
  }
131
146
 
@@ -195,6 +210,21 @@ async function fetchAdminJson(path, params, options) {
195
210
  const payload = await response.json();
196
211
  return payload.data;
197
212
  }
213
+ async function fetchRawJson(path, params, options) {
214
+ const url = new URL(`${resolveBaseUrl(options?.baseUrl)}${path}`, window.location.origin);
215
+ for (const [key, value] of Object.entries(params)) {
216
+ if (typeof value === "string" && value.length > 0) {
217
+ url.searchParams.set(key, value);
218
+ }
219
+ }
220
+ const response = await fetch(url.toString(), {
221
+ headers: await withAuthHeaders({}, options?.getRequestHeaders)
222
+ });
223
+ if (!response.ok) {
224
+ throw new Error(`Admin request failed (${response.status})`);
225
+ }
226
+ return await response.json();
227
+ }
198
228
  async function fetchAdminOverview(range, namespace, options) {
199
229
  const windowRange = getRangeWindow(range);
200
230
  return await fetchAdminJson("/v1/admin/overview", {
@@ -233,6 +263,23 @@ async function fetchAdminAgents(search, namespace, options) {
233
263
  limit: "8"
234
264
  }, options);
235
265
  }
266
+ async function fetchThreadDetail(threadId, options) {
267
+ return await fetchAdminJson(
268
+ `/v1/threads/${encodeURIComponent(threadId)}`,
269
+ {},
270
+ options
271
+ );
272
+ }
273
+ async function fetchThreadMessages(threadId, messageOptions, options) {
274
+ return await fetchRawJson(
275
+ `/v1/threads/${encodeURIComponent(threadId)}/messages`,
276
+ {
277
+ limit: messageOptions?.limit?.toString(),
278
+ before: messageOptions?.before
279
+ },
280
+ options
281
+ );
282
+ }
236
283
 
237
284
  // src/useCopilotzAdmin.ts
238
285
  function useCopilotzAdmin(options = {}) {
@@ -358,6 +405,19 @@ function Card({ className, ...props }) {
358
405
  }
359
406
  );
360
407
  }
408
+ function CardHeader({ className, ...props }) {
409
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
410
+ "div",
411
+ {
412
+ "data-slot": "card-header",
413
+ className: cn(
414
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
415
+ className
416
+ ),
417
+ ...props
418
+ }
419
+ );
420
+ }
361
421
  function CardContent({ className, ...props }) {
362
422
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
363
423
  "div",
@@ -416,10 +476,83 @@ function Button({
416
476
  );
417
477
  }
418
478
 
419
- // src/components/ui/input.tsx
479
+ // src/components/ui/tooltip.tsx
480
+ var TooltipPrimitive = __toESM(require("@radix-ui/react-tooltip"), 1);
420
481
  var import_jsx_runtime3 = require("react/jsx-runtime");
421
- function Input({ className, type, ...props }) {
482
+ function TooltipProvider({
483
+ delayDuration = 0,
484
+ ...props
485
+ }) {
422
486
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
487
+ TooltipPrimitive.Provider,
488
+ {
489
+ "data-slot": "tooltip-provider",
490
+ delayDuration,
491
+ ...props
492
+ }
493
+ );
494
+ }
495
+ function Tooltip({
496
+ ...props
497
+ }) {
498
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props }) });
499
+ }
500
+ function TooltipTrigger({
501
+ ...props
502
+ }) {
503
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
504
+ }
505
+ function TooltipContent({
506
+ className,
507
+ sideOffset = 0,
508
+ children,
509
+ ...props
510
+ }) {
511
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TooltipPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
512
+ TooltipPrimitive.Content,
513
+ {
514
+ "data-slot": "tooltip-content",
515
+ sideOffset,
516
+ className: cn(
517
+ "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
518
+ className
519
+ ),
520
+ ...props,
521
+ children: [
522
+ children,
523
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TooltipPrimitive.Arrow, { className: "bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
524
+ ]
525
+ }
526
+ ) });
527
+ }
528
+
529
+ // src/components/ui/sidebar.tsx
530
+ var React3 = __toESM(require("react"), 1);
531
+ var import_react_slot2 = require("@radix-ui/react-slot");
532
+ var import_class_variance_authority2 = require("class-variance-authority");
533
+ var import_lucide_react2 = require("lucide-react");
534
+
535
+ // src/hooks/use-mobile.ts
536
+ var React = __toESM(require("react"), 1);
537
+ var MOBILE_BREAKPOINT = 768;
538
+ function useIsMobile() {
539
+ const [isMobile, setIsMobile] = React.useState(void 0);
540
+ React.useEffect(() => {
541
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
542
+ const onChange = () => {
543
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
544
+ };
545
+ mql.addEventListener("change", onChange);
546
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
547
+ return () => mql.removeEventListener("change", onChange);
548
+ }, []);
549
+ return !!isMobile;
550
+ }
551
+
552
+ // src/components/ui/input.tsx
553
+ var import_jsx_runtime4 = require("react/jsx-runtime");
554
+ function Input({ className, type, ...props }) {
555
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
423
556
  "input",
424
557
  {
425
558
  type,
@@ -435,326 +568,942 @@ function Input({ className, type, ...props }) {
435
568
  );
436
569
  }
437
570
 
438
- // src/components/ui/badge.tsx
439
- var import_react_slot2 = require("@radix-ui/react-slot");
440
- var import_class_variance_authority2 = require("class-variance-authority");
441
- var import_jsx_runtime4 = require("react/jsx-runtime");
442
- var badgeVariants = (0, import_class_variance_authority2.cva)(
443
- "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",
444
- {
445
- variants: {
446
- variant: {
447
- default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
448
- secondary: "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
449
- 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",
450
- outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground"
451
- }
452
- },
453
- defaultVariants: {
454
- variant: "default"
455
- }
571
+ // src/components/ui/sheet.tsx
572
+ var React2 = __toESM(require("react"), 1);
573
+ var SheetPrimitive = __toESM(require("@radix-ui/react-dialog"), 1);
574
+ var import_lucide_react = require("lucide-react");
575
+ var import_jsx_runtime5 = require("react/jsx-runtime");
576
+ function cleanupBodyStyles() {
577
+ if (typeof document !== "undefined" && document.body.style.pointerEvents === "none") {
578
+ document.body.style.pointerEvents = "";
456
579
  }
457
- );
458
- function Badge({
580
+ }
581
+ function Sheet({ open, onOpenChange, ...props }) {
582
+ const prevOpenRef = React2.useRef(open);
583
+ React2.useEffect(() => {
584
+ if (prevOpenRef.current === true && open === false) {
585
+ const timeout = setTimeout(cleanupBodyStyles, 350);
586
+ return () => clearTimeout(timeout);
587
+ }
588
+ prevOpenRef.current = open;
589
+ }, [open]);
590
+ React2.useEffect(() => {
591
+ return () => {
592
+ cleanupBodyStyles();
593
+ };
594
+ }, []);
595
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SheetPrimitive.Root, { "data-slot": "sheet", open, onOpenChange, ...props });
596
+ }
597
+ function SheetPortal({
598
+ ...props
599
+ }) {
600
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SheetPrimitive.Portal, { "data-slot": "sheet-portal", ...props });
601
+ }
602
+ function SheetOverlay({
459
603
  className,
460
- variant,
461
- asChild = false,
462
604
  ...props
463
605
  }) {
464
- const Comp = asChild ? import_react_slot2.Slot : "span";
465
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
466
- Comp,
606
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
607
+ SheetPrimitive.Overlay,
467
608
  {
468
- "data-slot": "badge",
469
- className: cn(badgeVariants({ variant }), className),
609
+ "data-slot": "sheet-overlay",
610
+ className: cn(
611
+ "fixed inset-0 z-50 bg-black/50",
612
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
613
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
614
+ "data-[state=closed]:pointer-events-none",
615
+ className
616
+ ),
470
617
  ...props
471
618
  }
472
619
  );
473
620
  }
474
-
475
- // src/components/ui/select.tsx
476
- var React = __toESM(require("react"), 1);
477
- var SelectPrimitive = __toESM(require("@radix-ui/react-select"), 1);
478
- var import_lucide_react = require("lucide-react");
479
- var import_jsx_runtime5 = require("react/jsx-runtime");
480
- var Select = SelectPrimitive.Root;
481
- var SelectValue = SelectPrimitive.Value;
482
- var SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
483
- SelectPrimitive.Trigger,
484
- {
485
- ref,
486
- className: cn(
487
- "flex h-9 w-full items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-xs outline-none transition-colors placeholder:text-muted-foreground focus:ring-2 focus:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50",
488
- className
489
- ),
490
- ...props,
491
- children: [
492
- children,
493
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.ChevronDown, { className: "h-4 w-4 opacity-50" }) })
494
- ]
495
- }
496
- ));
497
- SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
498
- var SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SelectPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
499
- SelectPrimitive.Content,
500
- {
501
- ref,
502
- className: cn(
503
- "relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
504
- position === "popper" && "translate-y-1",
505
- className
506
- ),
507
- position,
508
- ...props,
509
- children: [
510
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SelectPrimitive.ScrollUpButton, { className: "flex cursor-default items-center justify-center py-1", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.ChevronUp, { className: "h-4 w-4" }) }),
511
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
512
- SelectPrimitive.Viewport,
513
- {
514
- className: cn(
515
- "p-1",
516
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
517
- ),
518
- children
519
- }
520
- ),
521
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SelectPrimitive.ScrollDownButton, { className: "flex cursor-default items-center justify-center py-1", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.ChevronDown, { className: "h-4 w-4" }) })
522
- ]
523
- }
524
- ) }));
525
- SelectContent.displayName = SelectPrimitive.Content.displayName;
526
- var SelectItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
527
- SelectPrimitive.Item,
528
- {
529
- ref,
530
- className: cn(
531
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
532
- className
533
- ),
534
- ...props,
535
- children: [
536
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SelectPrimitive.ItemText, { children }),
537
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SelectPrimitive.ItemIndicator, { className: "absolute right-2 inline-flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.Check, { className: "h-4 w-4" }) })
538
- ]
539
- }
540
- ));
541
- SelectItem.displayName = SelectPrimitive.Item.displayName;
542
-
543
- // src/CopilotzAdmin.tsx
544
- var import_jsx_runtime6 = require("react/jsx-runtime");
545
- var CopilotzAdmin = ({
546
- config: userConfig,
547
- className
548
- }) => {
549
- const config = (0, import_react2.useMemo)(
550
- () => mergeAdminConfig(defaultAdminConfig, userConfig),
551
- [userConfig]
552
- );
553
- const [threadSearch, setThreadSearch] = (0, import_react2.useState)("");
554
- const [participantSearch, setParticipantSearch] = (0, import_react2.useState)("");
555
- const [agentSearch, setAgentSearch] = (0, import_react2.useState)("");
556
- const deferredThreadSearch = (0, import_react2.useDeferredValue)(threadSearch);
557
- const deferredParticipantSearch = (0, import_react2.useDeferredValue)(participantSearch);
558
- const deferredAgentSearch = (0, import_react2.useDeferredValue)(agentSearch);
559
- const admin = useCopilotzAdmin({
560
- baseUrl: config.baseUrl,
561
- getRequestHeaders: config.getRequestHeaders,
562
- namespace: config.namespace,
563
- range: config.initialRange,
564
- interval: config.initialInterval,
565
- threadSearch: deferredThreadSearch,
566
- participantSearch: deferredParticipantSearch,
567
- agentSearch: deferredAgentSearch
568
- });
569
- const cards = [
570
- {
571
- label: config.labels.messagesCard,
572
- value: admin.overview?.messageTotals.total ?? 0,
573
- detail: `${admin.overview?.messageTotals.toolCallMessages ?? 0} tool-call messages`
574
- },
621
+ function SheetContent({
622
+ className,
623
+ children,
624
+ side = "right",
625
+ ...props
626
+ }) {
627
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(SheetPortal, { children: [
628
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SheetOverlay, {}),
629
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
630
+ SheetPrimitive.Content,
631
+ {
632
+ "data-slot": "sheet-content",
633
+ "aria-describedby": void 0,
634
+ className: cn(
635
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
636
+ side === "right" && "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
637
+ side === "left" && "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
638
+ side === "top" && "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
639
+ side === "bottom" && "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
640
+ className
641
+ ),
642
+ ...props,
643
+ children: [
644
+ children,
645
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(SheetPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-secondary 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", children: [
646
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.XIcon, { className: "size-4" }),
647
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "sr-only", children: "Close" })
648
+ ] })
649
+ ]
650
+ }
651
+ )
652
+ ] });
653
+ }
654
+ function SheetHeader({ className, ...props }) {
655
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
656
+ "div",
575
657
  {
576
- label: config.labels.activeThreadsCard,
577
- value: admin.overview?.threadTotals.active ?? 0,
578
- detail: `${admin.overview?.threadTotals.total ?? 0} total threads`
579
- },
658
+ "data-slot": "sheet-header",
659
+ className: cn("flex flex-col gap-1.5 p-4", className),
660
+ ...props
661
+ }
662
+ );
663
+ }
664
+ function SheetTitle({
665
+ className,
666
+ ...props
667
+ }) {
668
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
669
+ SheetPrimitive.Title,
580
670
  {
581
- label: config.labels.participantsCard,
582
- value: admin.overview?.participantTotals.total ?? 0,
583
- detail: `${admin.overview?.participantTotals.agents ?? 0} agents`
584
- },
671
+ "data-slot": "sheet-title",
672
+ className: cn("text-foreground font-semibold", className),
673
+ ...props
674
+ }
675
+ );
676
+ }
677
+ function SheetDescription({
678
+ className,
679
+ ...props
680
+ }) {
681
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
682
+ SheetPrimitive.Description,
585
683
  {
586
- label: config.labels.tokensCard,
587
- value: admin.overview?.llmTotals.totalTokens ?? 0,
588
- detail: `${admin.overview?.llmTotals.totalCalls ?? 0} calls`
684
+ "data-slot": "sheet-description",
685
+ className: cn("text-muted-foreground text-sm", className),
686
+ ...props
687
+ }
688
+ );
689
+ }
690
+
691
+ // src/components/ui/sidebar.tsx
692
+ var import_jsx_runtime6 = require("react/jsx-runtime");
693
+ var SIDEBAR_COOKIE_NAME = "sidebar_state";
694
+ var SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
695
+ var SIDEBAR_WIDTH = "16rem";
696
+ var SIDEBAR_WIDTH_MOBILE = "18rem";
697
+ var SIDEBAR_WIDTH_ICON = "3rem";
698
+ var SIDEBAR_KEYBOARD_SHORTCUT = "b";
699
+ var SidebarContext = React3.createContext(null);
700
+ function useSidebar() {
701
+ const context = React3.useContext(SidebarContext);
702
+ if (!context) {
703
+ throw new Error("useSidebar must be used within a SidebarProvider.");
704
+ }
705
+ return context;
706
+ }
707
+ function SidebarProvider({
708
+ defaultOpen = true,
709
+ open: openProp,
710
+ onOpenChange: setOpenProp,
711
+ className,
712
+ style,
713
+ children,
714
+ ...props
715
+ }) {
716
+ const isMobile = useIsMobile();
717
+ const [openMobile, setOpenMobile] = React3.useState(false);
718
+ const [_open, _setOpen] = React3.useState(defaultOpen);
719
+ const open = openProp ?? _open;
720
+ const setOpen = React3.useCallback(
721
+ (value) => {
722
+ const openState = typeof value === "function" ? value(open) : value;
723
+ if (setOpenProp) {
724
+ setOpenProp(openState);
725
+ } else {
726
+ _setOpen(openState);
727
+ }
728
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
589
729
  },
730
+ [setOpenProp, open]
731
+ );
732
+ const toggleSidebar = React3.useCallback(() => {
733
+ return isMobile ? setOpenMobile((open2) => !open2) : setOpen((open2) => !open2);
734
+ }, [isMobile, setOpen, setOpenMobile]);
735
+ React3.useEffect(() => {
736
+ const handleKeyDown = (event) => {
737
+ if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
738
+ event.preventDefault();
739
+ toggleSidebar();
740
+ }
741
+ };
742
+ window.addEventListener("keydown", handleKeyDown);
743
+ return () => window.removeEventListener("keydown", handleKeyDown);
744
+ }, [toggleSidebar]);
745
+ const state = open ? "expanded" : "collapsed";
746
+ const contextValue = React3.useMemo(
747
+ () => ({
748
+ state,
749
+ open,
750
+ setOpen,
751
+ isMobile,
752
+ openMobile,
753
+ setOpenMobile,
754
+ toggleSidebar
755
+ }),
756
+ [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
757
+ );
758
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SidebarContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
759
+ "div",
590
760
  {
591
- label: config.labels.queueCard,
592
- value: admin.overview?.queueTotals.pending ?? 0,
593
- detail: `${admin.overview?.queueTotals.failed ?? 0} failed`
761
+ "data-slot": "sidebar-wrapper",
762
+ style: {
763
+ "--sidebar-width": SIDEBAR_WIDTH,
764
+ "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
765
+ ...style
766
+ },
767
+ className: cn(
768
+ "group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",
769
+ className
770
+ ),
771
+ ...props,
772
+ children
594
773
  }
595
- ];
596
- if (admin.isLoading && !admin.overview) {
597
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Card, { className: cn("border-border", className), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardContent, { className: "text-muted-foreground", children: config.labels.loading }) });
774
+ ) }) });
775
+ }
776
+ function Sidebar({
777
+ side = "left",
778
+ variant = "sidebar",
779
+ collapsible = "offcanvas",
780
+ className,
781
+ children,
782
+ ...props
783
+ }) {
784
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
785
+ if (collapsible === "none") {
786
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
787
+ "div",
788
+ {
789
+ "data-slot": "sidebar",
790
+ className: cn(
791
+ "bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col",
792
+ className
793
+ ),
794
+ ...props,
795
+ children
796
+ }
797
+ );
598
798
  }
599
- if (admin.error && !admin.overview) {
600
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Card, { className: cn("border-destructive/50 bg-destructive/10", className), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardContent, { className: "space-y-4", children: [
601
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-base font-semibold text-destructive", children: admin.error.message }),
602
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
603
- Button,
604
- {
605
- variant: "destructive",
606
- onClick: () => void admin.refresh(),
607
- children: config.labels.retry
608
- }
609
- )
610
- ] }) });
799
+ if (isMobile) {
800
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Sheet, { open: openMobile, onOpenChange: setOpenMobile, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
801
+ SheetContent,
802
+ {
803
+ "data-sidebar": "sidebar",
804
+ "data-slot": "sidebar",
805
+ "data-mobile": "true",
806
+ className: "bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden",
807
+ style: {
808
+ "--sidebar-width": SIDEBAR_WIDTH_MOBILE
809
+ },
810
+ side,
811
+ children: [
812
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(SheetHeader, { className: "sr-only", children: [
813
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SheetTitle, { children: "Sidebar" }),
814
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SheetDescription, { children: "Displays the mobile sidebar." })
815
+ ] }),
816
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex h-full w-full flex-col", children })
817
+ ]
818
+ }
819
+ ) });
611
820
  }
612
- const isEmpty = cards.every((card) => card.value === 0) && admin.activity.length === 0 && admin.threads.length === 0 && admin.participants.length === 0 && admin.agents.length === 0;
613
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Card, { className: cn("gap-0 overflow-hidden py-0", className), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardContent, { className: "space-y-6 p-6", children: [
614
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("header", { className: "flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between", children: [
615
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "space-y-1", children: [
616
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-3", children: [
617
- config.branding.logo ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl border bg-background shadow-sm", children: config.branding.logo }) : null,
618
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
619
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "text-2xl font-semibold tracking-tight", children: config.branding.title }),
620
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-muted-foreground", children: config.branding.subtitle })
621
- ] })
622
- ] }),
623
- config.namespace ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "text-xs font-medium uppercase tracking-[0.18em] text-muted-foreground", children: [
624
- "Namespace: ",
625
- config.namespace
626
- ] }) : null
627
- ] }),
628
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
821
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
822
+ "div",
823
+ {
824
+ className: "group peer text-sidebar-foreground hidden md:block",
825
+ "data-state": state,
826
+ "data-collapsible": state === "collapsed" ? collapsible : "",
827
+ "data-variant": variant,
828
+ "data-side": side,
829
+ "data-slot": "sidebar",
830
+ children: [
831
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
832
+ "div",
833
+ {
834
+ "data-slot": "sidebar-gap",
835
+ className: cn(
836
+ "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
837
+ "group-data-[collapsible=offcanvas]:w-0",
838
+ "group-data-[side=right]:rotate-180",
839
+ variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
840
+ )
841
+ }
842
+ ),
629
843
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
844
+ "div",
845
+ {
846
+ "data-slot": "sidebar-container",
847
+ className: cn(
848
+ "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
849
+ side === "left" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
850
+ variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
851
+ className
852
+ ),
853
+ ...props,
854
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
855
+ "div",
856
+ {
857
+ "data-sidebar": "sidebar",
858
+ "data-slot": "sidebar-inner",
859
+ className: "bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm",
860
+ children
861
+ }
862
+ )
863
+ }
864
+ )
865
+ ]
866
+ }
867
+ );
868
+ }
869
+ function SidebarTrigger({
870
+ className,
871
+ onClick,
872
+ ...props
873
+ }) {
874
+ const { toggleSidebar } = useSidebar();
875
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
876
+ Button,
877
+ {
878
+ "data-sidebar": "trigger",
879
+ "data-slot": "sidebar-trigger",
880
+ variant: "ghost",
881
+ size: "icon",
882
+ className: cn("size-7", className),
883
+ onClick: (event) => {
884
+ onClick?.(event);
885
+ toggleSidebar();
886
+ },
887
+ ...props,
888
+ children: [
889
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.PanelLeftIcon, {}),
890
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "sr-only", children: "Toggle Sidebar" })
891
+ ]
892
+ }
893
+ );
894
+ }
895
+ function SidebarRail({ className, ...props }) {
896
+ const { toggleSidebar } = useSidebar();
897
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
898
+ "button",
899
+ {
900
+ "data-sidebar": "rail",
901
+ "data-slot": "sidebar-rail",
902
+ "aria-label": "Toggle Sidebar",
903
+ tabIndex: -1,
904
+ onClick: toggleSidebar,
905
+ title: "Toggle Sidebar",
906
+ className: cn(
907
+ "hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex",
908
+ "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
909
+ "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
910
+ "hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
911
+ "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
912
+ "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
913
+ className
914
+ ),
915
+ ...props
916
+ }
917
+ );
918
+ }
919
+ function SidebarInset({ className, ...props }) {
920
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
921
+ "main",
922
+ {
923
+ "data-slot": "sidebar-inset",
924
+ className: cn(
925
+ "bg-background relative flex w-full flex-1 flex-col",
926
+ "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
927
+ className
928
+ ),
929
+ ...props
930
+ }
931
+ );
932
+ }
933
+ function SidebarHeader({ className, ...props }) {
934
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
935
+ "div",
936
+ {
937
+ "data-slot": "sidebar-header",
938
+ "data-sidebar": "header",
939
+ className: cn("flex flex-col gap-2 p-2", className),
940
+ ...props
941
+ }
942
+ );
943
+ }
944
+ function SidebarFooter({ className, ...props }) {
945
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
946
+ "div",
947
+ {
948
+ "data-slot": "sidebar-footer",
949
+ "data-sidebar": "footer",
950
+ className: cn("flex flex-col gap-2 p-2", className),
951
+ ...props
952
+ }
953
+ );
954
+ }
955
+ function SidebarContent({ className, ...props }) {
956
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
957
+ "div",
958
+ {
959
+ "data-slot": "sidebar-content",
960
+ "data-sidebar": "content",
961
+ className: cn(
962
+ "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
963
+ className
964
+ ),
965
+ ...props
966
+ }
967
+ );
968
+ }
969
+ function SidebarGroup({ className, ...props }) {
970
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
971
+ "div",
972
+ {
973
+ "data-slot": "sidebar-group",
974
+ "data-sidebar": "group",
975
+ className: cn("relative flex w-full min-w-0 flex-col p-2", className),
976
+ ...props
977
+ }
978
+ );
979
+ }
980
+ function SidebarGroupLabel({
981
+ className,
982
+ asChild = false,
983
+ ...props
984
+ }) {
985
+ const Comp = asChild ? import_react_slot2.Slot : "div";
986
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
987
+ Comp,
988
+ {
989
+ "data-slot": "sidebar-group-label",
990
+ "data-sidebar": "group-label",
991
+ className: cn(
992
+ "text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
993
+ "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
994
+ className
995
+ ),
996
+ ...props
997
+ }
998
+ );
999
+ }
1000
+ function SidebarGroupContent({
1001
+ className,
1002
+ ...props
1003
+ }) {
1004
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1005
+ "div",
1006
+ {
1007
+ "data-slot": "sidebar-group-content",
1008
+ "data-sidebar": "group-content",
1009
+ className: cn("w-full text-sm", className),
1010
+ ...props
1011
+ }
1012
+ );
1013
+ }
1014
+ function SidebarMenu({ className, ...props }) {
1015
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1016
+ "ul",
1017
+ {
1018
+ "data-slot": "sidebar-menu",
1019
+ "data-sidebar": "menu",
1020
+ className: cn("flex w-full min-w-0 flex-col gap-1", className),
1021
+ ...props
1022
+ }
1023
+ );
1024
+ }
1025
+ function SidebarMenuItem({ className, ...props }) {
1026
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1027
+ "li",
1028
+ {
1029
+ "data-slot": "sidebar-menu-item",
1030
+ "data-sidebar": "menu-item",
1031
+ className: cn("group/menu-item relative", className),
1032
+ ...props
1033
+ }
1034
+ );
1035
+ }
1036
+ var sidebarMenuButtonVariants = (0, import_class_variance_authority2.cva)(
1037
+ "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
1038
+ {
1039
+ variants: {
1040
+ variant: {
1041
+ default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
1042
+ outline: "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]"
1043
+ },
1044
+ size: {
1045
+ default: "h-8 text-sm",
1046
+ sm: "h-7 text-xs",
1047
+ lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!"
1048
+ }
1049
+ },
1050
+ defaultVariants: {
1051
+ variant: "default",
1052
+ size: "default"
1053
+ }
1054
+ }
1055
+ );
1056
+ function SidebarMenuButton({
1057
+ asChild = false,
1058
+ isActive = false,
1059
+ variant = "default",
1060
+ size = "default",
1061
+ tooltip,
1062
+ className,
1063
+ ...props
1064
+ }) {
1065
+ const Comp = asChild ? import_react_slot2.Slot : "button";
1066
+ const { isMobile, state } = useSidebar();
1067
+ const button = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1068
+ Comp,
1069
+ {
1070
+ "data-slot": "sidebar-menu-button",
1071
+ "data-sidebar": "menu-button",
1072
+ "data-size": size,
1073
+ "data-active": isActive,
1074
+ className: cn(sidebarMenuButtonVariants({ variant, size }), className),
1075
+ ...props
1076
+ }
1077
+ );
1078
+ if (!tooltip) {
1079
+ return button;
1080
+ }
1081
+ if (typeof tooltip === "string") {
1082
+ tooltip = {
1083
+ children: tooltip
1084
+ };
1085
+ }
1086
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Tooltip, { children: [
1087
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TooltipTrigger, { asChild: true, children: button }),
1088
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1089
+ TooltipContent,
1090
+ {
1091
+ side: "right",
1092
+ align: "center",
1093
+ hidden: state !== "collapsed" || isMobile,
1094
+ ...tooltip
1095
+ }
1096
+ )
1097
+ ] });
1098
+ }
1099
+
1100
+ // src/components/layout/AdminSidebar.tsx
1101
+ var import_lucide_react3 = require("lucide-react");
1102
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1103
+ var NAV_ITEMS = [
1104
+ {
1105
+ page: "dashboard",
1106
+ label: "Dashboard",
1107
+ icon: import_lucide_react3.LayoutDashboard
1108
+ },
1109
+ {
1110
+ page: "threads",
1111
+ label: "Threads",
1112
+ icon: import_lucide_react3.MessageSquare,
1113
+ featureKey: "showThreads"
1114
+ },
1115
+ {
1116
+ page: "participants",
1117
+ label: "Participants",
1118
+ icon: import_lucide_react3.Users,
1119
+ featureKey: "showParticipants"
1120
+ },
1121
+ {
1122
+ page: "agents",
1123
+ label: "Agents",
1124
+ icon: import_lucide_react3.Bot,
1125
+ featureKey: "showAgents"
1126
+ },
1127
+ {
1128
+ page: "events",
1129
+ label: "Events",
1130
+ icon: import_lucide_react3.Activity,
1131
+ featureKey: "showEvents"
1132
+ }
1133
+ ];
1134
+ var AdminSidebar = ({
1135
+ config,
1136
+ currentPage,
1137
+ onNavigate
1138
+ }) => {
1139
+ const visibleItems = NAV_ITEMS.filter(
1140
+ (item) => !item.featureKey || config.features[item.featureKey]
1141
+ );
1142
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Sidebar, { collapsible: config.sidebar.collapsible, children: [
1143
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SidebarHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-3 px-2 py-3", children: [
1144
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex items-center justify-center shrink-0", children: config.branding.logo || /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-primary text-primary-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.LayoutDashboard, { className: "h-4 w-4" }) }) }),
1145
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col min-w-0 group-data-[collapsible=icon]:hidden", children: [
1146
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-sm font-semibold truncate", children: config.branding.title }),
1147
+ config.branding.subtitle && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-xs text-muted-foreground truncate", children: config.branding.subtitle })
1148
+ ] })
1149
+ ] }) }),
1150
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SidebarContent, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(SidebarGroup, { children: [
1151
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SidebarGroupLabel, { className: "group-data-[collapsible=icon]:hidden", children: "Navigation" }),
1152
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SidebarGroupContent, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SidebarMenu, { children: visibleItems.map((item) => {
1153
+ const Icon2 = item.icon;
1154
+ const label = config.labels[`${item.page}Title`] || item.label;
1155
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SidebarMenuItem, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1156
+ SidebarMenuButton,
1157
+ {
1158
+ isActive: currentPage === item.page,
1159
+ onClick: () => onNavigate(item.page),
1160
+ tooltip: label,
1161
+ children: [
1162
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon2, {}),
1163
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: label })
1164
+ ]
1165
+ }
1166
+ ) }, item.page);
1167
+ }) }) })
1168
+ ] }) }),
1169
+ config.namespace && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SidebarFooter, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "px-2 py-2 text-xs text-muted-foreground group-data-[collapsible=icon]:hidden", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-medium uppercase tracking-[0.18em]", children: config.namespace }) }) }),
1170
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SidebarRail, {})
1171
+ ] });
1172
+ };
1173
+
1174
+ // src/components/layout/AdminHeader.tsx
1175
+ var import_lucide_react5 = require("lucide-react");
1176
+
1177
+ // src/components/ui/select.tsx
1178
+ var React4 = __toESM(require("react"), 1);
1179
+ var SelectPrimitive = __toESM(require("@radix-ui/react-select"), 1);
1180
+ var import_lucide_react4 = require("lucide-react");
1181
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1182
+ var Select = SelectPrimitive.Root;
1183
+ var SelectValue = SelectPrimitive.Value;
1184
+ var SelectTrigger = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1185
+ SelectPrimitive.Trigger,
1186
+ {
1187
+ ref,
1188
+ className: cn(
1189
+ "flex h-9 w-full items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-xs outline-none transition-colors placeholder:text-muted-foreground focus:ring-2 focus:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50",
1190
+ className
1191
+ ),
1192
+ ...props,
1193
+ children: [
1194
+ children,
1195
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.ChevronDown, { className: "h-4 w-4 opacity-50" }) })
1196
+ ]
1197
+ }
1198
+ ));
1199
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
1200
+ var SelectContent = React4.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1201
+ SelectPrimitive.Content,
1202
+ {
1203
+ ref,
1204
+ className: cn(
1205
+ "relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
1206
+ position === "popper" && "translate-y-1",
1207
+ className
1208
+ ),
1209
+ position,
1210
+ ...props,
1211
+ children: [
1212
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectPrimitive.ScrollUpButton, { className: "flex cursor-default items-center justify-center py-1", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.ChevronUp, { className: "h-4 w-4" }) }),
1213
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1214
+ SelectPrimitive.Viewport,
1215
+ {
1216
+ className: cn(
1217
+ "p-1",
1218
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
1219
+ ),
1220
+ children
1221
+ }
1222
+ ),
1223
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectPrimitive.ScrollDownButton, { className: "flex cursor-default items-center justify-center py-1", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.ChevronDown, { className: "h-4 w-4" }) })
1224
+ ]
1225
+ }
1226
+ ) }));
1227
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
1228
+ var SelectItem = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1229
+ SelectPrimitive.Item,
1230
+ {
1231
+ ref,
1232
+ className: cn(
1233
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
1234
+ className
1235
+ ),
1236
+ ...props,
1237
+ children: [
1238
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectPrimitive.ItemText, { children }),
1239
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SelectPrimitive.ItemIndicator, { className: "absolute right-2 inline-flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.Check, { className: "h-4 w-4" }) })
1240
+ ]
1241
+ }
1242
+ ));
1243
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
1244
+
1245
+ // src/components/layout/AdminHeader.tsx
1246
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1247
+ var PAGE_TITLES = {
1248
+ dashboard: "Dashboard",
1249
+ threads: "Threads",
1250
+ "thread-detail": "Thread Detail",
1251
+ participants: "Participants",
1252
+ agents: "Agents",
1253
+ events: "Events"
1254
+ };
1255
+ var AdminHeader = ({
1256
+ config,
1257
+ currentPage,
1258
+ range,
1259
+ interval,
1260
+ onRangeChange,
1261
+ onIntervalChange,
1262
+ onRefresh,
1263
+ isLoading
1264
+ }) => {
1265
+ const pageTitle = config.labels[`${currentPage}Title`] || PAGE_TITLES[currentPage];
1266
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Card, { className: "py-0 border-b rounded-none relative z-10 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CardHeader, { className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
1267
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-1", children: [
1268
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Tooltip, { children: [
1269
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SidebarTrigger, { className: "-ml-1" }) }),
1270
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TooltipContent, { children: "Toggle Sidebar" })
1271
+ ] }),
1272
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: "text-sm font-medium ml-2", children: pageTitle })
1273
+ ] }),
1274
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex-1" }),
1275
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-1", children: [
1276
+ currentPage === "dashboard" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1277
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
630
1278
  Button,
631
1279
  {
632
- variant: admin.filters.range === "24h" ? "default" : "outline",
1280
+ variant: range === "24h" ? "default" : "ghost",
633
1281
  size: "sm",
634
- onClick: () => admin.setRange("24h"),
1282
+ className: "h-7 text-xs",
1283
+ onClick: () => onRangeChange("24h"),
635
1284
  children: config.labels.range24h
636
1285
  }
637
1286
  ),
638
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1287
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
639
1288
  Button,
640
1289
  {
641
- variant: admin.filters.range === "7d" ? "default" : "outline",
1290
+ variant: range === "7d" ? "default" : "ghost",
642
1291
  size: "sm",
643
- onClick: () => admin.setRange("7d"),
1292
+ className: "h-7 text-xs",
1293
+ onClick: () => onRangeChange("7d"),
644
1294
  children: config.labels.range7d
645
1295
  }
646
1296
  ),
647
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1297
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
648
1298
  Button,
649
1299
  {
650
- variant: admin.filters.range === "30d" ? "default" : "outline",
1300
+ variant: range === "30d" ? "default" : "ghost",
651
1301
  size: "sm",
652
- onClick: () => admin.setRange("30d"),
1302
+ className: "h-7 text-xs",
1303
+ onClick: () => onRangeChange("30d"),
653
1304
  children: config.labels.range30d
654
1305
  }
655
1306
  ),
656
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1307
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
657
1308
  Select,
658
1309
  {
659
- value: admin.filters.interval,
660
- onValueChange: (v) => admin.setInterval(v),
1310
+ value: interval,
1311
+ onValueChange: (v) => onIntervalChange(v),
661
1312
  children: [
662
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SelectTrigger, { className: "h-8 w-[110px]", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SelectValue, {}) }),
663
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(SelectContent, { children: [
664
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SelectItem, { value: "hour", children: config.labels.intervalHour }),
665
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SelectItem, { value: "day", children: config.labels.intervalDay })
1313
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectTrigger, { className: "h-7 w-[90px] text-xs", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectValue, {}) }),
1314
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(SelectContent, { children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectItem, { value: "hour", children: config.labels.intervalHour }),
1316
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectItem, { value: "day", children: config.labels.intervalDay })
666
1317
  ] })
667
1318
  ]
668
1319
  }
669
- ),
670
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1320
+ )
1321
+ ] }),
1322
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Tooltip, { children: [
1323
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
671
1324
  Button,
672
1325
  {
673
- variant: "outline",
674
- size: "sm",
675
- onClick: () => void admin.refresh(),
676
- children: config.labels.refresh
1326
+ variant: "ghost",
1327
+ size: "icon",
1328
+ className: "h-7 w-7",
1329
+ onClick: onRefresh,
1330
+ disabled: isLoading,
1331
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1332
+ import_lucide_react5.RefreshCw,
1333
+ {
1334
+ className: `h-4 w-4 ${isLoading ? "animate-spin" : ""}`
1335
+ }
1336
+ )
677
1337
  }
678
- ),
679
- config.branding.actions
680
- ] })
1338
+ ) }),
1339
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(TooltipContent, { children: config.labels.refresh })
1340
+ ] }),
1341
+ config.branding.actions
1342
+ ] })
1343
+ ] }) }) });
1344
+ };
1345
+
1346
+ // src/components/ui/badge.tsx
1347
+ var import_react_slot3 = require("@radix-ui/react-slot");
1348
+ var import_class_variance_authority3 = require("class-variance-authority");
1349
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1350
+ var badgeVariants = (0, import_class_variance_authority3.cva)(
1351
+ "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",
1352
+ {
1353
+ variants: {
1354
+ variant: {
1355
+ default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
1356
+ secondary: "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
1357
+ 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",
1358
+ outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground"
1359
+ }
1360
+ },
1361
+ defaultVariants: {
1362
+ variant: "default"
1363
+ }
1364
+ }
1365
+ );
1366
+ function Badge({
1367
+ className,
1368
+ variant,
1369
+ asChild = false,
1370
+ ...props
1371
+ }) {
1372
+ const Comp = asChild ? import_react_slot3.Slot : "span";
1373
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1374
+ Comp,
1375
+ {
1376
+ "data-slot": "badge",
1377
+ className: cn(badgeVariants({ variant }), className),
1378
+ ...props
1379
+ }
1380
+ );
1381
+ }
1382
+
1383
+ // src/components/views/DashboardView.tsx
1384
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1385
+ var DashboardView = ({
1386
+ config,
1387
+ overview,
1388
+ activity,
1389
+ threads,
1390
+ participants,
1391
+ agents,
1392
+ interval,
1393
+ threadSearch,
1394
+ participantSearch,
1395
+ agentSearch,
1396
+ onThreadSearchChange,
1397
+ onParticipantSearchChange,
1398
+ onAgentSearchChange,
1399
+ onThreadClick
1400
+ }) => {
1401
+ const cards = [
1402
+ {
1403
+ label: config.labels.messagesCard,
1404
+ value: overview?.messageTotals.total ?? 0,
1405
+ detail: `${overview?.messageTotals.toolCallMessages ?? 0} tool-call messages`
1406
+ },
1407
+ {
1408
+ label: config.labels.activeThreadsCard,
1409
+ value: overview?.threadTotals.active ?? 0,
1410
+ detail: `${overview?.threadTotals.total ?? 0} total threads`
1411
+ },
1412
+ {
1413
+ label: config.labels.participantsCard,
1414
+ value: overview?.participantTotals.total ?? 0,
1415
+ detail: `${overview?.participantTotals.agents ?? 0} agents`
1416
+ },
1417
+ {
1418
+ label: config.labels.tokensCard,
1419
+ value: overview?.llmTotals.totalTokens ?? 0,
1420
+ detail: `${overview?.llmTotals.totalCalls ?? 0} calls`
1421
+ },
1422
+ {
1423
+ label: config.labels.queueCard,
1424
+ value: overview?.queueTotals.pending ?? 0,
1425
+ detail: `${overview?.queueTotals.failed ?? 0} failed`
1426
+ }
1427
+ ];
1428
+ const isEmpty = cards.every((card) => card.value === 0) && activity.length === 0 && threads.length === 0 && participants.length === 0 && agents.length === 0;
1429
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "space-y-6", children: [
1430
+ isEmpty && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rounded-xl border border-dashed p-10 text-center", children: [
1431
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "text-lg font-semibold", children: config.labels.emptyTitle }),
1432
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "mt-2 text-sm text-muted-foreground", children: config.labels.emptyDescription })
681
1433
  ] }),
682
- isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rounded-xl border border-dashed p-10 text-center", children: [
683
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { className: "text-lg font-semibold", children: config.labels.emptyTitle }),
684
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-2 text-sm text-muted-foreground", children: config.labels.emptyDescription })
685
- ] }) : null,
686
- config.features.showOverview ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "space-y-4", children: [
687
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionHeading, { title: config.labels.overviewTitle }),
688
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-5", children: cards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1434
+ config.features.showOverview && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { className: "space-y-4", children: [
1435
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SectionHeading, { title: config.labels.overviewTitle }),
1436
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-5", children: cards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
689
1437
  "div",
690
1438
  {
691
- className: "rounded-xl border bg-background p-5 shadow-sm",
1439
+ className: "rounded-xl border bg-card p-5 shadow-sm",
692
1440
  children: [
693
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm font-medium text-muted-foreground", children: card.label }),
694
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-3 text-3xl font-semibold tracking-tight", children: formatNumber(card.value) }),
695
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-2 text-xs text-muted-foreground", children: card.detail })
1441
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm font-medium text-muted-foreground", children: card.label }),
1442
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "mt-3 text-3xl font-semibold tracking-tight", children: formatNumber(card.value) }),
1443
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "mt-2 text-xs text-muted-foreground", children: card.detail })
696
1444
  ]
697
1445
  },
698
1446
  card.label
699
1447
  )) })
700
- ] }) : null,
701
- config.features.showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "space-y-4", children: [
702
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionHeading, { title: config.labels.activityTitle }),
703
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1448
+ ] }),
1449
+ config.features.showActivity && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { className: "space-y-4", children: [
1450
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SectionHeading, { title: config.labels.activityTitle }),
1451
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
704
1452
  ActivityChart,
705
1453
  {
706
- interval: admin.filters.interval,
1454
+ interval,
707
1455
  labels: config.labels,
708
1456
  maxBars: config.ui.maxActivityBars,
709
- points: admin.activity
1457
+ points: activity
710
1458
  }
711
1459
  )
712
- ] }) : null,
713
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid gap-6 lg:grid-cols-3", children: [
714
- config.features.showThreads ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1460
+ ] }),
1461
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "grid gap-6 lg:grid-cols-3", children: [
1462
+ config.features.showThreads && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
715
1463
  DataTable,
716
1464
  {
717
- rows: admin.threads,
1465
+ rows: threads,
718
1466
  searchPlaceholder: config.labels.threadSearchPlaceholder,
719
1467
  searchValue: threadSearch,
720
- setSearchValue: setThreadSearch,
1468
+ setSearchValue: onThreadSearchChange,
721
1469
  title: config.labels.threadsTitle,
722
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ThreadsTable, { rows: admin.threads, labels: config.labels })
1470
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1471
+ ThreadsTable,
1472
+ {
1473
+ rows: threads,
1474
+ labels: config.labels,
1475
+ onThreadClick
1476
+ }
1477
+ )
723
1478
  }
724
- ) : null,
725
- config.features.showParticipants ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1479
+ ),
1480
+ config.features.showParticipants && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
726
1481
  DataTable,
727
1482
  {
728
- rows: admin.participants,
1483
+ rows: participants,
729
1484
  searchPlaceholder: config.labels.participantSearchPlaceholder,
730
1485
  searchValue: participantSearch,
731
- setSearchValue: setParticipantSearch,
1486
+ setSearchValue: onParticipantSearchChange,
732
1487
  title: config.labels.participantsTitle,
733
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
734
- ParticipantsTable,
735
- {
736
- rows: admin.participants,
737
- labels: config.labels
738
- }
739
- )
1488
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ParticipantsTable, { rows: participants, labels: config.labels })
740
1489
  }
741
- ) : null,
742
- config.features.showAgents ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1490
+ ),
1491
+ config.features.showAgents && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
743
1492
  DataTable,
744
1493
  {
745
- rows: admin.agents,
1494
+ rows: agents,
746
1495
  searchPlaceholder: config.labels.agentSearchPlaceholder,
747
1496
  searchValue: agentSearch,
748
- setSearchValue: setAgentSearch,
1497
+ setSearchValue: onAgentSearchChange,
749
1498
  title: config.labels.agentsTitle,
750
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AgentsTable, { rows: admin.agents, labels: config.labels })
1499
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentsTable, { rows: agents, labels: config.labels })
751
1500
  }
752
- ) : null
1501
+ )
753
1502
  ] })
754
- ] }) });
1503
+ ] });
755
1504
  };
756
1505
  function SectionHeading({ title }) {
757
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { className: "text-lg font-semibold tracking-tight", children: title });
1506
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "text-lg font-semibold tracking-tight", children: title });
758
1507
  }
759
1508
  function ActivityChart(props) {
760
1509
  const trimmedPoints = props.points.slice(-props.maxBars);
@@ -762,12 +1511,12 @@ function ActivityChart(props) {
762
1511
  ...trimmedPoints.map((point) => point.messageCount),
763
1512
  1
764
1513
  );
765
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rounded-xl border bg-background p-5 shadow-sm", children: trimmedPoints.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-muted-foreground", children: props.labels.noResults }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex min-h-48 items-end gap-2", children: trimmedPoints.map((point) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1514
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "rounded-xl border bg-card p-5 shadow-sm", children: trimmedPoints.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-muted-foreground", children: props.labels.noResults }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex min-h-48 items-end gap-2", children: trimmedPoints.map((point) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
766
1515
  "div",
767
1516
  {
768
1517
  className: "flex min-w-0 flex-1 flex-col items-center gap-2",
769
1518
  children: [
770
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex h-36 w-full items-end rounded-lg bg-muted px-1 pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1519
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex h-36 w-full items-end rounded-lg bg-muted px-1 pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
771
1520
  "div",
772
1521
  {
773
1522
  className: "w-full rounded-md bg-primary transition-all",
@@ -776,9 +1525,9 @@ function ActivityChart(props) {
776
1525
  }
777
1526
  }
778
1527
  ) }),
779
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-center", children: [
780
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs font-medium", children: formatBucket(point.bucket, props.interval) }),
781
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "text-[11px] text-muted-foreground", children: [
1528
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-center", children: [
1529
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs font-medium", children: formatBucket(point.bucket, props.interval) }),
1530
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-[11px] text-muted-foreground", children: [
782
1531
  formatNumber(point.messageCount),
783
1532
  " msg"
784
1533
  ] })
@@ -789,10 +1538,10 @@ function ActivityChart(props) {
789
1538
  )) }) });
790
1539
  }
791
1540
  function DataTable(props) {
792
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rounded-xl border bg-background p-5 shadow-sm", children: [
793
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mb-4 flex items-center justify-between gap-3", children: [
794
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionHeading, { title: props.title }),
795
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1541
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "rounded-xl border bg-card p-5 shadow-sm", children: [
1542
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mb-4 flex items-center justify-between gap-3", children: [
1543
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SectionHeading, { title: props.title }),
1544
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
796
1545
  Input,
797
1546
  {
798
1547
  className: "h-8 w-full max-w-44",
@@ -802,24 +1551,29 @@ function DataTable(props) {
802
1551
  }
803
1552
  )
804
1553
  ] }),
805
- props.rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-muted-foreground", children: "No results" }) : props.children
1554
+ props.rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-muted-foreground", children: "No results" }) : props.children
806
1555
  ] });
807
1556
  }
808
1557
  function ThreadsTable({
809
1558
  rows,
810
- labels
1559
+ labels,
1560
+ onThreadClick
811
1561
  }) {
812
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "space-y-3", children: rows.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1562
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "space-y-3", children: rows.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
813
1563
  "div",
814
1564
  {
815
- className: "rounded-lg border bg-muted/50 p-4",
1565
+ className: cn(
1566
+ "rounded-lg border bg-muted/50 p-4",
1567
+ onThreadClick && "cursor-pointer hover:bg-muted transition-colors"
1568
+ ),
1569
+ onClick: () => onThreadClick?.(thread.threadId),
816
1570
  children: [
817
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
818
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-w-0", children: [
819
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "font-medium", children: thread.name }),
820
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-1 truncate text-xs text-muted-foreground", children: thread.summary ?? thread.lastMessagePreview ?? "No summary yet" })
1571
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
1572
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "min-w-0", children: [
1573
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "font-medium", children: thread.name }),
1574
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "mt-1 truncate text-xs text-muted-foreground", children: thread.summary ?? thread.lastMessagePreview ?? "No summary yet" })
821
1575
  ] }),
822
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1576
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
823
1577
  Badge,
824
1578
  {
825
1579
  variant: thread.status === "archived" ? "secondary" : "default",
@@ -827,16 +1581,16 @@ function ThreadsTable({
827
1581
  }
828
1582
  )
829
1583
  ] }),
830
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
831
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1584
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
1585
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
832
1586
  formatNumber(thread.messageCount),
833
1587
  " messages"
834
1588
  ] }),
835
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1589
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
836
1590
  thread.participantIds.length,
837
1591
  " participants"
838
1592
  ] }),
839
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: formatDate(thread.lastActivityAt) })
1593
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: formatDate(thread.lastActivityAt) })
840
1594
  ] })
841
1595
  ]
842
1596
  },
@@ -847,28 +1601,28 @@ function ParticipantsTable({
847
1601
  rows,
848
1602
  labels
849
1603
  }) {
850
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "space-y-3", children: rows.map((participant) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1604
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "space-y-3", children: rows.map((participant) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
851
1605
  "div",
852
1606
  {
853
1607
  className: "rounded-lg border bg-muted/50 p-4",
854
1608
  children: [
855
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
856
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-w-0", children: [
857
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "font-medium", children: participant.displayName }),
858
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-1 text-xs uppercase tracking-[0.18em] text-muted-foreground", children: participant.participantType })
1609
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
1610
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "min-w-0", children: [
1611
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "font-medium", children: participant.displayName }),
1612
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "mt-1 text-xs uppercase tracking-[0.18em] text-muted-foreground", children: participant.participantType })
859
1613
  ] }),
860
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Badge, { variant: "outline", children: participant.isGlobal ? labels.scopeGlobal : labels.scopeScoped })
1614
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Badge, { variant: "outline", children: participant.isGlobal ? labels.scopeGlobal : labels.scopeScoped })
861
1615
  ] }),
862
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
863
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1616
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
1617
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
864
1618
  formatNumber(participant.messageCount),
865
1619
  " messages"
866
1620
  ] }),
867
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1621
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
868
1622
  formatNumber(participant.threadCount),
869
1623
  " threads"
870
1624
  ] }),
871
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: formatDate(participant.lastActivityAt) })
1625
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: formatDate(participant.lastActivityAt) })
872
1626
  ] })
873
1627
  ]
874
1628
  },
@@ -879,32 +1633,32 @@ function AgentsTable({
879
1633
  rows,
880
1634
  labels
881
1635
  }) {
882
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "space-y-3", children: rows.map((agent) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1636
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "space-y-3", children: rows.map((agent) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
883
1637
  "div",
884
1638
  {
885
1639
  className: "rounded-lg border bg-muted/50 p-4",
886
1640
  children: [
887
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
888
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-w-0", children: [
889
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "font-medium", children: agent.displayName }),
890
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-1 truncate text-xs text-muted-foreground", children: agent.description ?? agent.agentId })
1641
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
1642
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "min-w-0", children: [
1643
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "font-medium", children: agent.displayName }),
1644
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "mt-1 truncate text-xs text-muted-foreground", children: agent.description ?? agent.agentId })
891
1645
  ] }),
892
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Badge, { variant: "outline", children: agent.isConfigured ? labels.configured : labels.unconfigured })
1646
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Badge, { variant: "outline", children: agent.isConfigured ? labels.configured : labels.unconfigured })
893
1647
  ] }),
894
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3 grid grid-cols-2 gap-2 text-xs text-muted-foreground", children: [
895
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1648
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mt-3 grid grid-cols-2 gap-2 text-xs text-muted-foreground", children: [
1649
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
896
1650
  formatNumber(agent.messageCount),
897
1651
  " messages"
898
1652
  ] }),
899
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1653
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
900
1654
  formatNumber(agent.llmCallCount),
901
1655
  " LLM calls"
902
1656
  ] }),
903
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1657
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
904
1658
  formatNumber(agent.toolCallMessageCount),
905
1659
  " tool calls"
906
1660
  ] }),
907
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1661
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
908
1662
  formatNumber(agent.totalTokens),
909
1663
  " tokens"
910
1664
  ] })
@@ -940,6 +1694,476 @@ function formatDate(value) {
940
1694
  function formatNumber(value) {
941
1695
  return new Intl.NumberFormat().format(value);
942
1696
  }
1697
+
1698
+ // src/components/views/ThreadsView.tsx
1699
+ var import_lucide_react6 = require("lucide-react");
1700
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1701
+ var ThreadsView = ({
1702
+ config,
1703
+ threads,
1704
+ searchValue,
1705
+ onSearchChange,
1706
+ onThreadClick
1707
+ }) => {
1708
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-4", children: [
1709
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative max-w-sm", children: [
1710
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.Search, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
1711
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1712
+ Input,
1713
+ {
1714
+ className: "pl-9",
1715
+ placeholder: config.labels.threadSearchPlaceholder,
1716
+ value: searchValue,
1717
+ onChange: (e) => onSearchChange(e.target.value)
1718
+ }
1719
+ )
1720
+ ] }),
1721
+ threads.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "rounded-xl border border-dashed p-10 text-center", children: [
1722
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.MessageSquare, { className: "mx-auto h-8 w-8 text-muted-foreground/50" }),
1723
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "mt-3 text-sm text-muted-foreground", children: searchValue ? config.labels.noResults : config.labels.emptyDescription })
1724
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "space-y-2", children: threads.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1725
+ "div",
1726
+ {
1727
+ className: "flex items-center gap-4 rounded-lg border bg-card p-4 transition-colors hover:bg-muted/50 cursor-pointer",
1728
+ onClick: () => onThreadClick?.(thread.threadId),
1729
+ children: [
1730
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex-1 min-w-0", children: [
1731
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2", children: [
1732
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "font-medium truncate", children: thread.name }),
1733
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1734
+ Badge,
1735
+ {
1736
+ variant: thread.status === "archived" ? "secondary" : "default",
1737
+ className: "shrink-0",
1738
+ children: thread.status === "archived" ? config.labels.statusArchived : config.labels.statusActive
1739
+ }
1740
+ )
1741
+ ] }),
1742
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "mt-1 text-sm text-muted-foreground truncate", children: thread.summary ?? thread.lastMessagePreview ?? "No summary yet" })
1743
+ ] }),
1744
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "text-right text-xs text-muted-foreground shrink-0 space-y-1", children: [
1745
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { children: [
1746
+ formatNumber2(thread.messageCount),
1747
+ " messages"
1748
+ ] }),
1749
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { children: [
1750
+ thread.participantIds.length,
1751
+ " participants"
1752
+ ] }),
1753
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { children: formatDate2(thread.lastActivityAt) })
1754
+ ] })
1755
+ ]
1756
+ },
1757
+ thread.threadId
1758
+ )) })
1759
+ ] });
1760
+ };
1761
+ function formatDate2(value) {
1762
+ if (!value) return "No activity";
1763
+ const date = new Date(value);
1764
+ if (Number.isNaN(date.getTime())) return value;
1765
+ return date.toLocaleString(void 0, {
1766
+ month: "short",
1767
+ day: "numeric",
1768
+ hour: "numeric",
1769
+ minute: "2-digit"
1770
+ });
1771
+ }
1772
+ function formatNumber2(value) {
1773
+ return new Intl.NumberFormat().format(value);
1774
+ }
1775
+
1776
+ // src/components/views/ThreadDetailView.tsx
1777
+ var import_react2 = require("react");
1778
+ var import_lucide_react7 = require("lucide-react");
1779
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1780
+ var MESSAGES_PAGE_SIZE = 50;
1781
+ var ThreadDetailView = ({
1782
+ threadId,
1783
+ config,
1784
+ onBack
1785
+ }) => {
1786
+ const [thread, setThread] = (0, import_react2.useState)(null);
1787
+ const [messages, setMessages] = (0, import_react2.useState)([]);
1788
+ const [pageInfo, setPageInfo] = (0, import_react2.useState)(null);
1789
+ const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
1790
+ const [isLoadingMore, setIsLoadingMore] = (0, import_react2.useState)(false);
1791
+ const [error, setError] = (0, import_react2.useState)(null);
1792
+ const fetchOptions = {
1793
+ baseUrl: config.baseUrl,
1794
+ getRequestHeaders: config.getRequestHeaders
1795
+ };
1796
+ const loadInitial = (0, import_react2.useCallback)(async () => {
1797
+ setIsLoading(true);
1798
+ setError(null);
1799
+ try {
1800
+ const [threadData, messagesData] = await Promise.all([
1801
+ fetchThreadDetail(threadId, fetchOptions),
1802
+ fetchThreadMessages(threadId, { limit: MESSAGES_PAGE_SIZE }, fetchOptions)
1803
+ ]);
1804
+ setThread(threadData);
1805
+ setMessages(messagesData.data);
1806
+ setPageInfo(messagesData.pageInfo);
1807
+ } catch (err) {
1808
+ setError(
1809
+ err instanceof Error ? err : new Error("Failed to load thread")
1810
+ );
1811
+ } finally {
1812
+ setIsLoading(false);
1813
+ }
1814
+ }, [threadId, config.baseUrl, config.getRequestHeaders]);
1815
+ (0, import_react2.useEffect)(() => {
1816
+ void loadInitial();
1817
+ }, [loadInitial]);
1818
+ const loadMore = (0, import_react2.useCallback)(async () => {
1819
+ if (!pageInfo?.hasMoreBefore || !pageInfo.oldestMessageId || isLoadingMore) {
1820
+ return;
1821
+ }
1822
+ setIsLoadingMore(true);
1823
+ try {
1824
+ const older = await fetchThreadMessages(
1825
+ threadId,
1826
+ { limit: MESSAGES_PAGE_SIZE, before: pageInfo.oldestMessageId },
1827
+ fetchOptions
1828
+ );
1829
+ setMessages((prev) => [...older.data, ...prev]);
1830
+ setPageInfo(older.pageInfo);
1831
+ } catch {
1832
+ } finally {
1833
+ setIsLoadingMore(false);
1834
+ }
1835
+ }, [threadId, pageInfo, isLoadingMore, config.baseUrl, config.getRequestHeaders]);
1836
+ if (isLoading) {
1837
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex items-center justify-center py-20", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) });
1838
+ }
1839
+ if (error) {
1840
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "space-y-4 py-10 text-center", children: [
1841
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-destructive font-medium", children: error.message }),
1842
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex justify-center gap-2", children: [
1843
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Button, { variant: "outline", onClick: onBack, children: [
1844
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.ArrowLeft, { className: "mr-2 h-4 w-4" }),
1845
+ "Back"
1846
+ ] }),
1847
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "destructive", onClick: () => void loadInitial(), children: config.labels.retry })
1848
+ ] })
1849
+ ] });
1850
+ }
1851
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "space-y-6", children: [
1852
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-start gap-4", children: [
1853
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1854
+ Button,
1855
+ {
1856
+ variant: "ghost",
1857
+ size: "icon",
1858
+ className: "mt-1 shrink-0",
1859
+ onClick: onBack,
1860
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.ArrowLeft, { className: "h-4 w-4" })
1861
+ }
1862
+ ),
1863
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 min-w-0", children: [
1864
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
1865
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "text-xl font-semibold truncate", children: thread?.name ?? threadId }),
1866
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1867
+ Badge,
1868
+ {
1869
+ variant: thread?.status === "archived" ? "secondary" : "default",
1870
+ children: thread?.status === "archived" ? config.labels.statusArchived : config.labels.statusActive
1871
+ }
1872
+ )
1873
+ ] }),
1874
+ thread?.summary && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "mt-1 text-sm text-muted-foreground", children: thread.summary }),
1875
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "mt-2 flex flex-wrap gap-4 text-xs text-muted-foreground", children: [
1876
+ thread?.createdAt && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
1877
+ "Created ",
1878
+ formatDate3(thread.createdAt)
1879
+ ] }),
1880
+ thread?.updatedAt && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
1881
+ "Updated ",
1882
+ formatDate3(thread.updatedAt)
1883
+ ] }),
1884
+ thread?.participants && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
1885
+ thread.participants.length,
1886
+ " participants"
1887
+ ] })
1888
+ ] })
1889
+ ] })
1890
+ ] }),
1891
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "space-y-1", children: [
1892
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("h3", { className: "text-sm font-medium text-muted-foreground", children: [
1893
+ "Messages (",
1894
+ messages.length,
1895
+ pageInfo?.hasMoreBefore ? "+" : "",
1896
+ ")"
1897
+ ] }) }),
1898
+ pageInfo?.hasMoreBefore && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1899
+ Button,
1900
+ {
1901
+ variant: "ghost",
1902
+ size: "sm",
1903
+ onClick: () => void loadMore(),
1904
+ disabled: isLoadingMore,
1905
+ children: [
1906
+ isLoadingMore ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Loader2, { className: "mr-2 h-3 w-3 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.ChevronUp, { className: "mr-2 h-3 w-3" }),
1907
+ "Load older messages"
1908
+ ]
1909
+ }
1910
+ ) }),
1911
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rounded-lg border bg-card", children: messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "p-6 text-center text-sm text-muted-foreground", children: "No messages in this thread yet." }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "divide-y", children: messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(MessageRow, { message }, message.id)) }) })
1912
+ ] })
1913
+ ] });
1914
+ };
1915
+ function MessageRow({ message }) {
1916
+ const [expanded, setExpanded] = (0, import_react2.useState)(false);
1917
+ const hasToolCalls = Array.isArray(message.toolCalls) && message.toolCalls.length > 0;
1918
+ const hasReasoning = !!message.reasoning;
1919
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-start gap-3", children: [
1920
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SenderIcon, { senderType: message.senderType }),
1921
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 min-w-0", children: [
1922
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
1923
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium", children: message.senderId ?? message.senderUserId ?? message.senderType }),
1924
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Badge, { variant: "outline", className: "text-[10px] px-1.5 py-0", children: message.senderType }),
1925
+ message.createdAt && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-muted-foreground", children: formatTimestamp(message.createdAt) })
1926
+ ] }),
1927
+ message.content && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "mt-1 text-sm whitespace-pre-wrap break-words", children: message.content }),
1928
+ (hasToolCalls || hasReasoning) && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "mt-2 space-y-2", children: [
1929
+ hasToolCalls && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1930
+ "button",
1931
+ {
1932
+ type: "button",
1933
+ onClick: () => setExpanded(!expanded),
1934
+ className: "inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors",
1935
+ children: [
1936
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Wrench, { className: "h-3 w-3" }),
1937
+ message.toolCalls.length,
1938
+ " tool call",
1939
+ message.toolCalls.length > 1 ? "s" : ""
1940
+ ]
1941
+ }
1942
+ ),
1943
+ hasReasoning && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1944
+ "button",
1945
+ {
1946
+ type: "button",
1947
+ onClick: () => setExpanded(!expanded),
1948
+ className: "inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors",
1949
+ children: [
1950
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Cpu, { className: "h-3 w-3" }),
1951
+ "Reasoning"
1952
+ ]
1953
+ }
1954
+ ),
1955
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("pre", { className: "mt-2 rounded-md bg-muted p-3 text-xs overflow-auto max-h-60", children: JSON.stringify(
1956
+ {
1957
+ ...hasToolCalls ? { toolCalls: message.toolCalls } : {},
1958
+ ...hasReasoning ? { reasoning: message.reasoning } : {}
1959
+ },
1960
+ null,
1961
+ 2
1962
+ ) })
1963
+ ] })
1964
+ ] })
1965
+ ] }) });
1966
+ }
1967
+ function SenderIcon({ senderType }) {
1968
+ const base = "flex h-7 w-7 shrink-0 items-center justify-center rounded-full";
1969
+ switch (senderType) {
1970
+ case "agent":
1971
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn(base, "bg-primary/10 text-primary"), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Bot, { className: "h-3.5 w-3.5" }) });
1972
+ case "user":
1973
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn(base, "bg-secondary text-secondary-foreground"), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.User, { className: "h-3.5 w-3.5" }) });
1974
+ case "tool":
1975
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn(base, "bg-muted text-muted-foreground"), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Wrench, { className: "h-3.5 w-3.5" }) });
1976
+ default:
1977
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn(base, "bg-muted text-muted-foreground"), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Cpu, { className: "h-3.5 w-3.5" }) });
1978
+ }
1979
+ }
1980
+ function formatDate3(value) {
1981
+ const date = new Date(value);
1982
+ if (Number.isNaN(date.getTime())) return value;
1983
+ return date.toLocaleDateString(void 0, {
1984
+ month: "short",
1985
+ day: "numeric",
1986
+ year: "numeric"
1987
+ });
1988
+ }
1989
+ function formatTimestamp(value) {
1990
+ const date = new Date(value);
1991
+ if (Number.isNaN(date.getTime())) return value;
1992
+ return date.toLocaleString(void 0, {
1993
+ month: "short",
1994
+ day: "numeric",
1995
+ hour: "numeric",
1996
+ minute: "2-digit",
1997
+ second: "2-digit"
1998
+ });
1999
+ }
2000
+
2001
+ // src/CopilotzAdmin.tsx
2002
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2003
+ var CopilotzAdmin = ({
2004
+ config: userConfig,
2005
+ className
2006
+ }) => {
2007
+ const config = (0, import_react3.useMemo)(
2008
+ () => mergeAdminConfig(defaultAdminConfig, userConfig),
2009
+ [userConfig]
2010
+ );
2011
+ const [route, setRoute] = (0, import_react3.useState)({ page: config.defaultPage });
2012
+ const [threadSearch, setThreadSearch] = (0, import_react3.useState)("");
2013
+ const [participantSearch, setParticipantSearch] = (0, import_react3.useState)("");
2014
+ const [agentSearch, setAgentSearch] = (0, import_react3.useState)("");
2015
+ const deferredThreadSearch = (0, import_react3.useDeferredValue)(threadSearch);
2016
+ const deferredParticipantSearch = (0, import_react3.useDeferredValue)(participantSearch);
2017
+ const deferredAgentSearch = (0, import_react3.useDeferredValue)(agentSearch);
2018
+ const admin = useCopilotzAdmin({
2019
+ baseUrl: config.baseUrl,
2020
+ getRequestHeaders: config.getRequestHeaders,
2021
+ namespace: config.namespace,
2022
+ range: config.initialRange,
2023
+ interval: config.initialInterval,
2024
+ threadSearch: deferredThreadSearch,
2025
+ participantSearch: deferredParticipantSearch,
2026
+ agentSearch: deferredAgentSearch
2027
+ });
2028
+ const navigate = (0, import_react3.useCallback)(
2029
+ (next) => {
2030
+ setRoute(next);
2031
+ config.onNavigate?.(next);
2032
+ },
2033
+ [config]
2034
+ );
2035
+ const handleSidebarNavigate = (0, import_react3.useCallback)(
2036
+ (page) => navigate({ page }),
2037
+ [navigate]
2038
+ );
2039
+ const handleThreadClick = (0, import_react3.useCallback)(
2040
+ (threadId) => navigate({ page: "thread-detail", resourceId: threadId }),
2041
+ [navigate]
2042
+ );
2043
+ const handleBackToThreads = (0, import_react3.useCallback)(
2044
+ () => navigate({ page: "threads" }),
2045
+ [navigate]
2046
+ );
2047
+ const sidebarPage = route.page === "thread-detail" ? "threads" : route.page;
2048
+ if (admin.isLoading && !admin.overview) {
2049
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Card, { className: cn("border-border", className), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CardContent, { className: "text-muted-foreground flex items-center justify-center min-h-[200px]", children: config.labels.loading }) });
2050
+ }
2051
+ if (admin.error && !admin.overview) {
2052
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2053
+ Card,
2054
+ {
2055
+ className: cn("border-destructive/50 bg-destructive/10", className),
2056
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(CardContent, { className: "space-y-4", children: [
2057
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-base font-semibold text-destructive", children: admin.error.message }),
2058
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2059
+ Button,
2060
+ {
2061
+ variant: "destructive",
2062
+ onClick: () => void admin.refresh(),
2063
+ children: config.labels.retry
2064
+ }
2065
+ )
2066
+ ] })
2067
+ }
2068
+ );
2069
+ }
2070
+ const renderCurrentView = () => {
2071
+ switch (route.page) {
2072
+ case "dashboard":
2073
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2074
+ DashboardView,
2075
+ {
2076
+ config,
2077
+ overview: admin.overview,
2078
+ activity: admin.activity,
2079
+ threads: admin.threads,
2080
+ participants: admin.participants,
2081
+ agents: admin.agents,
2082
+ interval: admin.filters.interval,
2083
+ threadSearch,
2084
+ participantSearch,
2085
+ agentSearch,
2086
+ onThreadSearchChange: setThreadSearch,
2087
+ onParticipantSearchChange: setParticipantSearch,
2088
+ onAgentSearchChange: setAgentSearch,
2089
+ onThreadClick: handleThreadClick
2090
+ }
2091
+ );
2092
+ case "threads":
2093
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2094
+ ThreadsView,
2095
+ {
2096
+ config,
2097
+ threads: admin.threads,
2098
+ searchValue: threadSearch,
2099
+ onSearchChange: setThreadSearch,
2100
+ onThreadClick: handleThreadClick
2101
+ }
2102
+ );
2103
+ case "thread-detail":
2104
+ return route.resourceId ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2105
+ ThreadDetailView,
2106
+ {
2107
+ threadId: route.resourceId,
2108
+ config,
2109
+ onBack: handleBackToThreads
2110
+ }
2111
+ ) : null;
2112
+ case "participants":
2113
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "rounded-xl border border-dashed p-10 text-center", children: [
2114
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { className: "text-lg font-semibold", children: config.labels.participantsTitle }),
2115
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mt-2 text-sm text-muted-foreground", children: "Detailed participant management coming soon." })
2116
+ ] });
2117
+ case "agents":
2118
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "rounded-xl border border-dashed p-10 text-center", children: [
2119
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { className: "text-lg font-semibold", children: config.labels.agentsTitle }),
2120
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mt-2 text-sm text-muted-foreground", children: "Agent configuration management coming soon." })
2121
+ ] });
2122
+ case "events":
2123
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "rounded-xl border border-dashed p-10 text-center", children: [
2124
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { className: "text-lg font-semibold", children: config.labels.eventsTitle }),
2125
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mt-2 text-sm text-muted-foreground", children: "Event inspector coming soon." })
2126
+ ] });
2127
+ default:
2128
+ return null;
2129
+ }
2130
+ };
2131
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SidebarProvider, { defaultOpen: config.sidebar.defaultOpen, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2132
+ "div",
2133
+ {
2134
+ className: cn(
2135
+ "flex h-[100svh] md:h-screen bg-background w-full overflow-hidden",
2136
+ className
2137
+ ),
2138
+ children: [
2139
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2140
+ AdminSidebar,
2141
+ {
2142
+ config,
2143
+ currentPage: sidebarPage,
2144
+ onNavigate: handleSidebarNavigate
2145
+ }
2146
+ ),
2147
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SidebarInset, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col h-full min-h-0", children: [
2148
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2149
+ AdminHeader,
2150
+ {
2151
+ config,
2152
+ currentPage: route.page,
2153
+ range: admin.filters.range,
2154
+ interval: admin.filters.interval,
2155
+ onRangeChange: admin.setRange,
2156
+ onIntervalChange: admin.setInterval,
2157
+ onRefresh: () => void admin.refresh(),
2158
+ isLoading: admin.isLoading
2159
+ }
2160
+ ),
2161
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex-1 overflow-auto p-6", children: renderCurrentView() })
2162
+ ] }) })
2163
+ ]
2164
+ }
2165
+ ) }) });
2166
+ };
943
2167
  // Annotate the CommonJS export names for ESM import in node:
944
2168
  0 && (module.exports = {
945
2169
  CopilotzAdmin,