@copilotz/admin 0.3.5 → 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,319 +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("p-8", className), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardContent, { className: "p-0 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 p-8", className), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardContent, { className: "p-0 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.jsxs)("div", { className: cn("space-y-6 text-foreground", className), 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-card 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: [
629
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
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
+ ),
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: "w-[110px] h-8", 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 })
1433
+ ] }),
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)(
1437
+ "div",
1438
+ {
1439
+ className: "rounded-xl border bg-card p-5 shadow-sm",
1440
+ children: [
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 })
1444
+ ]
1445
+ },
1446
+ card.label
1447
+ )) })
681
1448
  ] }),
682
- isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Card, { className: "border-dashed py-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardContent, { className: "p-0", 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 gap-4 md:grid-cols-2 xl:grid-cols-5", children: cards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Card, { className: "gap-0 py-0", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardContent, { className: "p-5", children: [
689
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm font-medium text-muted-foreground", children: card.label }),
690
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-3 text-3xl font-semibold tracking-tight", children: formatNumber(card.value) }),
691
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-2 text-xs text-muted-foreground", children: card.detail })
692
- ] }) }, card.label)) })
693
- ] }) : null,
694
- config.features.showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "space-y-4", children: [
695
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionHeading, { title: config.labels.activityTitle }),
696
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
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)(
697
1452
  ActivityChart,
698
1453
  {
699
- interval: admin.filters.interval,
1454
+ interval,
700
1455
  labels: config.labels,
701
1456
  maxBars: config.ui.maxActivityBars,
702
- points: admin.activity
1457
+ points: activity
703
1458
  }
704
1459
  )
705
- ] }) : null,
706
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid gap-6 xl:grid-cols-3", children: [
707
- 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)(
708
1463
  DataTable,
709
1464
  {
710
- rows: admin.threads,
1465
+ rows: threads,
711
1466
  searchPlaceholder: config.labels.threadSearchPlaceholder,
712
1467
  searchValue: threadSearch,
713
- setSearchValue: setThreadSearch,
1468
+ setSearchValue: onThreadSearchChange,
714
1469
  title: config.labels.threadsTitle,
715
- 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
+ )
716
1478
  }
717
- ) : null,
718
- config.features.showParticipants ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1479
+ ),
1480
+ config.features.showParticipants && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
719
1481
  DataTable,
720
1482
  {
721
- rows: admin.participants,
1483
+ rows: participants,
722
1484
  searchPlaceholder: config.labels.participantSearchPlaceholder,
723
1485
  searchValue: participantSearch,
724
- setSearchValue: setParticipantSearch,
1486
+ setSearchValue: onParticipantSearchChange,
725
1487
  title: config.labels.participantsTitle,
726
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
727
- ParticipantsTable,
728
- {
729
- rows: admin.participants,
730
- labels: config.labels
731
- }
732
- )
1488
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ParticipantsTable, { rows: participants, labels: config.labels })
733
1489
  }
734
- ) : null,
735
- config.features.showAgents ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1490
+ ),
1491
+ config.features.showAgents && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
736
1492
  DataTable,
737
1493
  {
738
- rows: admin.agents,
1494
+ rows: agents,
739
1495
  searchPlaceholder: config.labels.agentSearchPlaceholder,
740
1496
  searchValue: agentSearch,
741
- setSearchValue: setAgentSearch,
1497
+ setSearchValue: onAgentSearchChange,
742
1498
  title: config.labels.agentsTitle,
743
- 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 })
744
1500
  }
745
- ) : null
1501
+ )
746
1502
  ] })
747
1503
  ] });
748
1504
  };
749
1505
  function SectionHeading({ title }) {
750
- 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 });
751
1507
  }
