@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.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,319 +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("p-8", className), children: /* @__PURE__ */ jsx6(CardContent, { className: "p-0 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 p-8", className), children: /* @__PURE__ */ jsxs2(CardContent, { className: "p-0 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__ */ jsxs2("div", { className: cn("space-y-6 text-foreground", className), 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-card 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",
597
- size: "sm",
598
- onClick: () => admin.setRange("24h"),
599
- children: config.labels.range24h
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
+ )
600
806
  }
601
807
  ),
602
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",
1252
+ size: "sm",
1253
+ className: "h-7 text-xs",
1254
+ onClick: () => onRangeChange("24h"),
1255
+ children: config.labels.range24h
1256
+ }
1257
+ ),
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: "w-[110px] h-8", 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 })
1404
+ ] }),
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(
1408
+ "div",
1409
+ {
1410
+ className: "rounded-xl border bg-card p-5 shadow-sm",
1411
+ children: [
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 })
1415
+ ]
1416
+ },
1417
+ card.label
1418
+ )) })
645
1419
  ] }),
646
- isEmpty ? /* @__PURE__ */ jsx6(Card, { className: "border-dashed py-10 text-center", children: /* @__PURE__ */ jsxs2(CardContent, { className: "p-0", 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 gap-4 md:grid-cols-2 xl:grid-cols-5", children: cards.map((card) => /* @__PURE__ */ jsx6(Card, { className: "gap-0 py-0", children: /* @__PURE__ */ jsxs2(CardContent, { className: "p-5", children: [
653
- /* @__PURE__ */ jsx6("p", { className: "text-sm font-medium text-muted-foreground", children: card.label }),
654
- /* @__PURE__ */ jsx6("p", { className: "mt-3 text-3xl font-semibold tracking-tight", children: formatNumber(card.value) }),
655
- /* @__PURE__ */ jsx6("p", { className: "mt-2 text-xs text-muted-foreground", children: card.detail })
656
- ] }) }, card.label)) })
657
- ] }) : null,
658
- config.features.showActivity ? /* @__PURE__ */ jsxs2("section", { className: "space-y-4", children: [
659
- /* @__PURE__ */ jsx6(SectionHeading, { title: config.labels.activityTitle }),
660
- /* @__PURE__ */ jsx6(
1420
+ config.features.showActivity && /* @__PURE__ */ jsxs7("section", { className: "space-y-4", children: [
1421
+ /* @__PURE__ */ jsx11(SectionHeading, { title: config.labels.activityTitle }),
1422
+ /* @__PURE__ */ jsx11(
661
1423
  ActivityChart,
662
1424
  {
663
- interval: admin.filters.interval,
1425
+ interval,
664
1426
  labels: config.labels,
665
1427
  maxBars: config.ui.maxActivityBars,
666
- points: admin.activity
1428
+ points: activity
667
1429
  }
668
1430
  )
669
- ] }) : null,
670
- /* @__PURE__ */ jsxs2("div", { className: "grid gap-6 xl:grid-cols-3", children: [
671
- 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(
672
1434
  DataTable,
673
1435
  {
674
- rows: admin.threads,
1436
+ rows: threads,
675
1437
  searchPlaceholder: config.labels.threadSearchPlaceholder,
676
1438
  searchValue: threadSearch,
677
- setSearchValue: setThreadSearch,
1439
+ setSearchValue: onThreadSearchChange,
678
1440
  title: config.labels.threadsTitle,
679
- 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
+ )
680
1449
  }
681
- ) : null,
682
- config.features.showParticipants ? /* @__PURE__ */ jsx6(
1450
+ ),
1451
+ config.features.showParticipants && /* @__PURE__ */ jsx11(
683
1452
  DataTable,
684
1453
  {
685
- rows: admin.participants,
1454
+ rows: participants,
686
1455
  searchPlaceholder: config.labels.participantSearchPlaceholder,
687
1456
  searchValue: participantSearch,
688
- setSearchValue: setParticipantSearch,
1457
+ setSearchValue: onParticipantSearchChange,
689
1458
  title: config.labels.participantsTitle,
690
- children: /* @__PURE__ */ jsx6(
691
- ParticipantsTable,
692
- {
693
- rows: admin.participants,
694
- labels: config.labels
695
- }
696
- )
1459
+ children: /* @__PURE__ */ jsx11(ParticipantsTable, { rows: participants, labels: config.labels })
697
1460
  }
698
- ) : null,
699
- config.features.showAgents ? /* @__PURE__ */ jsx6(
1461
+ ),
1462
+ config.features.showAgents && /* @__PURE__ */ jsx11(
700
1463
  DataTable,
701
1464
  {
702
- rows: admin.agents,
1465
+ rows: agents,
703
1466
  searchPlaceholder: config.labels.agentSearchPlaceholder,
704
1467
  searchValue: agentSearch,
705
- setSearchValue: setAgentSearch,
1468
+ setSearchValue: onAgentSearchChange,
706
1469
  title: config.labels.agentsTitle,
707
- children: /* @__PURE__ */ jsx6(AgentsTable, { rows: admin.agents, labels: config.labels })
1470
+ children: /* @__PURE__ */ jsx11(AgentsTable, { rows: agents, labels: config.labels })
708
1471
  }
709
- ) : null
1472
+ )
710
1473
  ] })
711
1474
  ] });
