@copilotz/admin 0.3.6 → 0.3.7

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