752
1508
  function ActivityChart(props) {
753
1509
  const trimmedPoints = props.points.slice(-props.maxBars);
@@ -755,23 +1511,23 @@ function ActivityChart(props) {
755
1511
  ...trimmedPoints.map((point) => point.messageCount),
756
1512
  1
757
1513
  );
758
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Card, { className: "gap-0 py-0", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardContent, { className: "p-5", 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-56 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)(
759
1515
  "div",
760
1516
  {
761
1517
  className: "flex min-w-0 flex-1 flex-col items-center gap-2",
762
1518
  children: [
763
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex h-40 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)(
764
1520
  "div",
765
1521
  {
766
- className: "w-full rounded-md bg-primary",
1522
+ className: "w-full rounded-md bg-primary transition-all",
767
1523
  style: {
768
1524
  height: `${Math.max(point.messageCount / maxMessages * 100, 8)}%`
769
1525
  }
770
1526
  }
771
1527
  ) }),
772
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-center", children: [
773
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs font-medium", children: formatBucket(point.bucket, props.interval) }),
774
- /* @__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: [
775
1531
  formatNumber(point.messageCount),
776
1532
  " msg"
777
1533
  ] })
@@ -779,40 +1535,45 @@ function ActivityChart(props) {
779
1535
  ]
780
1536
  },
781
1537
  point.bucket
782
- )) }) }) });
1538
+ )) }) });
783
1539
  }
784
1540
  function DataTable(props) {
785
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Card, { className: "gap-0 py-0", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(CardContent, { className: "p-5", children: [
786
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mb-4 flex items-center justify-between gap-3", children: [
787
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionHeading, { title: props.title }),
788
- /* @__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)(
789
1545
  Input,
790
1546
  {
791
- className: "w-full max-w-44 h-8",
1547
+ className: "h-8 w-full max-w-44",
792
1548
  onChange: (event) => props.setSearchValue(event.target.value),
793
1549
  placeholder: props.searchPlaceholder,
794
1550
  value: props.searchValue
795
1551
  }
796
1552
  )
797
1553
  ] }),
798
- props.rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-muted-foreground", children: "No results" }) : props.children
799
- ] }) });
1554
+ props.rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-muted-foreground", children: "No results" }) : props.children
1555
+ ] });
800
1556
  }
801
1557
  function ThreadsTable({
802
1558
  rows,
803
- labels
1559
+ labels,
1560
+ onThreadClick
804
1561
  }) {
805
- 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)(
806
1563
  "div",
807
1564
  {
808
- 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),
809
1570
  children: [
810
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
811
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-w-0", children: [
812
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "font-medium", children: thread.name }),
813
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-1 text-xs text-muted-foreground truncate", 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" })
814
1575
  ] }),
815
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1576
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
816
1577
  Badge,
817
1578
  {
818
1579
  variant: thread.status === "archived" ? "secondary" : "default",
@@ -820,16 +1581,16 @@ function ThreadsTable({
820
1581
  }
821
1582
  )
822
1583
  ] }),
823
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
824
- /* @__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: [
825
1586
  formatNumber(thread.messageCount),
826
1587
  " messages"
827
1588
  ] }),
828
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1589
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
829
1590
  thread.participantIds.length,
830
1591
  " participants"
831
1592
  ] }),
832
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: formatDate(thread.lastActivityAt) })
1593
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: formatDate(thread.lastActivityAt) })
833
1594
  ] })
834
1595
  ]
835
1596
  },
@@ -840,28 +1601,28 @@ function ParticipantsTable({
840
1601
  rows,
841
1602
  labels
842
1603
  }) {
843
- 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)(
844
1605
  "div",
845
1606
  {
846
1607
  className: "rounded-lg border bg-muted/50 p-4",
847
1608
  children: [
848
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
849
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-w-0", children: [
850
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "font-medium", children: participant.displayName }),
851
- /* @__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 })
852
1613
  ] }),
853
- /* @__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 })
854
1615
  ] }),
855
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
856
- /* @__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: [
857
1618
  formatNumber(participant.messageCount),
858
1619
  " messages"
859
1620
  ] }),
860
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1621
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
861
1622
  formatNumber(participant.threadCount),
862
1623
  " threads"
863
1624
  ] }),
864
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: formatDate(participant.lastActivityAt) })
1625
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: formatDate(participant.lastActivityAt) })
865
1626
  ] })
866
1627
  ]
867
1628
  },
@@ -872,32 +1633,32 @@ function AgentsTable({
872
1633
  rows,
873
1634
  labels
874
1635
  }) {
875
- 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)(
876
1637
  "div",
877
1638
  {
878
1639
  className: "rounded-lg border bg-muted/50 p-4",
879
1640
  children: [
880
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
881
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-w-0", children: [
882
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "font-medium", children: agent.displayName }),
883
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mt-1 text-xs text-muted-foreground truncate", 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 })
884
1645
  ] }),
885
- /* @__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 })
886
1647
  ] }),
887
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3 grid grid-cols-2 gap-2 text-xs text-muted-foreground", children: [
888
- /* @__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: [
889
1650
  formatNumber(agent.messageCount),
890
1651
  " messages"
891
1652
  ] }),
892
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1653
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
893
1654
  formatNumber(agent.llmCallCount),
894
1655
  " LLM calls"
895
1656
  ] }),
896
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1657
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
897
1658
  formatNumber(agent.toolCallMessageCount),
898
1659
  " tool calls"
899
1660
  ] }),
900
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1661
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
901
1662
  formatNumber(agent.totalTokens),
902
1663
  " tokens"
903
1664
  ] })
@@ -933,6 +1694,476 @@ function formatDate(value) {
933
1694
  function formatNumber(value) {
934
1695
  return new Intl.NumberFormat().format(value);
935
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
+ };
936
2167
  // Annotate the CommonJS export names for ESM import in node:
937
2168
  0 && (module.exports = {
938
2169
  CopilotzAdmin,