712
1475
  };
713
1476
  function SectionHeading({ title }) {
714
- 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 });
715
1478
  }
716
1479
  function ActivityChart(props) {
717
1480
  const trimmedPoints = props.points.slice(-props.maxBars);
@@ -719,23 +1482,23 @@ function ActivityChart(props) {
719
1482
  ...trimmedPoints.map((point) => point.messageCount),
720
1483
  1
721
1484
  );
722
- return /* @__PURE__ */ jsx6(Card, { className: "gap-0 py-0", children: /* @__PURE__ */ jsx6(CardContent, { className: "p-5", children: trimmedPoints.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: props.labels.noResults }) : /* @__PURE__ */ jsx6("div", { className: "flex min-h-56 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(
723
1486
  "div",
724
1487
  {
725
1488
  className: "flex min-w-0 flex-1 flex-col items-center gap-2",
726
1489
  children: [
727
- /* @__PURE__ */ jsx6("div", { className: "flex h-40 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(
728
1491
  "div",
729
1492
  {
730
- className: "w-full rounded-md bg-primary",
1493
+ className: "w-full rounded-md bg-primary transition-all",
731
1494
  style: {
732
1495
  height: `${Math.max(point.messageCount / maxMessages * 100, 8)}%`
733
1496
  }
734
1497
  }
735
1498
  ) }),
736
- /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
737
- /* @__PURE__ */ jsx6("p", { className: "text-xs font-medium", children: formatBucket(point.bucket, props.interval) }),
738
- /* @__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: [
739
1502
  formatNumber(point.messageCount),
740
1503
  " msg"
741
1504
  ] })
@@ -743,40 +1506,45 @@ function ActivityChart(props) {
743
1506
  ]
744
1507
  },
745
1508
  point.bucket
746
- )) }) }) });
1509
+ )) }) });
747
1510
  }
748
1511
  function DataTable(props) {
749
- return /* @__PURE__ */ jsx6(Card, { className: "gap-0 py-0", children: /* @__PURE__ */ jsxs2(CardContent, { className: "p-5", children: [
750
- /* @__PURE__ */ jsxs2("div", { className: "mb-4 flex items-center justify-between gap-3", children: [
751
- /* @__PURE__ */ jsx6(SectionHeading, { title: props.title }),
752
- /* @__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(
753
1516
  Input,
754
1517
  {
755
- className: "w-full max-w-44 h-8",
1518
+ className: "h-8 w-full max-w-44",
756
1519
  onChange: (event) => props.setSearchValue(event.target.value),
757
1520
  placeholder: props.searchPlaceholder,
758
1521
  value: props.searchValue
759
1522
  }
760
1523
  )
761
1524
  ] }),
762
- props.rows.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: "No results" }) : props.children
763
- ] }) });
1525
+ props.rows.length === 0 ? /* @__PURE__ */ jsx11("p", { className: "text-sm text-muted-foreground", children: "No results" }) : props.children
1526
+ ] });
764
1527
  }
765
1528
  function ThreadsTable({
766
1529
  rows,
767
- labels
1530
+ labels,
1531
+ onThreadClick
768
1532
  }) {
769
- 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(
770
1534
  "div",
771
1535
  {
772
- 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),
773
1541
  children: [
774
- /* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between gap-3", children: [
775
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
776
- /* @__PURE__ */ jsx6("p", { className: "font-medium", children: thread.name }),
777
- /* @__PURE__ */ jsx6("p", { className: "mt-1 text-xs text-muted-foreground truncate", 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" })
778
1546
  ] }),
779
- /* @__PURE__ */ jsx6(
1547
+ /* @__PURE__ */ jsx11(
780
1548
  Badge,
781
1549
  {
782
1550
  variant: thread.status === "archived" ? "secondary" : "default",
@@ -784,16 +1552,16 @@ function ThreadsTable({
784
1552
  }
785
1553
  )
786
1554
  ] }),
787
- /* @__PURE__ */ jsxs2("div", { className: "mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
788
- /* @__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: [
789
1557
  formatNumber(thread.messageCount),
790
1558
  " messages"
791
1559
  ] }),
792
- /* @__PURE__ */ jsxs2("span", { children: [
1560
+ /* @__PURE__ */ jsxs7("span", { children: [
793
1561
  thread.participantIds.length,
794
1562
  " participants"
795
1563
  ] }),
796
- /* @__PURE__ */ jsx6("span", { children: formatDate(thread.lastActivityAt) })
1564
+ /* @__PURE__ */ jsx11("span", { children: formatDate(thread.lastActivityAt) })
797
1565
  ] })
798
1566
  ]
799
1567
  },
@@ -804,28 +1572,28 @@ function ParticipantsTable({
804
1572
  rows,
805
1573
  labels
806
1574
  }) {
807
- 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(
808
1576
  "div",
809
1577
  {
810
1578
  className: "rounded-lg border bg-muted/50 p-4",
811
1579
  children: [
812
- /* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between gap-3", children: [
813
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
814
- /* @__PURE__ */ jsx6("p", { className: "font-medium", children: participant.displayName }),
815
- /* @__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 })
816
1584
  ] }),
817
- /* @__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 })
818
1586
  ] }),
819
- /* @__PURE__ */ jsxs2("div", { className: "mt-3 flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
820
- /* @__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: [
821
1589
  formatNumber(participant.messageCount),
822
1590
  " messages"
823
1591
  ] }),
824
- /* @__PURE__ */ jsxs2("span", { children: [
1592
+ /* @__PURE__ */ jsxs7("span", { children: [
825
1593
  formatNumber(participant.threadCount),
826
1594
  " threads"
827
1595
  ] }),
828
- /* @__PURE__ */ jsx6("span", { children: formatDate(participant.lastActivityAt) })
1596
+ /* @__PURE__ */ jsx11("span", { children: formatDate(participant.lastActivityAt) })
829
1597
  ] })
830
1598
  ]
831
1599
  },
@@ -836,32 +1604,32 @@ function AgentsTable({
836
1604
  rows,
837
1605
  labels
838
1606
  }) {
839
- 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(
840
1608
  "div",
841
1609
  {
842
1610
  className: "rounded-lg border bg-muted/50 p-4",
843
1611
  children: [
844
- /* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between gap-3", children: [
845
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
846
- /* @__PURE__ */ jsx6("p", { className: "font-medium", children: agent.displayName }),
847
- /* @__PURE__ */ jsx6("p", { className: "mt-1 text-xs text-muted-foreground truncate", 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 })
848
1616
  ] }),
849
- /* @__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 })
850
1618
  ] }),
851
- /* @__PURE__ */ jsxs2("div", { className: "mt-3 grid grid-cols-2 gap-2 text-xs text-muted-foreground", children: [
852
- /* @__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: [
853
1621
  formatNumber(agent.messageCount),
854
1622
  " messages"
855
1623
  ] }),
856
- /* @__PURE__ */ jsxs2("span", { children: [
1624
+ /* @__PURE__ */ jsxs7("span", { children: [
857
1625
  formatNumber(agent.llmCallCount),
858
1626
  " LLM calls"
859
1627
  ] }),
860
- /* @__PURE__ */ jsxs2("span", { children: [
1628
+ /* @__PURE__ */ jsxs7("span", { children: [
861
1629
  formatNumber(agent.toolCallMessageCount),
862
1630
  " tool calls"
863
1631
  ] }),
864
- /* @__PURE__ */ jsxs2("span", { children: [
1632
+ /* @__PURE__ */ jsxs7("span", { children: [
865
1633
  formatNumber(agent.totalTokens),
866
1634
  " tokens"
867
1635
  ] })
@@ -897,6 +1665,484 @@ function formatDate(value) {
897
1665
  function formatNumber(value) {
898
1666
  return new Intl.NumberFormat().format(value);
899
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
+ };
900
2146
  export {
901
2147
  CopilotzAdmin,
902
2148
  defaultAdminConfig,