@datum-cloud/activity-ui 0.5.1 → 0.6.0-dev.917556e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +14 -0
  2. package/dist/components/ActionMultiSelect.d.ts.map +1 -1
  3. package/dist/components/ActivityDigestSummary.d.ts +15 -0
  4. package/dist/components/ActivityDigestSummary.d.ts.map +1 -0
  5. package/dist/components/ActivityFeed.d.ts +11 -2
  6. package/dist/components/ActivityFeed.d.ts.map +1 -1
  7. package/dist/components/ActivityFeedFilters.d.ts +9 -1
  8. package/dist/components/ActivityFeedFilters.d.ts.map +1 -1
  9. package/dist/components/ActivityFeedGroupItem.d.ts +23 -0
  10. package/dist/components/ActivityFeedGroupItem.d.ts.map +1 -0
  11. package/dist/components/ActivityFeedItem.d.ts +8 -1
  12. package/dist/components/ActivityFeedItem.d.ts.map +1 -1
  13. package/dist/components/AuditLogFeedItem.d.ts.map +1 -1
  14. package/dist/components/CelEditor.d.ts +1 -1
  15. package/dist/components/CelEditor.d.ts.map +1 -1
  16. package/dist/components/ChangeSourceToggle.d.ts.map +1 -1
  17. package/dist/components/DateTimeRangePicker.d.ts.map +1 -1
  18. package/dist/components/EventFeedItem.d.ts.map +1 -1
  19. package/dist/components/EventTypeToggle.d.ts.map +1 -1
  20. package/dist/components/FilterBuilder.d.ts.map +1 -1
  21. package/dist/components/FilterBuilderWithAutocomplete.d.ts.map +1 -1
  22. package/dist/components/PolicyDetailView.d.ts.map +1 -1
  23. package/dist/components/PolicyEditView.d.ts.map +1 -1
  24. package/dist/components/PolicyEditor.d.ts.map +1 -1
  25. package/dist/components/UserSelect.d.ts.map +1 -1
  26. package/dist/components/VerbToggle.d.ts.map +1 -1
  27. package/dist/components/ui/add-filter-dropdown.d.ts.map +1 -1
  28. package/dist/components/ui/combobox.d.ts +0 -4
  29. package/dist/components/ui/combobox.d.ts.map +1 -1
  30. package/dist/components/ui/filter-chip.d.ts.map +1 -1
  31. package/dist/components/ui/multi-combobox.d.ts +2 -5
  32. package/dist/components/ui/multi-combobox.d.ts.map +1 -1
  33. package/dist/components/ui/segmented-toggle.d.ts +26 -0
  34. package/dist/components/ui/segmented-toggle.d.ts.map +1 -0
  35. package/dist/components/ui/time-range-dropdown.d.ts +13 -4
  36. package/dist/components/ui/time-range-dropdown.d.ts.map +1 -1
  37. package/dist/hooks/useActivityFeed.d.ts.map +1 -1
  38. package/dist/hooks/useAuditLogQuery.d.ts +1 -1
  39. package/dist/hooks/useAuditLogQuery.d.ts.map +1 -1
  40. package/dist/index.d.ts +6 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.esm.js +1664 -1352
  43. package/dist/index.esm.js.map +1 -1
  44. package/dist/index.js +1597 -1282
  45. package/dist/index.js.map +1 -1
  46. package/dist/lib/groupActivities.d.ts +40 -0
  47. package/dist/lib/groupActivities.d.ts.map +1 -0
  48. package/dist/lib/verb.d.ts +22 -0
  49. package/dist/lib/verb.d.ts.map +1 -0
  50. package/package.json +7 -12
  51. package/dist/components/ui/alert.d.ts +0 -9
  52. package/dist/components/ui/alert.d.ts.map +0 -1
  53. package/dist/components/ui/button.d.ts +0 -12
  54. package/dist/components/ui/button.d.ts.map +0 -1
  55. package/dist/components/ui/card.d.ts +0 -9
  56. package/dist/components/ui/card.d.ts.map +0 -1
  57. package/dist/components/ui/checkbox.d.ts +0 -5
  58. package/dist/components/ui/checkbox.d.ts.map +0 -1
  59. package/dist/components/ui/input.d.ts +0 -6
  60. package/dist/components/ui/input.d.ts.map +0 -1
  61. package/dist/components/ui/label.d.ts +0 -8
  62. package/dist/components/ui/label.d.ts.map +0 -1
  63. package/dist/components/ui/select.d.ts +0 -14
  64. package/dist/components/ui/select.d.ts.map +0 -1
  65. package/dist/components/ui/separator.d.ts +0 -5
  66. package/dist/components/ui/separator.d.ts.map +0 -1
  67. package/dist/components/ui/sheet.d.ts +0 -23
  68. package/dist/components/ui/sheet.d.ts.map +0 -1
  69. package/dist/components/ui/skeleton.d.ts +0 -3
  70. package/dist/components/ui/skeleton.d.ts.map +0 -1
  71. package/dist/components/ui/tabs.d.ts +0 -8
  72. package/dist/components/ui/tabs.d.ts.map +0 -1
  73. package/dist/components/ui/textarea.d.ts +0 -6
  74. package/dist/components/ui/textarea.d.ts.map +0 -1
package/dist/index.esm.js CHANGED
@@ -14,10 +14,13 @@ export { Separator } from '@datum-cloud/datum-ui/separator';
14
14
  import { Textarea } from '@datum-cloud/datum-ui/textarea';
15
15
  export { Textarea } from '@datum-cloud/datum-ui/textarea';
16
16
  import { badgeVariants as badgeVariants$1, Badge as Badge$1 } from '@datum-cloud/datum-ui/badge';
17
- import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from 'cmdk';
18
- import * as Popover from '@radix-ui/react-popover';
17
+ import { Autocomplete } from '@datum-cloud/datum-ui/autocomplete';
19
18
  import { Checkbox } from '@datum-cloud/datum-ui/checkbox';
20
19
  export { Checkbox } from '@datum-cloud/datum-ui/checkbox';
20
+ import { DateTimeRangePicker as DateTimeRangePicker$1 } from '@datum-cloud/datum-ui/picker';
21
+ import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from '@datum-cloud/datum-ui/command';
22
+ import { Popover, PopoverTrigger, PopoverContent } from '@datum-cloud/datum-ui/popover';
23
+ import { ButtonGroup } from '@datum-cloud/datum-ui/button-group';
21
24
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
22
25
  import { TableRow, TableCell, Table, TableHeader, TableHead, TableBody } from '@datum-cloud/datum-ui/table';
23
26
  import { Skeleton } from '@datum-cloud/datum-ui/skeleton';
@@ -30,6 +33,7 @@ import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFo
30
33
  import { Tabs, TabsList, TabsTrigger, TabsContent } from '@datum-cloud/datum-ui/tabs';
31
34
  export { Tabs, TabsContent, TabsList, TabsTrigger } from '@datum-cloud/datum-ui/tabs';
32
35
  import { Dialog as Dialog$1 } from '@datum-cloud/datum-ui/dialog';
36
+ import { MultiSelect } from '@datum-cloud/datum-ui/multi-select';
33
37
 
34
38
  /**
35
39
  * TypeScript types for the Activity API (activity.miloapis.com/v1alpha1)
@@ -202,7 +206,7 @@ function FilterBuilder({ onFilterChange, initialFilter = '', initialLimit = 100,
202
206
  const newFilter = filter ? `${filter} && ${example}` : example;
203
207
  handleFilterChange(newFilter);
204
208
  };
205
- return (jsxs(Card, { className: `mb-6 ${className}`, children: [jsx(CardHeader, { className: "pb-4", children: jsxs("div", { className: "flex justify-between items-center", children: [jsx("h3", { className: "m-0 text-2xl font-semibold text-foreground", children: "Build Your Query" }), jsxs(Button, { onClick: () => setShowHelp(!showHelp), type: "tertiary", theme: "outline", htmlType: "button", children: [showHelp ? 'Hide' : 'Show', " Help"] })] }) }), jsx(Separator, {}), jsxs(CardContent, { className: "pt-6", children: [showHelp && (jsxs("div", { className: "bg-muted p-6 rounded-lg mb-6 max-h-[400px] overflow-y-auto border border-border", children: [jsx("h4", { className: "text-base font-semibold text-foreground", children: "Available Filter Fields" }), jsx("div", { className: "mt-4", children: FILTER_FIELDS.map((field) => (jsxs("div", { className: "mb-6 pb-4 border-b border-border last:border-b-0", children: [jsxs("div", { className: "flex justify-between items-center mb-2", children: [jsx("strong", { className: "text-foreground", children: field.name }), jsx("span", { className: "text-sm text-muted-foreground italic", children: field.type })] }), jsx("p", { className: "my-2 text-muted-foreground", children: field.description }), field.examples && field.examples.length > 0 && (jsxs("div", { className: "mt-2", children: [jsx("strong", { className: "text-foreground", children: "Examples:" }), field.examples.map((example, idx) => (jsxs("div", { className: "flex items-center gap-2 my-2", children: [jsx("code", { className: "flex-1 px-2 py-1 bg-background rounded text-sm font-mono", children: example }), jsx(Button, { onClick: () => insertExample(example), type: "primary", theme: "solid", size: "small", className: "bg-green-600 hover:bg-green-700 dark:bg-green-700 dark:hover:bg-green-600", htmlType: "button", children: "Insert" })] }, idx)))] }))] }, field.name))) }), jsxs("div", { className: "mt-4", children: [jsx("h4", { className: "mb-2 text-base font-semibold text-foreground", children: "Common Operators" }), jsxs("ul", { className: "m-0 pl-6 text-muted-foreground", children: [jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "==" }), " - Equals"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "!=" }), " - Not equals"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "&&" }), " - And"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "||" }), " - Or"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "in" }), " - In list (e.g., ", jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "verb in [\"create\", \"delete\"]" }), ")"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: ".startsWith()" }), " - String starts with"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: ".contains()" }), " - String contains"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "timestamp()" }), " - Parse timestamp (e.g., ", jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "timestamp(\"2024-01-01T00:00:00Z\")" }), ")"] })] })] })] })), jsxs("div", { className: "mb-4", children: [jsx(Label, { htmlFor: "filter-input", className: "block mb-2", children: "CEL Filter Expression" }), jsx(Textarea, { id: "filter-input", value: filter, onChange: (e) => handleFilterChange(e.target.value), placeholder: 'e.g., verb == "delete" && ns == "production"', rows: 4, className: "font-mono text-sm resize-y" })] }), jsxs("div", { className: "mb-4", children: [jsx(Label, { htmlFor: "limit-input", className: "block mb-2", children: "Result Limit (max 1,000)" }), jsx(Input, { id: "limit-input", type: "number", value: limit, onChange: (e) => handleLimitChange(parseInt(e.target.value) || 100), min: 1, max: 1000, className: "w-44" })] }), jsx(Separator, { className: "my-6" }), jsxs("div", { children: [jsx("h4", { className: "mb-3 text-base font-semibold text-foreground", children: "Quick Filters" }), jsxs("div", { className: "flex flex-wrap gap-2", children: [jsx(Button, { onClick: () => insertExample('verb == "delete"'), htmlType: "button", type: "tertiary", theme: "outline", children: "Delete Operations" }), jsx(Button, { onClick: () => insertExample('resource == "secrets"'), htmlType: "button", type: "tertiary", theme: "outline", children: "Secret Access" }), jsx(Button, { onClick: () => insertExample('user.startsWith("system:")'), htmlType: "button", type: "tertiary", theme: "outline", children: "System Users" }), jsx(Button, { onClick: () => insertExample('stage == "ResponseComplete"'), htmlType: "button", type: "tertiary", theme: "outline", children: "Completed Requests" })] })] })] })] }));
209
+ return (jsxs(Card, { className: `mb-6 ${className}`, children: [jsx(CardHeader, { className: "pb-4", children: jsxs("div", { className: "flex justify-between items-center", children: [jsx("h3", { className: "m-0 text-2xl font-semibold text-foreground", children: "Build Your Query" }), jsxs(Button, { onClick: () => setShowHelp(!showHelp), type: "tertiary", theme: "outline", htmlType: "button", children: [showHelp ? 'Hide' : 'Show', " Help"] })] }) }), jsx(Separator, {}), jsxs(CardContent, { className: "pt-6", children: [showHelp && (jsxs("div", { className: "bg-muted p-6 rounded-lg mb-6 max-h-[400px] overflow-y-auto border border-border", children: [jsx("h4", { className: "text-base font-semibold text-foreground", children: "Available Filter Fields" }), jsx("div", { className: "mt-4", children: FILTER_FIELDS.map((field) => (jsxs("div", { className: "mb-6 pb-4 border-b border-border last:border-b-0", children: [jsxs("div", { className: "flex justify-between items-center mb-2", children: [jsx("strong", { className: "text-foreground", children: field.name }), jsx("span", { className: "text-sm text-muted-foreground italic", children: field.type })] }), jsx("p", { className: "my-2 text-muted-foreground", children: field.description }), field.examples && field.examples.length > 0 && (jsxs("div", { className: "mt-2", children: [jsx("strong", { className: "text-foreground", children: "Examples:" }), field.examples.map((example, idx) => (jsxs("div", { className: "flex items-center gap-2 my-2", children: [jsx("code", { className: "flex-1 px-2 py-1 bg-background rounded text-sm font-mono", children: example }), jsx(Button, { onClick: () => insertExample(example), type: "success", theme: "solid", size: "small", htmlType: "button", children: "Insert" })] }, idx)))] }))] }, field.name))) }), jsxs("div", { className: "mt-4", children: [jsx("h4", { className: "mb-2 text-base font-semibold text-foreground", children: "Common Operators" }), jsxs("ul", { className: "m-0 pl-6 text-muted-foreground", children: [jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "==" }), " - Equals"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "!=" }), " - Not equals"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "&&" }), " - And"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "||" }), " - Or"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "in" }), " - In list (e.g., ", jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "verb in [\"create\", \"delete\"]" }), ")"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: ".startsWith()" }), " - String starts with"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: ".contains()" }), " - String contains"] }), jsxs("li", { className: "my-1", children: [jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "timestamp()" }), " - Parse timestamp (e.g., ", jsx("code", { className: "px-1 py-0.5 bg-background rounded text-sm font-mono", children: "timestamp(\"2024-01-01T00:00:00Z\")" }), ")"] })] })] })] })), jsxs("div", { className: "mb-4", children: [jsx(Label, { htmlFor: "filter-input", className: "block mb-2", children: "CEL Filter Expression" }), jsx(Textarea, { id: "filter-input", value: filter, onChange: (e) => handleFilterChange(e.target.value), placeholder: 'e.g., verb == "delete" && ns == "production"', rows: 4, className: "font-mono text-sm resize-y" })] }), jsxs("div", { className: "mb-4", children: [jsx(Label, { htmlFor: "limit-input", className: "block mb-2", children: "Result Limit (max 1,000)" }), jsx(Input, { id: "limit-input", type: "number", value: limit, onChange: (e) => handleLimitChange(parseInt(e.target.value) || 100), min: 1, max: 1000, className: "w-44" })] }), jsx(Separator, { className: "my-6" }), jsxs("div", { children: [jsx("h4", { className: "mb-3 text-base font-semibold text-foreground", children: "Quick Filters" }), jsxs("div", { className: "flex flex-wrap gap-2", children: [jsx(Button, { onClick: () => insertExample('verb == "delete"'), htmlType: "button", type: "tertiary", theme: "outline", children: "Delete Operations" }), jsx(Button, { onClick: () => insertExample('resource == "secrets"'), htmlType: "button", type: "tertiary", theme: "outline", children: "Secret Access" }), jsx(Button, { onClick: () => insertExample('user.startsWith("system:")'), htmlType: "button", type: "tertiary", theme: "outline", children: "System Users" }), jsx(Button, { onClick: () => insertExample('stage == "ResponseComplete"'), htmlType: "button", type: "tertiary", theme: "outline", children: "Completed Requests" })] })] })] })] }));
206
210
  }
207
211
 
208
212
  function mapVariant(variant) {
@@ -311,7 +315,7 @@ function FilterBuilderWithAutocomplete({ onFilterChange, initialFilter = '', ini
311
315
  // Suggest logical operators to continue
312
316
  const completedValuePattern = /["'][^"']*["']\s*$/;
313
317
  const completedNumberPattern = /\d+\s*$/;
314
- const afterLogicalOp = /(\&\&|\|\|)\s*$/;
318
+ const afterLogicalOp = /(&&|\|\|)\s*$/;
315
319
  if ((completedValuePattern.test(textBeforeCursor) || completedNumberPattern.test(textBeforeCursor)) && !afterLogicalOp.test(textBeforeCursor)) {
316
320
  // After a complete value, suggest logical operators
317
321
  newSuggestions.push({ text: ' && ', description: 'Logical AND - add another condition', type: 'operator' }, { text: ' || ', description: 'Logical OR - add alternative condition', type: 'operator' });
@@ -436,7 +440,7 @@ function FilterBuilderWithAutocomplete({ onFilterChange, initialFilter = '', ini
436
440
  return 'outline';
437
441
  }
438
442
  };
439
- return (jsxs(Card, { className: `p-8 rounded-xl mb-6 ${className}`, children: [jsxs(CardHeader, { className: "flex flex-row justify-between items-center p-0 mb-6 pb-4 border-b border-border", children: [jsx("h3", { className: "m-0 text-2xl font-semibold text-foreground", children: "Search Audit Logs" }), jsxs("div", { className: "flex gap-2", children: [jsx(Button, { onClick: () => setShowShortcuts(!showShortcuts), type: "tertiary", theme: "outline", size: "small", htmlType: "button", title: "View keyboard shortcuts", children: "Shortcuts" }), jsx(Button, { onClick: () => setShowHelp(!showHelp), type: "tertiary", theme: "outline", size: "small", htmlType: "button", title: "View available filter fields and examples", children: "Field Guide" })] })] }), jsxs(CardContent, { className: "p-0", children: [showShortcuts && (jsxs("div", { className: "bg-blue-50 border border-blue-500 dark:bg-blue-950/50 dark:border-blue-700 p-6 rounded-lg mb-6 max-h-96 overflow-y-auto", children: [jsx("h4", { className: "mt-0 mb-4 text-lg font-semibold text-foreground", children: "Keyboard Shortcuts" }), jsxs("div", { className: "grid grid-cols-[auto_1fr] gap-x-6 gap-y-3 items-start", children: [jsx("kbd", { className: "px-2 py-1 bg-background border border-input rounded font-mono text-sm", children: "Ctrl+Space" }), jsx("span", { children: "Trigger autocomplete suggestions at cursor position" }), jsx("kbd", { className: "px-2 py-1 bg-background border border-input rounded font-mono text-sm", children: "\u2191 / \u2193" }), jsx("span", { children: "Navigate through autocomplete suggestions" }), jsx("kbd", { className: "px-2 py-1 bg-background border border-input rounded font-mono text-sm", children: "Tab" }), jsx("span", { children: "Accept the selected suggestion" }), jsx("kbd", { className: "px-2 py-1 bg-background border border-input rounded font-mono text-sm", children: "Enter" }), jsx("span", { children: "Accept the selected suggestion" }), jsx("kbd", { className: "px-2 py-1 bg-background border border-input rounded font-mono text-sm", children: "Esc" }), jsx("span", { children: "Close autocomplete suggestions" })] }), jsxs("div", { className: "mt-4 p-3 bg-background rounded text-sm", children: [jsx("strong", { children: "Pro Tips:" }), jsxs("ul", { className: "mt-2 mb-0 pl-6", children: [jsx("li", { children: "Autocomplete appears automatically as you type" }), jsxs("li", { children: ["After completing a value (e.g., ", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "\"get\"" }), "), suggestions show logical operators (", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "&&" }), ", ", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "||" }), ")"] }), jsxs("li", { children: ["After typing ", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "&&" }), " or ", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "||" }), ", all field names are suggested"] }), jsxs("li", { children: ["Type ", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "." }), " after a field name to see available string functions"] })] })] })] })), showHelp && (jsxs("div", { className: "bg-muted p-6 rounded-lg mb-6 max-h-96 overflow-y-auto", children: [jsx("h4", { className: "mt-0 mb-4 text-lg font-semibold text-foreground", children: "Available Fields & Examples" }), jsx("p", { className: "mb-4 text-muted-foreground text-sm", children: "Click \"Insert\" to add an example to your query" }), jsx("div", { className: "mt-4", children: FILTER_FIELDS.map((field) => (jsxs("div", { className: "mb-6 pb-4 border-b border-border last:border-b-0", children: [jsxs("div", { className: "flex justify-between items-center mb-2", children: [jsx("strong", { className: "text-foreground", children: field.name }), jsx("span", { className: "text-sm text-muted-foreground italic", children: field.type })] }), jsx("p", { className: "my-2 text-muted-foreground", children: field.description }), field.examples && field.examples.length > 0 && (jsxs("div", { className: "mt-2", children: [jsx("strong", { className: "text-sm text-foreground", children: "Examples:" }), field.examples.map((example, idx) => (jsxs("div", { className: "flex items-center gap-2 my-2", children: [jsx("code", { className: "flex-1 px-2 py-1 bg-background rounded text-sm font-mono", children: example }), jsx(Button, { onClick: () => insertExample(example), type: "primary", theme: "solid", size: "small", htmlType: "button", className: "bg-green-600 hover:bg-green-700 dark:bg-green-700 dark:hover:bg-green-600", children: "Insert" })] }, idx)))] }))] }, field.name))) })] })), jsxs("div", { className: "relative mb-4", children: [jsxs(Label, { htmlFor: "filter-input", className: "block mb-2", children: [jsx("strong", { className: "text-foreground", children: "Filter Expression" }), jsx("span", { className: "ml-2 text-sm text-muted-foreground font-normal", children: "Press Ctrl+Space for suggestions" })] }), jsx("p", { className: "m-0 mb-3 text-sm text-muted-foreground", children: "Use autocomplete to build your query, or type field names directly (e.g., verb, objectRef.resource)" }), jsx(Textarea, { ref: textareaRef, id: "filter-input", value: filter, onChange: (e) => handleFilterChange(e.target.value), onKeyDown: handleKeyDown, onFocus: () => updateSuggestions(filter), placeholder: 'Example: verb == "delete" && objectRef.namespace == "production"', rows: 4, className: "font-mono text-sm bg-muted resize-y focus:bg-background" }), showSuggestions && suggestions.length > 0 && (jsx("div", { className: "absolute top-full left-0 right-0 max-h-72 overflow-y-auto bg-background border border-input border-t-0 rounded-b shadow-md z-50 -mt-1", children: suggestions.map((suggestion, idx) => (jsxs("div", { className: `px-4 py-3 cursor-pointer flex items-center gap-3 border-b border-border transition-colors ${idx === selectedSuggestion ? 'bg-pink-100' : 'hover:bg-muted'}`, onClick: () => insertSuggestion(suggestion), onMouseEnter: () => setSelectedSuggestion(idx), children: [jsx("span", { className: "font-mono font-semibold text-foreground min-w-[120px]", children: suggestion.text }), jsx("span", { className: "flex-1 text-muted-foreground text-sm", children: suggestion.description }), jsx(Badge, { variant: getBadgeVariant(suggestion.type), className: "uppercase", children: suggestion.type })] }, idx))) }))] }), jsxs("div", { className: "mb-4", children: [jsxs(Label, { htmlFor: "limit-input", className: "block mb-2", children: [jsx("strong", { className: "text-foreground", children: "Number of results" }), jsx("span", { className: "ml-2 text-sm text-muted-foreground font-normal", children: "(1 to 1,000 events)" })] }), jsx(Input, { id: "limit-input", type: "number", value: limit, onChange: (e) => handleLimitChange(parseInt(e.target.value) || 100), min: 1, max: 1000, className: "w-44 bg-muted focus:bg-background" })] })] })] }));
443
+ return (jsxs(Card, { className: `p-8 rounded-xl mb-6 ${className}`, children: [jsxs(CardHeader, { className: "flex flex-row justify-between items-center p-0 mb-6 pb-4 border-b border-border", children: [jsx("h3", { className: "m-0 text-2xl font-semibold text-foreground", children: "Search Audit Logs" }), jsxs("div", { className: "flex gap-2", children: [jsx(Button, { onClick: () => setShowShortcuts(!showShortcuts), type: "tertiary", theme: "outline", size: "small", htmlType: "button", title: "View keyboard shortcuts", children: "Shortcuts" }), jsx(Button, { onClick: () => setShowHelp(!showHelp), type: "tertiary", theme: "outline", size: "small", htmlType: "button", title: "View available filter fields and examples", children: "Field Guide" })] })] }), jsxs(CardContent, { className: "p-0", children: [showShortcuts && (jsxs("div", { className: "bg-[var(--info-100)] border border-[var(--info-300)] p-6 rounded-lg mb-6 max-h-96 overflow-y-auto", children: [jsx("h4", { className: "mt-0 mb-4 text-lg font-semibold text-foreground", children: "Keyboard Shortcuts" }), jsxs("div", { className: "grid grid-cols-[auto_1fr] gap-x-6 gap-y-3 items-start", children: [jsx("kbd", { className: "px-2 py-1 bg-background border border-input rounded font-mono text-sm", children: "Ctrl+Space" }), jsx("span", { children: "Trigger autocomplete suggestions at cursor position" }), jsx("kbd", { className: "px-2 py-1 bg-background border border-input rounded font-mono text-sm", children: "\u2191 / \u2193" }), jsx("span", { children: "Navigate through autocomplete suggestions" }), jsx("kbd", { className: "px-2 py-1 bg-background border border-input rounded font-mono text-sm", children: "Tab" }), jsx("span", { children: "Accept the selected suggestion" }), jsx("kbd", { className: "px-2 py-1 bg-background border border-input rounded font-mono text-sm", children: "Enter" }), jsx("span", { children: "Accept the selected suggestion" }), jsx("kbd", { className: "px-2 py-1 bg-background border border-input rounded font-mono text-sm", children: "Esc" }), jsx("span", { children: "Close autocomplete suggestions" })] }), jsxs("div", { className: "mt-4 p-3 bg-background rounded text-sm", children: [jsx("strong", { children: "Pro Tips:" }), jsxs("ul", { className: "mt-2 mb-0 pl-6", children: [jsx("li", { children: "Autocomplete appears automatically as you type" }), jsxs("li", { children: ["After completing a value (e.g., ", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "\"get\"" }), "), suggestions show logical operators (", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "&&" }), ", ", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "||" }), ")"] }), jsxs("li", { children: ["After typing ", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "&&" }), " or ", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "||" }), ", all field names are suggested"] }), jsxs("li", { children: ["Type ", jsx("code", { className: "px-1 py-0.5 bg-muted rounded text-sm", children: "." }), " after a field name to see available string functions"] })] })] })] })), showHelp && (jsxs("div", { className: "bg-muted p-6 rounded-lg mb-6 max-h-96 overflow-y-auto", children: [jsx("h4", { className: "mt-0 mb-4 text-lg font-semibold text-foreground", children: "Available Fields & Examples" }), jsx("p", { className: "mb-4 text-muted-foreground text-sm", children: "Click \"Insert\" to add an example to your query" }), jsx("div", { className: "mt-4", children: FILTER_FIELDS.map((field) => (jsxs("div", { className: "mb-6 pb-4 border-b border-border last:border-b-0", children: [jsxs("div", { className: "flex justify-between items-center mb-2", children: [jsx("strong", { className: "text-foreground", children: field.name }), jsx("span", { className: "text-sm text-muted-foreground italic", children: field.type })] }), jsx("p", { className: "my-2 text-muted-foreground", children: field.description }), field.examples && field.examples.length > 0 && (jsxs("div", { className: "mt-2", children: [jsx("strong", { className: "text-sm text-foreground", children: "Examples:" }), field.examples.map((example, idx) => (jsxs("div", { className: "flex items-center gap-2 my-2", children: [jsx("code", { className: "flex-1 px-2 py-1 bg-background rounded text-sm font-mono", children: example }), jsx(Button, { onClick: () => insertExample(example), type: "success", theme: "solid", size: "small", htmlType: "button", children: "Insert" })] }, idx)))] }))] }, field.name))) })] })), jsxs("div", { className: "relative mb-4", children: [jsxs(Label, { htmlFor: "filter-input", className: "block mb-2", children: [jsx("strong", { className: "text-foreground", children: "Filter Expression" }), jsx("span", { className: "ml-2 text-sm text-muted-foreground font-normal", children: "Press Ctrl+Space for suggestions" })] }), jsx("p", { className: "m-0 mb-3 text-sm text-muted-foreground", children: "Use autocomplete to build your query, or type field names directly (e.g., verb, objectRef.resource)" }), jsx(Textarea, { ref: textareaRef, id: "filter-input", value: filter, onChange: (e) => handleFilterChange(e.target.value), onKeyDown: handleKeyDown, onFocus: () => updateSuggestions(filter), placeholder: 'Example: verb == "delete" && objectRef.namespace == "production"', rows: 4, className: "font-mono text-sm bg-muted resize-y focus:bg-background" }), showSuggestions && suggestions.length > 0 && (jsx("div", { className: "absolute top-full left-0 right-0 max-h-72 overflow-y-auto bg-background border border-input border-t-0 rounded-b shadow-md z-50 -mt-1", children: suggestions.map((suggestion, idx) => (jsxs("div", { className: `px-4 py-3 cursor-pointer flex items-center gap-3 border-b border-border transition-colors ${idx === selectedSuggestion ? 'bg-accent' : 'hover:bg-muted'}`, onClick: () => insertSuggestion(suggestion), onMouseEnter: () => setSelectedSuggestion(idx), children: [jsx("span", { className: "font-mono font-semibold text-foreground min-w-[120px]", children: suggestion.text }), jsx("span", { className: "flex-1 text-muted-foreground text-sm", children: suggestion.description }), jsx(Badge, { variant: getBadgeVariant(suggestion.type), className: "uppercase", children: suggestion.type })] }, idx))) }))] }), jsxs("div", { className: "mb-4", children: [jsxs(Label, { htmlFor: "limit-input", className: "block mb-2", children: [jsx("strong", { className: "text-foreground", children: "Number of results" }), jsx("span", { className: "ml-2 text-sm text-muted-foreground font-normal", children: "(1 to 1,000 events)" })] }), jsx(Input, { id: "limit-input", type: "number", value: limit, onChange: (e) => handleLimitChange(parseInt(e.target.value) || 100), min: 1, max: 1000, className: "w-44 bg-muted focus:bg-background" })] })] })] }));
440
444
  }
441
445
 
442
446
  /**
@@ -1184,6 +1188,48 @@ function constructNow(date) {
1184
1188
  return constructFrom(date, Date.now());
1185
1189
  }
1186
1190
 
1191
+ /**
1192
+ * The {@link isSameDay} function options.
1193
+ */
1194
+
1195
+ /**
1196
+ * @name isSameDay
1197
+ * @category Day Helpers
1198
+ * @summary Are the given dates in the same day (and year and month)?
1199
+ *
1200
+ * @description
1201
+ * Are the given dates in the same day (and year and month)?
1202
+ *
1203
+ * @param laterDate - The first date to check
1204
+ * @param earlierDate - The second date to check
1205
+ * @param options - An object with options
1206
+ *
1207
+ * @returns The dates are in the same day (and year and month)
1208
+ *
1209
+ * @example
1210
+ * // Are 4 September 06:00:00 and 4 September 18:00:00 in the same day?
1211
+ * const result = isSameDay(new Date(2014, 8, 4, 6, 0), new Date(2014, 8, 4, 18, 0))
1212
+ * //=> true
1213
+ *
1214
+ * @example
1215
+ * // Are 4 September and 4 October in the same day?
1216
+ * const result = isSameDay(new Date(2014, 8, 4), new Date(2014, 9, 4))
1217
+ * //=> false
1218
+ *
1219
+ * @example
1220
+ * // Are 4 September, 2014 and 4 September, 2015 in the same day?
1221
+ * const result = isSameDay(new Date(2014, 8, 4), new Date(2015, 8, 4))
1222
+ * //=> false
1223
+ */
1224
+ function isSameDay(laterDate, earlierDate, options) {
1225
+ const [dateLeft_, dateRight_] = normalizeDates(
1226
+ options?.in,
1227
+ laterDate,
1228
+ earlierDate,
1229
+ );
1230
+ return +startOfDay(dateLeft_) === +startOfDay(dateRight_);
1231
+ }
1232
+
1187
1233
  /**
1188
1234
  * @name isDate
1189
1235
  * @category Common Helpers
@@ -3920,6 +3966,36 @@ function formatISO(date, options) {
3920
3966
  return result;
3921
3967
  }
3922
3968
 
3969
+ /**
3970
+ * The {@link isToday} function options.
3971
+ */
3972
+
3973
+ /**
3974
+ * @name isToday
3975
+ * @category Day Helpers
3976
+ * @summary Is the given date today?
3977
+ * @pure false
3978
+ *
3979
+ * @description
3980
+ * Is the given date today?
3981
+ *
3982
+ * @param date - The date to check
3983
+ * @param options - An object with options
3984
+ *
3985
+ * @returns The date is today
3986
+ *
3987
+ * @example
3988
+ * // If today is 6 October 2014, is 6 October 14:00:00 today?
3989
+ * const result = isToday(new Date(2014, 9, 6, 14, 0))
3990
+ * //=> true
3991
+ */
3992
+ function isToday(date, options) {
3993
+ return isSameDay(
3994
+ constructFrom(date, date),
3995
+ constructNow(date),
3996
+ );
3997
+ }
3998
+
3923
3999
  /**
3924
4000
  * The {@link subDays} function options.
3925
4001
  */
@@ -3947,6 +4023,36 @@ function subDays(date, amount, options) {
3947
4023
  return addDays(date, -amount, options);
3948
4024
  }
3949
4025
 
4026
+ /**
4027
+ * The {@link isYesterday} function options.
4028
+ */
4029
+
4030
+ /**
4031
+ * @name isYesterday
4032
+ * @category Day Helpers
4033
+ * @summary Is the given date yesterday?
4034
+ * @pure false
4035
+ *
4036
+ * @description
4037
+ * Is the given date yesterday?
4038
+ *
4039
+ * @param date - The date to check
4040
+ * @param options - An object with options
4041
+ *
4042
+ * @returns The date is yesterday
4043
+ *
4044
+ * @example
4045
+ * // If today is 6 October 2014, is 5 October 14:00:00 yesterday?
4046
+ * const result = isYesterday(new Date(2014, 9, 5, 14, 0))
4047
+ * //=> true
4048
+ */
4049
+ function isYesterday(date, options) {
4050
+ return isSameDay(
4051
+ constructFrom(date, date),
4052
+ subDays(constructNow(date), 1),
4053
+ );
4054
+ }
4055
+
3950
4056
  /**
3951
4057
  * The {@link subHours} function options.
3952
4058
  */
@@ -4007,649 +4113,413 @@ function subMinutes(date, amount, options) {
4007
4113
  return addMinutes(date, -15, options);
4008
4114
  }
4009
4115
 
4010
- /**
4011
- * @license lucide-react v0.577.0 - ISC
4012
- *
4013
- * This source code is licensed under the ISC license.
4014
- * See the LICENSE file in the root directory of this source tree.
4015
- */
4016
-
4017
- const mergeClasses = (...classes) => classes.filter((className, index, array) => {
4018
- return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
4019
- }).join(" ").trim();
4020
-
4021
- /**
4022
- * @license lucide-react v0.577.0 - ISC
4023
- *
4024
- * This source code is licensed under the ISC license.
4025
- * See the LICENSE file in the root directory of this source tree.
4026
- */
4027
-
4028
- const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
4029
-
4030
- /**
4031
- * @license lucide-react v0.577.0 - ISC
4032
- *
4033
- * This source code is licensed under the ISC license.
4034
- * See the LICENSE file in the root directory of this source tree.
4035
- */
4036
-
4037
- const toCamelCase = (string) => string.replace(
4038
- /^([A-Z])|[\s-_]+(\w)/g,
4039
- (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()
4040
- );
4041
-
4042
- /**
4043
- * @license lucide-react v0.577.0 - ISC
4044
- *
4045
- * This source code is licensed under the ISC license.
4046
- * See the LICENSE file in the root directory of this source tree.
4047
- */
4048
-
4049
-
4050
- const toPascalCase = (string) => {
4051
- const camelCase = toCamelCase(string);
4052
- return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
4053
- };
4054
-
4055
- /**
4056
- * @license lucide-react v0.577.0 - ISC
4057
- *
4058
- * This source code is licensed under the ISC license.
4059
- * See the LICENSE file in the root directory of this source tree.
4060
- */
4061
-
4062
- var defaultAttributes = {
4063
- xmlns: "http://www.w3.org/2000/svg",
4064
- width: 24,
4065
- height: 24,
4066
- viewBox: "0 0 24 24",
4067
- fill: "none",
4068
- stroke: "currentColor",
4069
- strokeWidth: 2,
4070
- strokeLinecap: "round",
4071
- strokeLinejoin: "round"
4072
- };
4073
-
4074
- /**
4075
- * @license lucide-react v0.577.0 - ISC
4076
- *
4077
- * This source code is licensed under the ISC license.
4078
- * See the LICENSE file in the root directory of this source tree.
4079
- */
4080
-
4081
- const hasA11yProp = (props) => {
4082
- for (const prop in props) {
4083
- if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
4084
- return true;
4085
- }
4086
- }
4087
- return false;
4088
- };
4116
+ /** Sentinel for the "All" item; the public value stays '' for "no filter". */
4117
+ const ALL_VALUE = '__all__';
4118
+ function Combobox({ options, value, onValueChange, placeholder = 'Select…', searchPlaceholder = 'Search…', emptyMessage = 'No results', disabled, loading, className, clearable = false, showAllOption = true, allOptionLabel = 'All', }) {
4119
+ const items = React.useMemo(() => {
4120
+ const base = options.map((o) => ({ value: o.value, label: o.label, count: o.count }));
4121
+ return showAllOption ? [{ value: ALL_VALUE, label: allOptionLabel }, ...base] : base;
4122
+ }, [options, showAllOption, allOptionLabel]);
4123
+ // An empty value selects the "All" item so the trigger reads its label.
4124
+ const selectedValue = value === '' && showAllOption ? ALL_VALUE : value;
4125
+ return (jsx(Autocomplete, { className: className, options: items, value: selectedValue, onValueChange: (next) => onValueChange(next === ALL_VALUE ? '' : next), placeholder: placeholder, searchPlaceholder: searchPlaceholder, emptyContent: emptyMessage, disabled: disabled, loading: loading, footer: clearable && value ? (jsx("button", { type: "button", className: "text-muted-foreground hover:text-foreground w-full px-2 py-1.5 text-left text-xs", onClick: () => onValueChange(''), children: "Clear selection" })) : undefined, renderValue: (option) => (jsx("span", { className: "truncate", children: option.count !== undefined ? `${option.label} (${option.count})` : option.label })), renderOption: (option) => (jsxs("span", { className: "flex w-full items-center justify-between gap-2", children: [jsx("span", { className: "truncate", children: option.label }), 'count' in option && option.count !== undefined && (jsx("span", { className: "text-muted-foreground text-xs", children: option.count }))] })) }));
4126
+ }
4089
4127
 
4128
+ const RELATIVE_KEY = /^now-(\d+)([mhd])$/;
4129
+ /** Resolve a relative key such as `now-7d` to a `{ from, to }` window ending now. */
4130
+ function relativeKeyToRange(key, now = new Date()) {
4131
+ const match = RELATIVE_KEY.exec(key);
4132
+ if (!match)
4133
+ return null;
4134
+ const amount = Number(match[1]);
4135
+ const unitMs = { m: 60000, h: 3600000, d: 86400000 }[match[2]];
4136
+ return { from: new Date(now.getTime() - amount * unitMs), to: now };
4137
+ }
4138
+ /** `YYYY-MM-DDTHH:mm` in local time, the format the filter components store. */
4139
+ function toDatetimeLocal(iso) {
4140
+ const d = new Date(iso);
4141
+ const pad = (n) => String(n).padStart(2, '0');
4142
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
4143
+ }
4144
+ function toPickerPresets(presets) {
4145
+ return presets
4146
+ .filter((p) => RELATIVE_KEY.test(p.key))
4147
+ .map((p) => ({
4148
+ key: p.key,
4149
+ label: p.label,
4150
+ getRange: () => relativeKeyToRange(p.key),
4151
+ }));
4152
+ }
4090
4153
  /**
4091
- * @license lucide-react v0.577.0 - ISC
4092
- *
4093
- * This source code is licensed under the ISC license.
4094
- * See the LICENSE file in the root directory of this source tree.
4154
+ * TimeRangeDropdown - relative presets plus an absolute range, on datum-ui's
4155
+ * DateTimeRangePicker. The emitted value carries `preset` when a preset was
4156
+ * clicked, which keeps `start=now-7d` style URL state intact.
4095
4157
  */
4158
+ function TimeRangeDropdown({ presets, selectedPreset, onPresetSelect, onCustomRangeApply, customStart, customEnd, disabled = false, className, displayLabel, }) {
4159
+ const pickerPresets = React.useMemo(() => toPickerPresets(presets), [presets]);
4160
+ const value = React.useMemo(() => {
4161
+ if (selectedPreset !== 'custom') {
4162
+ const range = relativeKeyToRange(selectedPreset);
4163
+ return range
4164
+ ? { from: range.from.toISOString(), to: range.to.toISOString(), preset: selectedPreset }
4165
+ : null;
4166
+ }
4167
+ if (customStart && customEnd) {
4168
+ return { from: new Date(customStart).toISOString(), to: new Date(customEnd).toISOString() };
4169
+ }
4170
+ return null;
4171
+ }, [selectedPreset, customStart, customEnd]);
4172
+ const handleChange = (next) => {
4173
+ if (!next)
4174
+ return;
4175
+ const preset = next.preset;
4176
+ if (preset) {
4177
+ onPresetSelect(preset);
4178
+ return;
4179
+ }
4180
+ onCustomRangeApply(toDatetimeLocal(next.from), toDatetimeLocal(next.to));
4181
+ };
4182
+ return (jsx(DateTimeRangePicker$1, { className: className, triggerClassName: "h-9 rounded-lg px-3 text-xs", value: value, onChange: handleChange, presets: pickerPresets, disableFuture: true, clearable: false, disabled: disabled, placeholder: displayLabel ?? 'Select time range', triggerLabel: displayLabel ? () => displayLabel : undefined }));
4183
+ }
4096
4184
 
4097
-
4098
- const Icon = forwardRef(
4099
- ({
4100
- color = "currentColor",
4101
- size = 24,
4102
- strokeWidth = 2,
4103
- absoluteStrokeWidth,
4104
- className = "",
4105
- children,
4106
- iconNode,
4107
- ...rest
4108
- }, ref) => createElement(
4109
- "svg",
4185
+ const TIME_PRESETS$3 = [
4110
4186
  {
4111
- ref,
4112
- ...defaultAttributes,
4113
- width: size,
4114
- height: size,
4115
- stroke: color,
4116
- strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
4117
- className: mergeClasses("lucide", className),
4118
- ...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
4119
- ...rest
4187
+ key: 'last15min',
4188
+ label: 'Last 15 min',
4189
+ getValue: () => ({
4190
+ start: formatISO(subMinutes(new Date())),
4191
+ end: formatISO(new Date()),
4192
+ }),
4120
4193
  },
4121
- [
4122
- ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
4123
- ...Array.isArray(children) ? children : [children]
4124
- ]
4125
- )
4126
- );
4127
-
4194
+ {
4195
+ key: 'last1hour',
4196
+ label: 'Last hour',
4197
+ getValue: () => ({
4198
+ start: formatISO(subHours(new Date(), 1)),
4199
+ end: formatISO(new Date()),
4200
+ }),
4201
+ },
4202
+ {
4203
+ key: 'last6hours',
4204
+ label: 'Last 6 hours',
4205
+ getValue: () => ({
4206
+ start: formatISO(subHours(new Date(), 6)),
4207
+ end: formatISO(new Date()),
4208
+ }),
4209
+ },
4210
+ {
4211
+ key: 'last24hours',
4212
+ label: 'Last 24 hours',
4213
+ getValue: () => ({
4214
+ start: formatISO(subHours(new Date(), 24)),
4215
+ end: formatISO(new Date()),
4216
+ }),
4217
+ },
4218
+ {
4219
+ key: 'last7days',
4220
+ label: 'Last 7 days',
4221
+ getValue: () => ({
4222
+ start: formatISO(subDays(new Date(), 7)),
4223
+ end: formatISO(new Date()),
4224
+ }),
4225
+ },
4226
+ {
4227
+ key: 'last30days',
4228
+ label: 'Last 30 days',
4229
+ getValue: () => ({
4230
+ start: formatISO(subDays(new Date(), 30)),
4231
+ end: formatISO(new Date()),
4232
+ }),
4233
+ },
4234
+ ];
4235
+ /** Debounce delay for text inputs (ms) */
4236
+ const DEBOUNCE_DELAY = 300;
4128
4237
  /**
4129
- * @license lucide-react v0.577.0 - ISC
4130
- *
4131
- * This source code is licensed under the ISC license.
4132
- * See the LICENSE file in the root directory of this source tree.
4238
+ * Helper function to convert ISO string to datetime-local format
4133
4239
  */
4134
-
4135
-
4136
- const createLucideIcon = (iconName, iconNode) => {
4137
- const Component = forwardRef(
4138
- ({ className, ...props }, ref) => createElement(Icon, {
4139
- ref,
4140
- iconNode,
4141
- className: mergeClasses(
4142
- `lucide-${toKebabCase(toPascalCase(iconName))}`,
4143
- `lucide-${iconName}`,
4144
- className
4145
- ),
4146
- ...props
4147
- })
4148
- );
4149
- Component.displayName = toPascalCase(iconName);
4150
- return Component;
4240
+ const formatDatetimeLocal$4 = (isoString) => {
4241
+ const date = new Date(isoString);
4242
+ const year = date.getFullYear();
4243
+ const month = String(date.getMonth() + 1).padStart(2, '0');
4244
+ const day = String(date.getDate()).padStart(2, '0');
4245
+ const hours = String(date.getHours()).padStart(2, '0');
4246
+ const minutes = String(date.getMinutes()).padStart(2, '0');
4247
+ return `${year}-${month}-${day}T${hours}:${minutes}`;
4151
4248
  };
4152
-
4153
- /**
4154
- * @license lucide-react v0.577.0 - ISC
4155
- *
4156
- * This source code is licensed under the ISC license.
4157
- * See the LICENSE file in the root directory of this source tree.
4158
- */
4159
-
4160
-
4161
- const __iconNode$p = [
4162
- [
4163
- "path",
4164
- {
4165
- d: "M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",
4166
- key: "169zse"
4167
- }
4168
- ]
4169
- ];
4170
- const Activity = createLucideIcon("activity", __iconNode$p);
4171
-
4172
- /**
4173
- * @license lucide-react v0.577.0 - ISC
4174
- *
4175
- * This source code is licensed under the ISC license.
4176
- * See the LICENSE file in the root directory of this source tree.
4177
- */
4178
-
4179
-
4180
- const __iconNode$o = [
4181
- ["path", { d: "M10.268 21a2 2 0 0 0 3.464 0", key: "vwvbt9" }],
4182
- [
4183
- "path",
4184
- {
4185
- d: "M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326",
4186
- key: "11g9vi"
4249
+ function SimpleQueryBuilder({ client, onFilterChange, initialLimit = 100, className = '', disabled = false, }) {
4250
+ const [isAdvancedExpanded, setIsAdvancedExpanded] = useState(false);
4251
+ const [filters, setFilters] = useState({
4252
+ verb: '',
4253
+ resourceType: '',
4254
+ namespace: '',
4255
+ resourceName: '',
4256
+ username: '',
4257
+ });
4258
+ const [limit, setLimit] = useState(initialLimit);
4259
+ const [advancedFilter, setAdvancedFilter] = useState('');
4260
+ const [useAdvancedFilter, setUseAdvancedFilter] = useState(false);
4261
+ // Time range state
4262
+ const [selectedPreset, setSelectedPreset] = useState('last24hours');
4263
+ const [timeRange, setTimeRange] = useState(null);
4264
+ const [customStart, setCustomStart] = useState(() => formatDatetimeLocal$4(formatISO(subDays(new Date(), 1))));
4265
+ const [customEnd, setCustomEnd] = useState(() => formatDatetimeLocal$4(formatISO(new Date())));
4266
+ // Fetch facets for dynamic filter options
4267
+ const { verbs, resources, namespaces, usernames, isLoading: facetsLoading, error: facetsError } = useAuditLogFacets(client, timeRange);
4268
+ // Log facets error for debugging
4269
+ if (facetsError) {
4270
+ console.error('Failed to load audit log facets:', facetsError);
4187
4271
  }
4188
- ]
4189
- ];
4190
- const Bell = createLucideIcon("bell", __iconNode$o);
4191
-
4192
- /**
4193
- * @license lucide-react v0.577.0 - ISC
4194
- *
4195
- * This source code is licensed under the ISC license.
4196
- * See the LICENSE file in the root directory of this source tree.
4197
- */
4198
-
4199
-
4200
- const __iconNode$n = [
4201
- ["path", { d: "M8 2v4", key: "1cmpym" }],
4202
- ["path", { d: "M16 2v4", key: "4m81vk" }],
4203
- ["rect", { width: "18", height: "18", x: "3", y: "4", rx: "2", key: "1hopcy" }],
4204
- ["path", { d: "M3 10h18", key: "8toen8" }]
4205
- ];
4206
- const Calendar = createLucideIcon("calendar", __iconNode$n);
4207
-
4208
- /**
4209
- * @license lucide-react v0.577.0 - ISC
4210
- *
4211
- * This source code is licensed under the ISC license.
4212
- * See the LICENSE file in the root directory of this source tree.
4213
- */
4214
-
4272
+ // Refs for debouncing
4273
+ const debounceTimerRef = useRef(null);
4274
+ const isInitialMount = useRef(true);
4275
+ // Initialize with default time range and trigger initial query
4276
+ useEffect(() => {
4277
+ const defaultRange = TIME_PRESETS$3.find(p => p.key === 'last24hours')?.getValue();
4278
+ if (defaultRange) {
4279
+ setTimeRange(defaultRange);
4280
+ }
4281
+ }, []);
4282
+ const generateCEL = useCallback(() => {
4283
+ const parts = [];
4284
+ if (filters.verb) {
4285
+ parts.push(`verb == "${filters.verb}"`);
4286
+ }
4287
+ if (filters.resourceType) {
4288
+ parts.push(`objectRef.resource == "${filters.resourceType}"`);
4289
+ }
4290
+ if (filters.namespace) {
4291
+ parts.push(`objectRef.namespace == "${filters.namespace}"`);
4292
+ }
4293
+ if (filters.resourceName) {
4294
+ parts.push(`objectRef.name.contains("${filters.resourceName}")`);
4295
+ }
4296
+ if (filters.username) {
4297
+ parts.push(`user.username.contains("${filters.username}")`);
4298
+ }
4299
+ return parts.join(' && ');
4300
+ }, [filters]);
4301
+ // Execute query when filters change (with debouncing for text inputs)
4302
+ const executeQuery = useCallback(() => {
4303
+ if (!timeRange || disabled)
4304
+ return;
4305
+ const filter = useAdvancedFilter ? advancedFilter : generateCEL();
4306
+ onFilterChange({
4307
+ filter,
4308
+ limit,
4309
+ startTime: timeRange.start,
4310
+ endTime: timeRange.end
4311
+ });
4312
+ }, [useAdvancedFilter, advancedFilter, generateCEL, onFilterChange, limit, timeRange, disabled]);
4313
+ // Auto-execute query when dropdown filters or time range change (immediate)
4314
+ useEffect(() => {
4315
+ // Skip the initial mount - we want to wait for timeRange to be set
4316
+ if (isInitialMount.current) {
4317
+ isInitialMount.current = false;
4318
+ return;
4319
+ }
4320
+ // Clear any pending debounced query
4321
+ if (debounceTimerRef.current) {
4322
+ clearTimeout(debounceTimerRef.current);
4323
+ }
4324
+ executeQuery();
4325
+ }, [filters.verb, filters.resourceType, timeRange, limit, useAdvancedFilter]);
4326
+ // Auto-execute query when text filters change (debounced)
4327
+ useEffect(() => {
4328
+ if (isInitialMount.current || !timeRange)
4329
+ return;
4330
+ // Clear previous timer
4331
+ if (debounceTimerRef.current) {
4332
+ clearTimeout(debounceTimerRef.current);
4333
+ }
4334
+ // Set new debounced timer
4335
+ debounceTimerRef.current = setTimeout(() => {
4336
+ executeQuery();
4337
+ }, DEBOUNCE_DELAY);
4338
+ return () => {
4339
+ if (debounceTimerRef.current) {
4340
+ clearTimeout(debounceTimerRef.current);
4341
+ }
4342
+ };
4343
+ }, [filters.namespace, filters.resourceName, filters.username, advancedFilter]);
4344
+ const handleFilterChange = useCallback((field, value) => {
4345
+ setFilters(prev => ({ ...prev, [field]: value }));
4346
+ }, []);
4347
+ const handleTimePresetSelect = useCallback((presetKey) => {
4348
+ const preset = TIME_PRESETS$3.find(p => p.key === presetKey);
4349
+ if (preset) {
4350
+ setSelectedPreset(presetKey);
4351
+ setTimeRange(preset.getValue());
4352
+ }
4353
+ }, []);
4354
+ const handleCustomRangeApply = useCallback((start, end) => {
4355
+ setSelectedPreset('custom');
4356
+ setCustomStart(start);
4357
+ setCustomEnd(end);
4358
+ setTimeRange({
4359
+ start: new Date(start).toISOString(),
4360
+ end: new Date(end).toISOString(),
4361
+ });
4362
+ }, []);
4363
+ const handleLimitChange = useCallback((newLimit) => {
4364
+ const validLimit = Math.min(Math.max(1, newLimit), 1000);
4365
+ setLimit(validLimit);
4366
+ }, []);
4367
+ const handleAdvancedToggle = useCallback(() => {
4368
+ if (!isAdvancedExpanded) {
4369
+ // When expanding, populate advanced filter with generated CEL
4370
+ setAdvancedFilter(generateCEL());
4371
+ }
4372
+ setIsAdvancedExpanded(!isAdvancedExpanded);
4373
+ }, [isAdvancedExpanded, generateCEL]);
4374
+ const handleUseAdvancedChange = useCallback((checked) => {
4375
+ setUseAdvancedFilter(checked === true);
4376
+ }, []);
4377
+ // Get display label for time range
4378
+ const getTimeRangeLabel = () => {
4379
+ if (selectedPreset === 'custom') {
4380
+ if (timeRange?.start && timeRange?.end) {
4381
+ const start = new Date(timeRange.start);
4382
+ const end = new Date(timeRange.end);
4383
+ return `${start.toLocaleDateString()} - ${end.toLocaleDateString()}`;
4384
+ }
4385
+ return 'Custom';
4386
+ }
4387
+ const preset = TIME_PRESETS$3.find(p => p.key === selectedPreset);
4388
+ return preset?.label || 'Select time range';
4389
+ };
4390
+ return (jsxs("div", { className: `mb-6 pb-6 border-b border-border ${className}`, children: [jsxs("div", { className: "flex flex-wrap gap-4 items-end", children: [jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Action" }), jsx(Combobox, { options: verbs
4391
+ .filter(facet => facet.value)
4392
+ .map(facet => ({
4393
+ value: facet.value,
4394
+ label: facet.value,
4395
+ count: facet.count,
4396
+ })), value: filters.verb, onValueChange: (value) => handleFilterChange('verb', value), placeholder: "All", searchPlaceholder: "Search actions...", allOptionLabel: "All", disabled: disabled || useAdvancedFilter, loading: facetsLoading, className: "min-w-[130px]" })] }), jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Resource" }), jsx(Combobox, { options: resources
4397
+ .filter(facet => facet.value)
4398
+ .map(facet => ({
4399
+ value: facet.value,
4400
+ label: facet.value,
4401
+ count: facet.count,
4402
+ })), value: filters.resourceType, onValueChange: (value) => handleFilterChange('resourceType', value), placeholder: "All", searchPlaceholder: "Search resources...", allOptionLabel: "All", disabled: disabled || useAdvancedFilter, loading: facetsLoading, className: "min-w-[130px]" })] }), jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Namespace" }), jsx(Combobox, { options: namespaces
4403
+ .filter(facet => facet.value)
4404
+ .map(facet => ({
4405
+ value: facet.value,
4406
+ label: facet.value,
4407
+ count: facet.count,
4408
+ })), value: filters.namespace, onValueChange: (value) => handleFilterChange('namespace', value), placeholder: "All", searchPlaceholder: "Search namespaces...", allOptionLabel: "All", disabled: disabled || useAdvancedFilter, loading: facetsLoading, className: "min-w-[130px]" })] }), jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "User" }), jsx(Combobox, { options: usernames
4409
+ .filter(facet => facet.value)
4410
+ .map(facet => ({
4411
+ value: facet.value,
4412
+ label: facet.value,
4413
+ count: facet.count,
4414
+ })), value: filters.username, onValueChange: (value) => handleFilterChange('username', value), placeholder: "All", searchPlaceholder: "Search users...", allOptionLabel: "All", disabled: disabled || useAdvancedFilter, loading: facetsLoading, className: "min-w-[130px]" })] }), jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Name" }), jsx(Input, { type: "text", value: filters.resourceName, onChange: (e) => handleFilterChange('resourceName', e.target.value), placeholder: "Filter by name...", className: "min-w-[140px]", disabled: disabled || useAdvancedFilter })] }), jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Limit" }), jsx(Input, { type: "number", value: limit, onChange: (e) => handleLimitChange(parseInt(e.target.value) || 100), min: 1, max: 1000, className: "w-[80px]", disabled: disabled })] }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "small", className: "whitespace-nowrap text-muted-foreground hover:text-foreground", onClick: handleAdvancedToggle, "aria-expanded": isAdvancedExpanded, children: isAdvancedExpanded ? '- CEL' : '+ CEL' }), jsxs("div", { className: "flex flex-col gap-2 ml-auto", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Time Range" }), jsx(TimeRangeDropdown, { presets: TIME_PRESETS$3.map(p => ({ key: p.key, label: p.label })), selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() })] })] }), isAdvancedExpanded && (jsxs("div", { className: "mt-4 pt-4", children: [jsx(Separator, { className: "mb-4" }), jsx("div", { className: "mb-3", children: jsxs("div", { className: "flex items-center gap-2", children: [jsx(Checkbox, { id: "use-cel-expression", checked: useAdvancedFilter, onCheckedChange: handleUseAdvancedChange, disabled: disabled }), jsx(Label, { htmlFor: "use-cel-expression", className: "text-sm font-medium text-foreground cursor-pointer", children: "Use CEL Expression" })] }) }), useAdvancedFilter && (jsxs("div", { className: "mt-3", children: [jsx("p", { className: "m-0 mb-2 text-sm text-muted-foreground", children: "Write your query using CEL syntax for complex filters" }), jsx(Textarea, { value: advancedFilter, onChange: (e) => setAdvancedFilter(e.target.value), placeholder: 'Example: verb == "delete" && objectRef.namespace == "production"', rows: 3, className: "w-full font-mono resize-y", disabled: disabled })] }))] }))] }));
4415
+ }
4215
4416
 
4216
- const __iconNode$m = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
4217
- const Check = createLucideIcon("check", __iconNode$m);
4417
+ function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
4218
4418
 
4219
4419
  /**
4220
- * @license lucide-react v0.577.0 - ISC
4221
- *
4222
- * This source code is licensed under the ISC license.
4223
- * See the LICENSE file in the root directory of this source tree.
4420
+ * Concatenates two arrays faster than the array spread operator.
4224
4421
  */
4422
+ const concatArrays = (array1, array2) => {
4423
+ // Pre-allocate for better V8 optimization
4424
+ const combinedArray = new Array(array1.length + array2.length);
4425
+ for (let i = 0; i < array1.length; i++) {
4426
+ combinedArray[i] = array1[i];
4427
+ }
4428
+ for (let i = 0; i < array2.length; i++) {
4429
+ combinedArray[array1.length + i] = array2[i];
4430
+ }
4431
+ return combinedArray;
4432
+ };
4225
4433
 
4226
-
4227
- const __iconNode$l = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
4228
- const ChevronDown = createLucideIcon("chevron-down", __iconNode$l);
4229
-
4230
- /**
4231
- * @license lucide-react v0.577.0 - ISC
4232
- *
4233
- * This source code is licensed under the ISC license.
4234
- * See the LICENSE file in the root directory of this source tree.
4235
- */
4236
-
4237
-
4238
- const __iconNode$k = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
4239
- const ChevronRight = createLucideIcon("chevron-right", __iconNode$k);
4240
-
4241
- /**
4242
- * @license lucide-react v0.577.0 - ISC
4243
- *
4244
- * This source code is licensed under the ISC license.
4245
- * See the LICENSE file in the root directory of this source tree.
4246
- */
4247
-
4248
-
4249
- const __iconNode$j = [["path", { d: "m18 15-6-6-6 6", key: "153udz" }]];
4250
- const ChevronUp = createLucideIcon("chevron-up", __iconNode$j);
4251
-
4252
- /**
4253
- * @license lucide-react v0.577.0 - ISC
4254
- *
4255
- * This source code is licensed under the ISC license.
4256
- * See the LICENSE file in the root directory of this source tree.
4257
- */
4258
-
4259
-
4260
- const __iconNode$i = [
4261
- ["path", { d: "m7 15 5 5 5-5", key: "1hf1tw" }],
4262
- ["path", { d: "m7 9 5-5 5 5", key: "sgt6xg" }]
4263
- ];
4264
- const ChevronsUpDown = createLucideIcon("chevrons-up-down", __iconNode$i);
4265
-
4266
- /**
4267
- * @license lucide-react v0.577.0 - ISC
4268
- *
4269
- * This source code is licensed under the ISC license.
4270
- * See the LICENSE file in the root directory of this source tree.
4271
- */
4272
-
4273
-
4274
- const __iconNode$h = [
4275
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
4276
- ["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
4277
- ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
4278
- ];
4279
- const CircleAlert = createLucideIcon("circle-alert", __iconNode$h);
4280
-
4281
- /**
4282
- * @license lucide-react v0.577.0 - ISC
4283
- *
4284
- * This source code is licensed under the ISC license.
4285
- * See the LICENSE file in the root directory of this source tree.
4286
- */
4287
-
4288
-
4289
- const __iconNode$g = [
4290
- ["path", { d: "M21.801 10A10 10 0 1 1 17 3.335", key: "yps3ct" }],
4291
- ["path", { d: "m9 11 3 3L22 4", key: "1pflzl" }]
4292
- ];
4293
- const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$g);
4294
-
4295
- /**
4296
- * @license lucide-react v0.577.0 - ISC
4297
- *
4298
- * This source code is licensed under the ISC license.
4299
- * See the LICENSE file in the root directory of this source tree.
4300
- */
4301
-
4302
-
4303
- const __iconNode$f = [
4304
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
4305
- ["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
4306
- ];
4307
- const CircleCheck = createLucideIcon("circle-check", __iconNode$f);
4308
-
4309
- /**
4310
- * @license lucide-react v0.577.0 - ISC
4311
- *
4312
- * This source code is licensed under the ISC license.
4313
- * See the LICENSE file in the root directory of this source tree.
4314
- */
4315
-
4316
-
4317
- const __iconNode$e = [
4318
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
4319
- ["path", { d: "m15 9-6 6", key: "1uzhvr" }],
4320
- ["path", { d: "m9 9 6 6", key: "z0biqf" }]
4321
- ];
4322
- const CircleX = createLucideIcon("circle-x", __iconNode$e);
4323
-
4324
- /**
4325
- * @license lucide-react v0.577.0 - ISC
4326
- *
4327
- * This source code is licensed under the ISC license.
4328
- * See the LICENSE file in the root directory of this source tree.
4329
- */
4330
-
4331
-
4332
- const __iconNode$d = [
4333
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
4334
- ["path", { d: "M12 6v6l4 2", key: "mmk7yg" }]
4335
- ];
4336
- const Clock = createLucideIcon("clock", __iconNode$d);
4337
-
4338
- /**
4339
- * @license lucide-react v0.577.0 - ISC
4340
- *
4341
- * This source code is licensed under the ISC license.
4342
- * See the LICENSE file in the root directory of this source tree.
4343
- */
4344
-
4345
-
4346
- const __iconNode$c = [
4347
- ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
4348
- ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
4349
- ];
4350
- const Copy = createLucideIcon("copy", __iconNode$c);
4351
-
4352
- /**
4353
- * @license lucide-react v0.577.0 - ISC
4354
- *
4355
- * This source code is licensed under the ISC license.
4356
- * See the LICENSE file in the root directory of this source tree.
4357
- */
4358
-
4359
-
4360
- const __iconNode$b = [
4361
- [
4362
- "path",
4363
- {
4364
- d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",
4365
- key: "1nclc0"
4434
+ // Factory function ensures consistent object shapes
4435
+ const createClassValidatorObject = (classGroupId, validator) => ({
4436
+ classGroupId,
4437
+ validator
4438
+ });
4439
+ // Factory ensures consistent ClassPartObject shape
4440
+ const createClassPartObject = (nextPart = new Map(), validators = null, classGroupId) => ({
4441
+ nextPart,
4442
+ validators,
4443
+ classGroupId
4444
+ });
4445
+ const CLASS_PART_SEPARATOR = '-';
4446
+ const EMPTY_CONFLICTS = [];
4447
+ // I use two dots here because one dot is used as prefix for class groups in plugins
4448
+ const ARBITRARY_PROPERTY_PREFIX = 'arbitrary..';
4449
+ const createClassGroupUtils = config => {
4450
+ const classMap = createClassMap(config);
4451
+ const {
4452
+ conflictingClassGroups,
4453
+ conflictingClassGroupModifiers
4454
+ } = config;
4455
+ const getClassGroupId = className => {
4456
+ if (className.startsWith('[') && className.endsWith(']')) {
4457
+ return getGroupIdForArbitraryProperty(className);
4366
4458
  }
4367
- ],
4368
- ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
4369
- ];
4370
- const Eye = createLucideIcon("eye", __iconNode$b);
4371
-
4372
- /**
4373
- * @license lucide-react v0.577.0 - ISC
4374
- *
4375
- * This source code is licensed under the ISC license.
4376
- * See the LICENSE file in the root directory of this source tree.
4377
- */
4378
-
4379
-
4380
- const __iconNode$a = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
4381
- const LoaderCircle = createLucideIcon("loader-circle", __iconNode$a);
4382
-
4383
- /**
4384
- * @license lucide-react v0.577.0 - ISC
4385
- *
4386
- * This source code is licensed under the ISC license.
4387
- * See the LICENSE file in the root directory of this source tree.
4388
- */
4389
-
4390
-
4391
- const __iconNode$9 = [
4392
- [
4393
- "path",
4394
- {
4395
- d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
4396
- key: "1a8usu"
4459
+ const classParts = className.split(CLASS_PART_SEPARATOR);
4460
+ // Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and skip it.
4461
+ const startIndex = classParts[0] === '' && classParts.length > 1 ? 1 : 0;
4462
+ return getGroupRecursive(classParts, startIndex, classMap);
4463
+ };
4464
+ const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {
4465
+ if (hasPostfixModifier) {
4466
+ const modifierConflicts = conflictingClassGroupModifiers[classGroupId];
4467
+ const baseConflicts = conflictingClassGroups[classGroupId];
4468
+ if (modifierConflicts) {
4469
+ if (baseConflicts) {
4470
+ // Merge base conflicts with modifier conflicts
4471
+ return concatArrays(baseConflicts, modifierConflicts);
4472
+ }
4473
+ // Only modifier conflicts
4474
+ return modifierConflicts;
4475
+ }
4476
+ // Fall back to without postfix if no modifier conflicts
4477
+ return baseConflicts || EMPTY_CONFLICTS;
4397
4478
  }
4398
- ],
4399
- ["path", { d: "m15 5 4 4", key: "1mk7zo" }]
4400
- ];
4401
- const Pencil = createLucideIcon("pencil", __iconNode$9);
4402
-
4479
+ return conflictingClassGroups[classGroupId] || EMPTY_CONFLICTS;
4480
+ };
4481
+ return {
4482
+ getClassGroupId,
4483
+ getConflictingClassGroupIds
4484
+ };
4485
+ };
4486
+ const getGroupRecursive = (classParts, startIndex, classPartObject) => {
4487
+ const classPathsLength = classParts.length - startIndex;
4488
+ if (classPathsLength === 0) {
4489
+ return classPartObject.classGroupId;
4490
+ }
4491
+ const currentClassPart = classParts[startIndex];
4492
+ const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);
4493
+ if (nextClassPartObject) {
4494
+ const result = getGroupRecursive(classParts, startIndex + 1, nextClassPartObject);
4495
+ if (result) return result;
4496
+ }
4497
+ const validators = classPartObject.validators;
4498
+ if (validators === null) {
4499
+ return undefined;
4500
+ }
4501
+ // Build classRest string efficiently by joining from startIndex onwards
4502
+ const classRest = startIndex === 0 ? classParts.join(CLASS_PART_SEPARATOR) : classParts.slice(startIndex).join(CLASS_PART_SEPARATOR);
4503
+ const validatorsLength = validators.length;
4504
+ for (let i = 0; i < validatorsLength; i++) {
4505
+ const validatorObj = validators[i];
4506
+ if (validatorObj.validator(classRest)) {
4507
+ return validatorObj.classGroupId;
4508
+ }
4509
+ }
4510
+ return undefined;
4511
+ };
4403
4512
  /**
4404
- * @license lucide-react v0.577.0 - ISC
4513
+ * Get the class group ID for an arbitrary property.
4405
4514
  *
4406
- * This source code is licensed under the ISC license.
4407
- * See the LICENSE file in the root directory of this source tree.
4515
+ * @param className - The class name to get the group ID for. Is expected to be string starting with `[` and ending with `]`.
4408
4516
  */
4409
-
4410
-
4411
- const __iconNode$8 = [
4412
- ["path", { d: "M5 12h14", key: "1ays0h" }],
4413
- ["path", { d: "M12 5v14", key: "s699le" }]
4414
- ];
4415
- const Plus = createLucideIcon("plus", __iconNode$8);
4416
-
4417
- /**
4418
- * @license lucide-react v0.577.0 - ISC
4419
- *
4420
- * This source code is licensed under the ISC license.
4421
- * See the LICENSE file in the root directory of this source tree.
4422
- */
4423
-
4424
-
4425
- const __iconNode$7 = [
4426
- ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
4427
- ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
4428
- ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
4429
- ["path", { d: "M8 16H3v5", key: "1cv678" }]
4430
- ];
4431
- const RefreshCw = createLucideIcon("refresh-cw", __iconNode$7);
4432
-
4433
- /**
4434
- * @license lucide-react v0.577.0 - ISC
4435
- *
4436
- * This source code is licensed under the ISC license.
4437
- * See the LICENSE file in the root directory of this source tree.
4438
- */
4439
-
4440
-
4441
- const __iconNode$6 = [
4442
- ["path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8", key: "1p45f6" }],
4443
- ["path", { d: "M21 3v5h-5", key: "1q7to0" }]
4444
- ];
4445
- const RotateCw = createLucideIcon("rotate-cw", __iconNode$6);
4446
-
4447
- /**
4448
- * @license lucide-react v0.577.0 - ISC
4449
- *
4450
- * This source code is licensed under the ISC license.
4451
- * See the LICENSE file in the root directory of this source tree.
4452
- */
4453
-
4454
-
4455
- const __iconNode$5 = [
4456
- ["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
4457
- ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
4458
- ];
4459
- const Search = createLucideIcon("search", __iconNode$5);
4460
-
4461
- /**
4462
- * @license lucide-react v0.577.0 - ISC
4463
- *
4464
- * This source code is licensed under the ISC license.
4465
- * See the LICENSE file in the root directory of this source tree.
4466
- */
4467
-
4468
-
4469
- const __iconNode$4 = [
4470
- ["path", { d: "M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7", key: "1m0v6g" }],
4471
- [
4472
- "path",
4473
- {
4474
- d: "M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z",
4475
- key: "ohrbg2"
4476
- }
4477
- ]
4478
- ];
4479
- const SquarePen = createLucideIcon("square-pen", __iconNode$4);
4480
-
4481
- /**
4482
- * @license lucide-react v0.577.0 - ISC
4483
- *
4484
- * This source code is licensed under the ISC license.
4485
- * See the LICENSE file in the root directory of this source tree.
4486
- */
4487
-
4488
-
4489
- const __iconNode$3 = [
4490
- ["path", { d: "M10 11v6", key: "nco0om" }],
4491
- ["path", { d: "M14 11v6", key: "outv1u" }],
4492
- ["path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6", key: "miytrc" }],
4493
- ["path", { d: "M3 6h18", key: "d0wm0j" }],
4494
- ["path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2", key: "e791ji" }]
4495
- ];
4496
- const Trash2 = createLucideIcon("trash-2", __iconNode$3);
4497
-
4498
- /**
4499
- * @license lucide-react v0.577.0 - ISC
4500
- *
4501
- * This source code is licensed under the ISC license.
4502
- * See the LICENSE file in the root directory of this source tree.
4503
- */
4504
-
4505
-
4506
- const __iconNode$2 = [
4507
- [
4508
- "path",
4509
- {
4510
- d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",
4511
- key: "wmoenq"
4512
- }
4513
- ],
4514
- ["path", { d: "M12 9v4", key: "juzpu7" }],
4515
- ["path", { d: "M12 17h.01", key: "p32p05" }]
4516
- ];
4517
- const TriangleAlert = createLucideIcon("triangle-alert", __iconNode$2);
4518
-
4519
- /**
4520
- * @license lucide-react v0.577.0 - ISC
4521
- *
4522
- * This source code is licensed under the ISC license.
4523
- * See the LICENSE file in the root directory of this source tree.
4524
- */
4525
-
4526
-
4527
- const __iconNode$1 = [
4528
- ["path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2", key: "975kel" }],
4529
- ["circle", { cx: "12", cy: "7", r: "4", key: "17ys0d" }]
4530
- ];
4531
- const User = createLucideIcon("user", __iconNode$1);
4532
-
4533
- /**
4534
- * @license lucide-react v0.577.0 - ISC
4535
- *
4536
- * This source code is licensed under the ISC license.
4537
- * See the LICENSE file in the root directory of this source tree.
4538
- */
4539
-
4540
-
4541
- const __iconNode = [
4542
- ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
4543
- ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
4544
- ];
4545
- const X = createLucideIcon("x", __iconNode);
4546
-
4547
- function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
4548
-
4549
- /**
4550
- * Concatenates two arrays faster than the array spread operator.
4551
- */
4552
- const concatArrays = (array1, array2) => {
4553
- // Pre-allocate for better V8 optimization
4554
- const combinedArray = new Array(array1.length + array2.length);
4555
- for (let i = 0; i < array1.length; i++) {
4556
- combinedArray[i] = array1[i];
4557
- }
4558
- for (let i = 0; i < array2.length; i++) {
4559
- combinedArray[array1.length + i] = array2[i];
4560
- }
4561
- return combinedArray;
4562
- };
4563
-
4564
- // Factory function ensures consistent object shapes
4565
- const createClassValidatorObject = (classGroupId, validator) => ({
4566
- classGroupId,
4567
- validator
4568
- });
4569
- // Factory ensures consistent ClassPartObject shape
4570
- const createClassPartObject = (nextPart = new Map(), validators = null, classGroupId) => ({
4571
- nextPart,
4572
- validators,
4573
- classGroupId
4574
- });
4575
- const CLASS_PART_SEPARATOR = '-';
4576
- const EMPTY_CONFLICTS = [];
4577
- // I use two dots here because one dot is used as prefix for class groups in plugins
4578
- const ARBITRARY_PROPERTY_PREFIX = 'arbitrary..';
4579
- const createClassGroupUtils = config => {
4580
- const classMap = createClassMap(config);
4581
- const {
4582
- conflictingClassGroups,
4583
- conflictingClassGroupModifiers
4584
- } = config;
4585
- const getClassGroupId = className => {
4586
- if (className.startsWith('[') && className.endsWith(']')) {
4587
- return getGroupIdForArbitraryProperty(className);
4588
- }
4589
- const classParts = className.split(CLASS_PART_SEPARATOR);
4590
- // Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and skip it.
4591
- const startIndex = classParts[0] === '' && classParts.length > 1 ? 1 : 0;
4592
- return getGroupRecursive(classParts, startIndex, classMap);
4593
- };
4594
- const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {
4595
- if (hasPostfixModifier) {
4596
- const modifierConflicts = conflictingClassGroupModifiers[classGroupId];
4597
- const baseConflicts = conflictingClassGroups[classGroupId];
4598
- if (modifierConflicts) {
4599
- if (baseConflicts) {
4600
- // Merge base conflicts with modifier conflicts
4601
- return concatArrays(baseConflicts, modifierConflicts);
4602
- }
4603
- // Only modifier conflicts
4604
- return modifierConflicts;
4605
- }
4606
- // Fall back to without postfix if no modifier conflicts
4607
- return baseConflicts || EMPTY_CONFLICTS;
4608
- }
4609
- return conflictingClassGroups[classGroupId] || EMPTY_CONFLICTS;
4610
- };
4611
- return {
4612
- getClassGroupId,
4613
- getConflictingClassGroupIds
4614
- };
4615
- };
4616
- const getGroupRecursive = (classParts, startIndex, classPartObject) => {
4617
- const classPathsLength = classParts.length - startIndex;
4618
- if (classPathsLength === 0) {
4619
- return classPartObject.classGroupId;
4620
- }
4621
- const currentClassPart = classParts[startIndex];
4622
- const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);
4623
- if (nextClassPartObject) {
4624
- const result = getGroupRecursive(classParts, startIndex + 1, nextClassPartObject);
4625
- if (result) return result;
4626
- }
4627
- const validators = classPartObject.validators;
4628
- if (validators === null) {
4629
- return undefined;
4630
- }
4631
- // Build classRest string efficiently by joining from startIndex onwards
4632
- const classRest = startIndex === 0 ? classParts.join(CLASS_PART_SEPARATOR) : classParts.slice(startIndex).join(CLASS_PART_SEPARATOR);
4633
- const validatorsLength = validators.length;
4634
- for (let i = 0; i < validatorsLength; i++) {
4635
- const validatorObj = validators[i];
4636
- if (validatorObj.validator(classRest)) {
4637
- return validatorObj.classGroupId;
4638
- }
4639
- }
4640
- return undefined;
4641
- };
4642
- /**
4643
- * Get the class group ID for an arbitrary property.
4644
- *
4645
- * @param className - The class name to get the group ID for. Is expected to be string starting with `[` and ending with `]`.
4646
- */
4647
- const getGroupIdForArbitraryProperty = className => className.slice(1, -1).indexOf(':') === -1 ? undefined : (() => {
4648
- const content = className.slice(1, -1);
4649
- const colonIndex = content.indexOf(':');
4650
- const property = content.slice(0, colonIndex);
4651
- return property ? ARBITRARY_PROPERTY_PREFIX + property : undefined;
4652
- })();
4517
+ const getGroupIdForArbitraryProperty = className => className.slice(1, -1).indexOf(':') === -1 ? undefined : (() => {
4518
+ const content = className.slice(1, -1);
4519
+ const colonIndex = content.indexOf(':');
4520
+ const property = content.slice(0, colonIndex);
4521
+ return property ? ARBITRARY_PROPERTY_PREFIX + property : undefined;
4522
+ })();
4653
4523
  /**
4654
4524
  * Exported for testing only
4655
4525
  */
@@ -4893,15 +4763,27 @@ const createConfigUtils = config => ({
4893
4763
  cache: createLruCache(config.cacheSize),
4894
4764
  parseClassName: createParseClassName(config),
4895
4765
  sortModifiers: createSortModifiers(config),
4766
+ postfixLookupClassGroupIds: createPostfixLookupClassGroupIds(config),
4896
4767
  ...createClassGroupUtils(config)
4897
4768
  });
4769
+ const createPostfixLookupClassGroupIds = config => {
4770
+ const lookup = Object.create(null);
4771
+ const classGroupIds = config.postfixLookupClassGroups;
4772
+ if (classGroupIds) {
4773
+ for (let i = 0; i < classGroupIds.length; i++) {
4774
+ lookup[classGroupIds[i]] = true;
4775
+ }
4776
+ }
4777
+ return lookup;
4778
+ };
4898
4779
  const SPLIT_CLASSES_REGEX = /\s+/;
4899
4780
  const mergeClassList = (classList, configUtils) => {
4900
4781
  const {
4901
4782
  parseClassName,
4902
4783
  getClassGroupId,
4903
4784
  getConflictingClassGroupIds,
4904
- sortModifiers
4785
+ sortModifiers,
4786
+ postfixLookupClassGroupIds
4905
4787
  } = configUtils;
4906
4788
  /**
4907
4789
  * Set of classGroupIds in following format:
@@ -4927,7 +4809,18 @@ const mergeClassList = (classList, configUtils) => {
4927
4809
  continue;
4928
4810
  }
4929
4811
  let hasPostfixModifier = !!maybePostfixModifierPosition;
4930
- let classGroupId = getClassGroupId(hasPostfixModifier ? baseClassName.substring(0, maybePostfixModifierPosition) : baseClassName);
4812
+ let classGroupId;
4813
+ if (hasPostfixModifier) {
4814
+ const baseClassNameWithoutPostfix = baseClassName.substring(0, maybePostfixModifierPosition);
4815
+ classGroupId = getClassGroupId(baseClassNameWithoutPostfix);
4816
+ const classGroupIdWithPostfix = classGroupId && postfixLookupClassGroupIds[classGroupId] ? getClassGroupId(baseClassName) : undefined;
4817
+ if (classGroupIdWithPostfix && classGroupIdWithPostfix !== classGroupId) {
4818
+ classGroupId = classGroupIdWithPostfix;
4819
+ hasPostfixModifier = false;
4820
+ }
4821
+ } else {
4822
+ classGroupId = getClassGroupId(baseClassName);
4823
+ }
4931
4824
  if (!classGroupId) {
4932
4825
  if (!hasPostfixModifier) {
4933
4826
  // Not a Tailwind class
@@ -5032,6 +4925,7 @@ const fallbackThemeArr = [];
5032
4925
  const fromTheme = key => {
5033
4926
  const themeGetter = theme => theme[key] || fallbackThemeArr;
5034
4927
  themeGetter.isThemeGetter = true;
4928
+ themeGetter.themeKey = key;
5035
4929
  return themeGetter;
5036
4930
  };
5037
4931
  const arbitraryValueRegex = /^\[(?:(\w[\w-]*):)?(.+)\]$/i;
@@ -5039,7 +4933,7 @@ const arbitraryVariableRegex = /^\((?:(\w[\w-]*):)?(.+)\)$/i;
5039
4933
  const fractionRegex = /^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/;
5040
4934
  const tshirtUnitRegex = /^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/;
5041
4935
  const lengthUnitRegex = /\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/;
5042
- const colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/;
4936
+ const colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix|color|light-dark)\(.+\)$/;
5043
4937
  // Shadow always begins with x and y offset separated by underscore optionally prepended by inset
5044
4938
  const shadowRegex = /^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;
5045
4939
  const imageRegex = /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/;
@@ -5058,6 +4952,7 @@ const isNever = () => false;
5058
4952
  const isShadow = value => shadowRegex.test(value);
5059
4953
  const isImage = value => imageRegex.test(value);
5060
4954
  const isAnyNonArbitrary = value => !isArbitraryValue(value) && !isArbitraryVariable(value);
4955
+ const isNamedContainerQuery = value => value.startsWith('@container') && (value[10] === '/' && value[11] !== undefined || value[11] === 's' && value[16] !== undefined && value.startsWith('-size/', 10) || value[11] === 'n' && value[18] !== undefined && value.startsWith('-normal/', 10));
5061
4956
  const isArbitrarySize = value => getIsArbitraryValue(value, isLabelSize, isNever);
5062
4957
  const isArbitraryValue = value => arbitraryValueRegex.test(value);
5063
4958
  const isArbitraryLength = value => getIsArbitraryValue(value, isLabelLength, isLengthOnly);
@@ -5162,7 +5057,7 @@ const getDefaultConfig = () => {
5162
5057
  const scaleAlignSecondaryAxis = () => ['start', 'end', 'center', 'stretch', 'center-safe', 'end-safe'];
5163
5058
  const scaleMargin = () => ['auto', ...scaleUnambiguousSpacing()];
5164
5059
  const scaleSizing = () => [isFraction, 'auto', 'full', 'dvw', 'dvh', 'lvw', 'lvh', 'svw', 'svh', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];
5165
- const scaleSizingInline = () => [isFraction, 'screen', 'full', 'dvw', 'lvw', 'svw', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];
5060
+ const scaleSizingInline = () => [themeContainer, isFraction, 'screen', 'full', 'dvw', 'lvw', 'svw', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];
5166
5061
  const scaleSizingBlock = () => [isFraction, 'screen', 'full', 'lh', 'dvh', 'lvh', 'svh', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];
5167
5062
  const scaleColor = () => [themeColor, isArbitraryVariable, isArbitraryValue];
5168
5063
  const scaleBgPosition = () => [...scalePosition(), isArbitraryVariablePosition, isArbitraryPosition, {
@@ -5229,12 +5124,24 @@ const getDefaultConfig = () => {
5229
5124
  * @deprecated since Tailwind CSS v4.0.0
5230
5125
  */
5231
5126
  container: ['container'],
5127
+ /**
5128
+ * Container Type
5129
+ * @see https://tailwindcss.com/docs/responsive-design#container-queries
5130
+ */
5131
+ 'container-type': [{
5132
+ '@container': ['', 'normal', 'size', isArbitraryVariable, isArbitraryValue]
5133
+ }],
5134
+ /**
5135
+ * Container Name
5136
+ * @see https://tailwindcss.com/docs/responsive-design#named-containers
5137
+ */
5138
+ 'container-named': [isNamedContainerQuery],
5232
5139
  /**
5233
5140
  * Columns
5234
5141
  * @see https://tailwindcss.com/docs/columns
5235
5142
  */
5236
5143
  columns: [{
5237
- columns: [isNumber, isArbitraryValue, isArbitraryVariable, themeContainer]
5144
+ columns: [isNumber, 'auto', isArbitraryValue, isArbitraryVariable, themeContainer]
5238
5145
  }],
5239
5146
  /**
5240
5147
  * Break After
@@ -5870,42 +5777,42 @@ const getDefaultConfig = () => {
5870
5777
  }],
5871
5778
  /**
5872
5779
  * Inline Size
5873
- * @see https://tailwindcss.com/docs/width
5780
+ * @see https://tailwindcss.com/docs/inline-size
5874
5781
  */
5875
5782
  'inline-size': [{
5876
5783
  inline: ['auto', ...scaleSizingInline()]
5877
5784
  }],
5878
5785
  /**
5879
5786
  * Min-Inline Size
5880
- * @see https://tailwindcss.com/docs/min-width
5787
+ * @see https://tailwindcss.com/docs/min-inline-size
5881
5788
  */
5882
5789
  'min-inline-size': [{
5883
5790
  'min-inline': ['auto', ...scaleSizingInline()]
5884
5791
  }],
5885
5792
  /**
5886
5793
  * Max-Inline Size
5887
- * @see https://tailwindcss.com/docs/max-width
5794
+ * @see https://tailwindcss.com/docs/max-inline-size
5888
5795
  */
5889
5796
  'max-inline-size': [{
5890
5797
  'max-inline': ['none', ...scaleSizingInline()]
5891
5798
  }],
5892
5799
  /**
5893
5800
  * Block Size
5894
- * @see https://tailwindcss.com/docs/height
5801
+ * @see https://tailwindcss.com/docs/block-size
5895
5802
  */
5896
5803
  'block-size': [{
5897
5804
  block: ['auto', ...scaleSizingBlock()]
5898
5805
  }],
5899
5806
  /**
5900
5807
  * Min-Block Size
5901
- * @see https://tailwindcss.com/docs/min-height
5808
+ * @see https://tailwindcss.com/docs/min-block-size
5902
5809
  */
5903
5810
  'min-block-size': [{
5904
5811
  'min-block': ['auto', ...scaleSizingBlock()]
5905
5812
  }],
5906
5813
  /**
5907
5814
  * Max-Block Size
5908
- * @see https://tailwindcss.com/docs/max-height
5815
+ * @see https://tailwindcss.com/docs/max-block-size
5909
5816
  */
5910
5817
  'max-block-size': [{
5911
5818
  'max-block': ['none', ...scaleSizingBlock()]
@@ -5955,7 +5862,7 @@ const getDefaultConfig = () => {
5955
5862
  * @see https://tailwindcss.com/docs/max-height
5956
5863
  */
5957
5864
  'max-h': [{
5958
- 'max-h': ['screen', 'lh', ...scaleSizing()]
5865
+ 'max-h': ['screen', 'lh', 'none', ...scaleSizing()]
5959
5866
  }],
5960
5867
  // ------------------
5961
5868
  // --- Typography ---
@@ -6054,7 +5961,7 @@ const getDefaultConfig = () => {
6054
5961
  * @see https://tailwindcss.com/docs/line-height
6055
5962
  */
6056
5963
  leading: [{
6057
- leading: [/** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */
5964
+ leading: ['none', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */
6058
5965
  themeLeading, ...scaleUnambiguousSpacing()]
6059
5966
  }],
6060
5967
  /**
@@ -6157,6 +6064,13 @@ const getDefaultConfig = () => {
6157
6064
  indent: [{
6158
6065
  indent: scaleUnambiguousSpacing()
6159
6066
  }],
6067
+ /**
6068
+ * Tab Size
6069
+ * @see https://tailwindcss.com/docs/tab-size
6070
+ */
6071
+ 'tab-size': [{
6072
+ tab: [isInteger, isArbitraryVariable, isArbitraryValue]
6073
+ }],
6160
6074
  /**
6161
6075
  * Vertical Alignment
6162
6076
  * @see https://tailwindcss.com/docs/vertical-align
@@ -6254,7 +6168,7 @@ const getDefaultConfig = () => {
6254
6168
  to: ['t', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl']
6255
6169
  }, isInteger, isArbitraryVariable, isArbitraryValue],
6256
6170
  radial: ['', isArbitraryVariable, isArbitraryValue],
6257
- conic: [isInteger, isArbitraryVariable, isArbitraryValue]
6171
+ conic: ['', isInteger, isArbitraryVariable, isArbitraryValue]
6258
6172
  }, isArbitraryVariableImage, isArbitraryImage]
6259
6173
  }],
6260
6174
  /**
@@ -6651,7 +6565,9 @@ const getDefaultConfig = () => {
6651
6565
  shadow: [{
6652
6566
  shadow: [
6653
6567
  // Deprecated since Tailwind CSS v4.0.0
6654
- '', 'none', themeShadow, isArbitraryVariableShadow, isArbitraryShadow]
6568
+ '',
6569
+ // Deprecated since Tailwind CSS v4.0.0
6570
+ 'inner', 'none', themeShadow, isArbitraryVariableShadow, isArbitraryShadow]
6655
6571
  }],
6656
6572
  /**
6657
6573
  * Box Shadow Color
@@ -7364,7 +7280,14 @@ const getDefaultConfig = () => {
7364
7280
  * @see https://tailwindcss.com/docs/translate
7365
7281
  */
7366
7282
  'translate-none': ['translate-none'],
7367
- // ---------------------
7283
+ /**
7284
+ * Zoom
7285
+ * @see https://tailwindcss.com/docs/zoom
7286
+ */
7287
+ zoom: [{
7288
+ zoom: [isInteger, isArbitraryVariable, isArbitraryValue]
7289
+ }],
7290
+ // ---------------------
7368
7291
  // --- Interactivity ---
7369
7292
  // ---------------------
7370
7293
  /**
@@ -7430,6 +7353,34 @@ const getDefaultConfig = () => {
7430
7353
  'scroll-behavior': [{
7431
7354
  scroll: ['auto', 'smooth']
7432
7355
  }],
7356
+ /**
7357
+ * Scrollbar Thumb Color
7358
+ * @see https://tailwindcss.com/docs/scrollbar-color
7359
+ */
7360
+ 'scrollbar-thumb-color': [{
7361
+ 'scrollbar-thumb': scaleColor()
7362
+ }],
7363
+ /**
7364
+ * Scrollbar Track Color
7365
+ * @see https://tailwindcss.com/docs/scrollbar-color
7366
+ */
7367
+ 'scrollbar-track-color': [{
7368
+ 'scrollbar-track': scaleColor()
7369
+ }],
7370
+ /**
7371
+ * Scrollbar Gutter
7372
+ * @see https://tailwindcss.com/docs/scrollbar-gutter
7373
+ */
7374
+ 'scrollbar-gutter': [{
7375
+ 'scrollbar-gutter': ['auto', 'stable', 'both']
7376
+ }],
7377
+ /**
7378
+ * Scrollbar Width
7379
+ * @see https://tailwindcss.com/docs/scrollbar-width
7380
+ */
7381
+ 'scrollbar-w': [{
7382
+ scrollbar: ['auto', 'thin', 'none']
7383
+ }],
7433
7384
  /**
7434
7385
  * Scroll Margin
7435
7386
  * @see https://tailwindcss.com/docs/scroll-margin
@@ -7688,19 +7639,20 @@ const getDefaultConfig = () => {
7688
7639
  }]
7689
7640
  },
7690
7641
  conflictingClassGroups: {
7642
+ 'container-named': ['container-type'],
7691
7643
  overflow: ['overflow-x', 'overflow-y'],
7692
7644
  overscroll: ['overscroll-x', 'overscroll-y'],
7693
7645
  inset: ['inset-x', 'inset-y', 'inset-bs', 'inset-be', 'start', 'end', 'top', 'right', 'bottom', 'left'],
7694
- 'inset-x': ['right', 'left'],
7695
- 'inset-y': ['top', 'bottom'],
7646
+ 'inset-x': ['start', 'end', 'right', 'left'],
7647
+ 'inset-y': ['inset-bs', 'inset-be', 'top', 'bottom'],
7696
7648
  flex: ['basis', 'grow', 'shrink'],
7697
7649
  gap: ['gap-x', 'gap-y'],
7698
7650
  p: ['px', 'py', 'ps', 'pe', 'pbs', 'pbe', 'pt', 'pr', 'pb', 'pl'],
7699
- px: ['pr', 'pl'],
7700
- py: ['pt', 'pb'],
7651
+ px: ['ps', 'pe', 'pr', 'pl'],
7652
+ py: ['pbs', 'pbe', 'pt', 'pb'],
7701
7653
  m: ['mx', 'my', 'ms', 'me', 'mbs', 'mbe', 'mt', 'mr', 'mb', 'ml'],
7702
- mx: ['mr', 'ml'],
7703
- my: ['mt', 'mb'],
7654
+ mx: ['ms', 'me', 'mr', 'ml'],
7655
+ my: ['mbs', 'mbe', 'mt', 'mb'],
7704
7656
  size: ['w', 'h'],
7705
7657
  'font-size': ['leading'],
7706
7658
  'fvn-normal': ['fvn-ordinal', 'fvn-slashed-zero', 'fvn-figure', 'fvn-spacing', 'fvn-fraction'],
@@ -7719,19 +7671,19 @@ const getDefaultConfig = () => {
7719
7671
  'rounded-l': ['rounded-tl', 'rounded-bl'],
7720
7672
  'border-spacing': ['border-spacing-x', 'border-spacing-y'],
7721
7673
  'border-w': ['border-w-x', 'border-w-y', 'border-w-s', 'border-w-e', 'border-w-bs', 'border-w-be', 'border-w-t', 'border-w-r', 'border-w-b', 'border-w-l'],
7722
- 'border-w-x': ['border-w-r', 'border-w-l'],
7723
- 'border-w-y': ['border-w-t', 'border-w-b'],
7674
+ 'border-w-x': ['border-w-s', 'border-w-e', 'border-w-r', 'border-w-l'],
7675
+ 'border-w-y': ['border-w-bs', 'border-w-be', 'border-w-t', 'border-w-b'],
7724
7676
  'border-color': ['border-color-x', 'border-color-y', 'border-color-s', 'border-color-e', 'border-color-bs', 'border-color-be', 'border-color-t', 'border-color-r', 'border-color-b', 'border-color-l'],
7725
- 'border-color-x': ['border-color-r', 'border-color-l'],
7726
- 'border-color-y': ['border-color-t', 'border-color-b'],
7677
+ 'border-color-x': ['border-color-s', 'border-color-e', 'border-color-r', 'border-color-l'],
7678
+ 'border-color-y': ['border-color-bs', 'border-color-be', 'border-color-t', 'border-color-b'],
7727
7679
  translate: ['translate-x', 'translate-y', 'translate-none'],
7728
7680
  'translate-none': ['translate', 'translate-x', 'translate-y', 'translate-z'],
7729
7681
  'scroll-m': ['scroll-mx', 'scroll-my', 'scroll-ms', 'scroll-me', 'scroll-mbs', 'scroll-mbe', 'scroll-mt', 'scroll-mr', 'scroll-mb', 'scroll-ml'],
7730
- 'scroll-mx': ['scroll-mr', 'scroll-ml'],
7731
- 'scroll-my': ['scroll-mt', 'scroll-mb'],
7682
+ 'scroll-mx': ['scroll-ms', 'scroll-me', 'scroll-mr', 'scroll-ml'],
7683
+ 'scroll-my': ['scroll-mbs', 'scroll-mbe', 'scroll-mt', 'scroll-mb'],
7732
7684
  'scroll-p': ['scroll-px', 'scroll-py', 'scroll-ps', 'scroll-pe', 'scroll-pbs', 'scroll-pbe', 'scroll-pt', 'scroll-pr', 'scroll-pb', 'scroll-pl'],
7733
- 'scroll-px': ['scroll-pr', 'scroll-pl'],
7734
- 'scroll-py': ['scroll-pt', 'scroll-pb'],
7685
+ 'scroll-px': ['scroll-ps', 'scroll-pe', 'scroll-pr', 'scroll-pl'],
7686
+ 'scroll-py': ['scroll-pbs', 'scroll-pbe', 'scroll-pt', 'scroll-pb'],
7735
7687
  touch: ['touch-x', 'touch-y', 'touch-pz'],
7736
7688
  'touch-x': ['touch'],
7737
7689
  'touch-y': ['touch'],
@@ -7740,6 +7692,7 @@ const getDefaultConfig = () => {
7740
7692
  conflictingClassGroupModifiers: {
7741
7693
  'font-size': ['leading']
7742
7694
  },
7695
+ postfixLookupClassGroups: ['container-type'],
7743
7696
  orderSensitiveModifiers: ['*', '**', 'after', 'backdrop', 'before', 'details-content', 'file', 'first-letter', 'first-line', 'marker', 'placeholder', 'selection']
7744
7697
  };
7745
7698
  };
@@ -7750,475 +7703,673 @@ function cn(...inputs) {
7750
7703
  }
7751
7704
 
7752
7705
  /**
7753
- * Combobox component with type-ahead search and fuzzy matching.
7754
- * Built on cmdk for search functionality and Radix Popover for positioning.
7706
+ * Get icon for tenant type
7755
7707
  */
7756
- function Combobox({ options, value, onValueChange, placeholder = 'Select...', searchPlaceholder = 'Search...', emptyMessage = 'No results found.', disabled = false, loading = false, className, clearable = false, showAllOption = true, allOptionLabel = 'All', }) {
7757
- const [open, setOpen] = React.useState(false);
7758
- const [search, setSearch] = React.useState('');
7759
- // Find current selected option for display
7760
- const selectedOption = options.find((opt) => opt.value === value);
7761
- const displayValue = selectedOption
7762
- ? selectedOption.count !== undefined
7763
- ? `${selectedOption.label} (${selectedOption.count})`
7764
- : selectedOption.label
7765
- : value === '' && showAllOption
7766
- ? allOptionLabel
7767
- : placeholder;
7768
- const handleSelect = React.useCallback((selectedValue) => {
7769
- onValueChange(selectedValue === value ? '' : selectedValue);
7770
- setOpen(false);
7771
- setSearch('');
7772
- }, [onValueChange, value]);
7773
- const handleClear = React.useCallback((e) => {
7774
- e.stopPropagation();
7775
- onValueChange('');
7776
- }, [onValueChange]);
7777
- // Custom fuzzy filter function
7778
- const filterOptions = React.useCallback((optionValue, searchQuery) => {
7779
- if (!searchQuery)
7780
- return 1;
7781
- const option = options.find((o) => o.value === optionValue);
7782
- const label = option?.label || optionValue;
7783
- const lowerLabel = label.toLowerCase();
7784
- const lowerSearch = searchQuery.toLowerCase();
7785
- // Exact match
7786
- if (lowerLabel === lowerSearch)
7787
- return 1;
7788
- // Starts with
7789
- if (lowerLabel.startsWith(lowerSearch))
7790
- return 0.9;
7791
- // Contains
7792
- if (lowerLabel.includes(lowerSearch))
7793
- return 0.8;
7794
- // Fuzzy match - check if all search chars appear in order
7795
- let searchIdx = 0;
7796
- for (let i = 0; i < lowerLabel.length && searchIdx < lowerSearch.length; i++) {
7797
- if (lowerLabel[i] === lowerSearch[searchIdx]) {
7798
- searchIdx++;
7799
- }
7800
- }
7801
- if (searchIdx === lowerSearch.length)
7802
- return 0.6;
7803
- return 0;
7804
- }, [options]);
7805
- return (jsxs(Popover.Root, { open: open, onOpenChange: setOpen, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs("button", { type: "button", role: "combobox", "aria-expanded": open, "aria-haspopup": "listbox", disabled: disabled || loading, className: cn('flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background', 'placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', 'disabled:cursor-not-allowed disabled:opacity-50', className), children: [jsx("span", { className: "truncate text-left flex-1", children: loading ? 'Loading...' : displayValue }), jsxs("div", { className: "flex items-center gap-1 ml-2", children: [clearable && value && !disabled && (jsx("span", { role: "button", tabIndex: 0, onClick: handleClear, onKeyDown: (e) => {
7806
- if (e.key === 'Enter' || e.key === ' ') {
7807
- handleClear(e);
7808
- }
7809
- }, className: "rounded-sm opacity-50 hover:opacity-100 cursor-pointer", children: jsx(X, { className: "h-3 w-3" }) })), jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50" })] })] }) }), jsx(Popover.Portal, { children: jsx(Popover.Content, { className: cn('z-50 min-w-[var(--radix-popover-trigger-width)] overflow-hidden rounded-md border shadow-md', 'bg-white dark:bg-slate-900 text-foreground', 'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2'), sideOffset: 4, align: "start", children: jsxs(Command, { filter: filterOptions, className: "w-full", children: [jsx("div", { className: "flex items-center border-b px-3", children: jsx(CommandInput, { placeholder: searchPlaceholder, value: search, onValueChange: setSearch, className: "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50" }) }), jsxs(CommandList, { className: "max-h-[300px] overflow-y-auto p-1", children: [jsx(CommandEmpty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }), jsxs(CommandGroup, { children: [showAllOption && (jsxs(CommandItem, { value: "_all", onSelect: () => handleSelect(''), className: cn('relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', 'data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground', 'hover:bg-accent hover:text-accent-foreground'), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', value === '' ? 'opacity-100' : 'opacity-0') }), allOptionLabel] })), options.map((option) => (jsxs(CommandItem, { value: option.value, onSelect: () => handleSelect(option.value), className: cn('relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', 'data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground', 'hover:bg-accent hover:text-accent-foreground'), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', value === option.value ? 'opacity-100' : 'opacity-0') }), jsx("span", { className: "flex-1 truncate", children: option.label }), option.count !== undefined && (jsxs("span", { className: "ml-2 text-xs text-muted-foreground", children: ["(", option.count, ")"] }))] }, option.value)))] })] })] }) }) })] }));
7708
+ function getTenantIcon(type) {
7709
+ switch (type) {
7710
+ case "organization":
7711
+ return (jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" }) }));
7712
+ case "project":
7713
+ return (jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" }) }));
7714
+ case "user":
7715
+ return (jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }));
7716
+ case "platform":
7717
+ return (jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }));
7718
+ default:
7719
+ return null;
7720
+ }
7721
+ }
7722
+ /**
7723
+ * Get badge variant based on tenant type
7724
+ */
7725
+ function getTenantBadgeVariant(type) {
7726
+ switch (type) {
7727
+ case "organization":
7728
+ return "default";
7729
+ case "project":
7730
+ return "secondary";
7731
+ case "user":
7732
+ return "default";
7733
+ case "platform":
7734
+ return "default";
7735
+ default:
7736
+ return "secondary";
7737
+ }
7810
7738
  }
7811
-
7812
7739
  /**
7813
- * TimeRangeDropdown - A compact dropdown for selecting time ranges
7740
+ * TenantBadge displays tenant information in a compact badge format
7741
+ * Renders as a clickable link if tenantLinkResolver is provided and returns a URL
7814
7742
  */
7815
- function TimeRangeDropdown({ presets, selectedPreset, onPresetSelect, onCustomRangeApply, customStart: initialCustomStart, customEnd: initialCustomEnd, disabled = false, className, displayLabel, }) {
7816
- const [open, setOpen] = React.useState(false);
7817
- const [showCustomInputs, setShowCustomInputs] = React.useState(false);
7818
- const [customStart, setCustomStart] = React.useState(initialCustomStart || '');
7819
- const [customEnd, setCustomEnd] = React.useState(initialCustomEnd || '');
7820
- // Update custom values when props change
7821
- React.useEffect(() => {
7822
- if (initialCustomStart)
7823
- setCustomStart(initialCustomStart);
7824
- if (initialCustomEnd)
7825
- setCustomEnd(initialCustomEnd);
7826
- }, [initialCustomStart, initialCustomEnd]);
7827
- const selectedPresetObj = presets.find((p) => p.key === selectedPreset);
7828
- const label = displayLabel || selectedPresetObj?.label || 'Select time range';
7829
- const handlePresetClick = (presetKey) => {
7830
- onPresetSelect(presetKey);
7831
- setShowCustomInputs(false);
7832
- setOpen(false);
7833
- };
7834
- const handleCustomClick = () => {
7835
- setShowCustomInputs(true);
7836
- };
7837
- const handleCustomApply = () => {
7838
- if (customStart && customEnd) {
7839
- onCustomRangeApply(customStart, customEnd);
7840
- setShowCustomInputs(false);
7841
- setOpen(false);
7842
- }
7843
- };
7844
- const handleCustomCancel = () => {
7845
- setShowCustomInputs(false);
7846
- };
7847
- return (jsxs(Popover.Root, { open: open, onOpenChange: setOpen, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs("button", { type: "button", disabled: disabled, className: cn('flex h-7 items-center gap-2 rounded-md border border-input bg-background px-2 text-xs ring-offset-background', 'hover:bg-accent hover:text-accent-foreground', 'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', 'disabled:cursor-not-allowed disabled:opacity-50', className), children: [jsx(Calendar, { className: "h-4 w-4 text-muted-foreground" }), jsx("span", { className: "whitespace-nowrap", children: label }), jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })] }) }), jsx(Popover.Portal, { children: jsx(Popover.Content, { className: cn('z-50 min-w-[200px] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md', 'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2'), sideOffset: 4, align: "end", children: !showCustomInputs ? (jsxs("div", { className: "p-1", children: [presets.map((preset) => (jsx("button", { type: "button", onClick: () => handlePresetClick(preset.key), className: cn('relative flex w-full cursor-pointer select-none items-center rounded-sm px-3 py-2 text-sm outline-none', 'hover:bg-accent hover:text-accent-foreground', selectedPreset === preset.key && 'bg-accent text-accent-foreground font-medium'), children: preset.label }, preset.key))), jsx("div", { className: "my-1 h-px bg-border" }), jsx("button", { type: "button", onClick: handleCustomClick, className: cn('relative flex w-full cursor-pointer select-none items-center rounded-sm px-3 py-2 text-sm outline-none', 'hover:bg-accent hover:text-accent-foreground', selectedPreset === 'custom' && 'bg-accent text-accent-foreground font-medium'), children: "Custom range..." })] })) : (jsxs("div", { className: "p-4 min-w-[280px]", children: [jsxs("div", { className: "flex flex-col gap-3", children: [jsxs("div", { className: "flex flex-col gap-1.5", children: [jsx(Label, { htmlFor: "time-range-start", className: "text-xs text-muted-foreground font-semibold uppercase tracking-tight", children: "Start" }), jsx(Input, { id: "time-range-start", type: "datetime-local", value: customStart, onChange: (e) => setCustomStart(e.target.value), className: "w-full", disabled: disabled })] }), jsxs("div", { className: "flex flex-col gap-1.5", children: [jsx(Label, { htmlFor: "time-range-end", className: "text-xs text-muted-foreground font-semibold uppercase tracking-tight", children: "End" }), jsx(Input, { id: "time-range-end", type: "datetime-local", value: customEnd, onChange: (e) => setCustomEnd(e.target.value), className: "w-full", disabled: disabled })] })] }), jsxs("div", { className: "flex justify-end gap-2 mt-4 pt-3 border-t border-border", children: [jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "small", onClick: handleCustomCancel, children: "Back" }), jsx(Button, { htmlType: "button", size: "small", onClick: handleCustomApply, disabled: !customStart || !customEnd, children: "Apply" })] })] })) }) })] }));
7743
+ function TenantBadge({ tenant, tenantLinkResolver, className = "", size = "default", }) {
7744
+ const icon = getTenantIcon(tenant.type);
7745
+ const variant = getTenantBadgeVariant(tenant.type);
7746
+ const url = tenantLinkResolver?.(tenant);
7747
+ const badgeContent = (jsxs(Badge, { variant: variant, className: cn("inline-flex items-center gap-1 whitespace-nowrap", size === "compact" ? "text-xs h-4 py-0 px-1.5" : "text-xs h-5 px-2", url && "cursor-pointer hover:opacity-80 transition-opacity", className), children: [icon, jsx("span", { className: "font-medium capitalize", children: tenant.type }), tenant.type !== "platform" && (jsxs(Fragment, { children: [jsx("span", { className: "text-muted-foreground", children: "/" }), jsx("span", { children: tenant.name })] }))] }));
7748
+ if (url) {
7749
+ return (jsx("a", { href: url, className: "no-underline", onClick: (e) => e.stopPropagation(), title: `View ${tenant.type}: ${tenant.name}`, children: badgeContent }));
7750
+ }
7751
+ return badgeContent;
7848
7752
  }
7849
7753
 
7850
- const TIME_PRESETS$3 = [
7851
- {
7852
- key: 'last15min',
7853
- label: 'Last 15 min',
7854
- getValue: () => ({
7855
- start: formatISO(subMinutes(new Date())),
7856
- end: formatISO(new Date()),
7857
- }),
7858
- },
7859
- {
7860
- key: 'last1hour',
7861
- label: 'Last hour',
7862
- getValue: () => ({
7863
- start: formatISO(subHours(new Date(), 1)),
7864
- end: formatISO(new Date()),
7865
- }),
7866
- },
7867
- {
7868
- key: 'last6hours',
7869
- label: 'Last 6 hours',
7870
- getValue: () => ({
7871
- start: formatISO(subHours(new Date(), 6)),
7872
- end: formatISO(new Date()),
7873
- }),
7874
- },
7875
- {
7876
- key: 'last24hours',
7877
- label: 'Last 24 hours',
7878
- getValue: () => ({
7879
- start: formatISO(subHours(new Date(), 24)),
7880
- end: formatISO(new Date()),
7881
- }),
7882
- },
7883
- {
7884
- key: 'last7days',
7885
- label: 'Last 7 days',
7886
- getValue: () => ({
7887
- start: formatISO(subDays(new Date(), 7)),
7888
- end: formatISO(new Date()),
7889
- }),
7890
- },
7891
- {
7892
- key: 'last30days',
7893
- label: 'Last 30 days',
7894
- getValue: () => ({
7895
- start: formatISO(subDays(new Date(), 30)),
7896
- end: formatISO(new Date()),
7897
- }),
7898
- },
7899
- ];
7900
- /** Debounce delay for text inputs (ms) */
7901
- const DEBOUNCE_DELAY = 300;
7902
7754
  /**
7903
- * Helper function to convert ISO string to datetime-local format
7755
+ * Extract tenant information from audit event annotations if present
7756
+ * Expected annotations: tenant.type and tenant.name
7904
7757
  */
7905
- const formatDatetimeLocal$4 = (isoString) => {
7906
- const date = new Date(isoString);
7907
- const year = date.getFullYear();
7908
- const month = String(date.getMonth() + 1).padStart(2, '0');
7909
- const day = String(date.getDate()).padStart(2, '0');
7910
- const hours = String(date.getHours()).padStart(2, '0');
7911
- const minutes = String(date.getMinutes()).padStart(2, '0');
7912
- return `${year}-${month}-${day}T${hours}:${minutes}`;
7913
- };
7914
- function SimpleQueryBuilder({ client, onFilterChange, initialLimit = 100, className = '', disabled = false, }) {
7915
- const [isAdvancedExpanded, setIsAdvancedExpanded] = useState(false);
7916
- const [filters, setFilters] = useState({
7917
- verb: '',
7918
- resourceType: '',
7919
- namespace: '',
7920
- resourceName: '',
7921
- username: '',
7922
- });
7923
- const [limit, setLimit] = useState(initialLimit);
7924
- const [advancedFilter, setAdvancedFilter] = useState('');
7925
- const [useAdvancedFilter, setUseAdvancedFilter] = useState(false);
7926
- // Time range state
7927
- const [selectedPreset, setSelectedPreset] = useState('last24hours');
7928
- const [timeRange, setTimeRange] = useState(null);
7929
- const [customStart, setCustomStart] = useState(() => formatDatetimeLocal$4(formatISO(subDays(new Date(), 1))));
7930
- const [customEnd, setCustomEnd] = useState(() => formatDatetimeLocal$4(formatISO(new Date())));
7931
- // Fetch facets for dynamic filter options
7932
- const { verbs, resources, namespaces, usernames, isLoading: facetsLoading, error: facetsError } = useAuditLogFacets(client, timeRange);
7933
- // Log facets error for debugging
7934
- if (facetsError) {
7935
- console.error('Failed to load audit log facets:', facetsError);
7758
+ function extractTenantFromAnnotations(event) {
7759
+ const annotations = event.annotations;
7760
+ if (!annotations)
7761
+ return undefined;
7762
+ const tenantType = annotations["tenant.type"];
7763
+ const tenantName = annotations["tenant.name"];
7764
+ if (tenantType && tenantName) {
7765
+ const validTypes = [
7766
+ "platform",
7767
+ "organization",
7768
+ "project",
7769
+ "user",
7770
+ ];
7771
+ if (validTypes.includes(tenantType)) {
7772
+ return {
7773
+ type: tenantType,
7774
+ name: tenantName,
7775
+ };
7776
+ }
7936
7777
  }
7937
- // Refs for debouncing
7938
- const debounceTimerRef = useRef(null);
7939
- const isInitialMount = useRef(true);
7940
- // Initialize with default time range and trigger initial query
7941
- useEffect(() => {
7942
- const defaultRange = TIME_PRESETS$3.find(p => p.key === 'last24hours')?.getValue();
7943
- if (defaultRange) {
7944
- setTimeRange(defaultRange);
7778
+ return undefined;
7779
+ }
7780
+ /**
7781
+ * AuditEventViewer displays a list of audit events with details
7782
+ */
7783
+ function AuditEventViewer({ events, className = "", onEventSelect, tenantLinkResolver, }) {
7784
+ const [selectedEvent, setSelectedEvent] = useState(null);
7785
+ const [expandedEvents, setExpandedEvents] = useState(new Set());
7786
+ const toggleEventExpansion = (auditId) => {
7787
+ const newExpanded = new Set(expandedEvents);
7788
+ if (expandedEvents.has(auditId)) {
7789
+ newExpanded.delete(auditId);
7945
7790
  }
7946
- }, []);
7947
- const generateCEL = useCallback(() => {
7948
- const parts = [];
7949
- if (filters.verb) {
7950
- parts.push(`verb == "${filters.verb}"`);
7791
+ else {
7792
+ newExpanded.add(auditId);
7951
7793
  }
7952
- if (filters.resourceType) {
7953
- parts.push(`objectRef.resource == "${filters.resourceType}"`);
7794
+ setExpandedEvents(newExpanded);
7795
+ };
7796
+ const handleEventClick = (event) => {
7797
+ setSelectedEvent(event);
7798
+ if (onEventSelect) {
7799
+ onEventSelect(event);
7954
7800
  }
7955
- if (filters.namespace) {
7956
- parts.push(`objectRef.namespace == "${filters.namespace}"`);
7801
+ };
7802
+ const formatTimestamp = (timestamp) => {
7803
+ if (!timestamp)
7804
+ return "N/A";
7805
+ try {
7806
+ return format(new Date(timestamp), "yyyy-MM-dd HH:mm:ss");
7957
7807
  }
7958
- if (filters.resourceName) {
7959
- parts.push(`objectRef.name.contains("${filters.resourceName}")`);
7808
+ catch {
7809
+ return timestamp;
7960
7810
  }
7961
- if (filters.username) {
7962
- parts.push(`user.username.contains("${filters.username}")`);
7963
- }
7964
- return parts.join(' && ');
7965
- }, [filters]);
7966
- // Execute query when filters change (with debouncing for text inputs)
7967
- const executeQuery = useCallback(() => {
7968
- if (!timeRange || disabled)
7969
- return;
7970
- const filter = useAdvancedFilter ? advancedFilter : generateCEL();
7971
- onFilterChange({
7972
- filter,
7973
- limit,
7974
- startTime: timeRange.start,
7975
- endTime: timeRange.end
7976
- });
7977
- }, [useAdvancedFilter, advancedFilter, generateCEL, onFilterChange, limit, timeRange, disabled]);
7978
- // Auto-execute query when dropdown filters or time range change (immediate)
7979
- useEffect(() => {
7980
- // Skip the initial mount - we want to wait for timeRange to be set
7981
- if (isInitialMount.current) {
7982
- isInitialMount.current = false;
7983
- return;
7984
- }
7985
- // Clear any pending debounced query
7986
- if (debounceTimerRef.current) {
7987
- clearTimeout(debounceTimerRef.current);
7988
- }
7989
- executeQuery();
7990
- }, [filters.verb, filters.resourceType, timeRange, limit, useAdvancedFilter]);
7991
- // Auto-execute query when text filters change (debounced)
7992
- useEffect(() => {
7993
- if (isInitialMount.current || !timeRange)
7994
- return;
7995
- // Clear previous timer
7996
- if (debounceTimerRef.current) {
7997
- clearTimeout(debounceTimerRef.current);
7998
- }
7999
- // Set new debounced timer
8000
- debounceTimerRef.current = setTimeout(() => {
8001
- executeQuery();
8002
- }, DEBOUNCE_DELAY);
8003
- return () => {
8004
- if (debounceTimerRef.current) {
8005
- clearTimeout(debounceTimerRef.current);
8006
- }
8007
- };
8008
- }, [filters.namespace, filters.resourceName, filters.username, advancedFilter]);
8009
- const handleFilterChange = useCallback((field, value) => {
8010
- setFilters(prev => ({ ...prev, [field]: value }));
8011
- }, []);
8012
- const handleTimePresetSelect = useCallback((presetKey) => {
8013
- const preset = TIME_PRESETS$3.find(p => p.key === presetKey);
8014
- if (preset) {
8015
- setSelectedPreset(presetKey);
8016
- setTimeRange(preset.getValue());
8017
- }
8018
- }, []);
8019
- const handleCustomRangeApply = useCallback((start, end) => {
8020
- setSelectedPreset('custom');
8021
- setCustomStart(start);
8022
- setCustomEnd(end);
8023
- setTimeRange({
8024
- start: new Date(start).toISOString(),
8025
- end: new Date(end).toISOString(),
8026
- });
8027
- }, []);
8028
- const handleLimitChange = useCallback((newLimit) => {
8029
- const validLimit = Math.min(Math.max(1, newLimit), 1000);
8030
- setLimit(validLimit);
8031
- }, []);
8032
- const handleAdvancedToggle = useCallback(() => {
8033
- if (!isAdvancedExpanded) {
8034
- // When expanding, populate advanced filter with generated CEL
8035
- setAdvancedFilter(generateCEL());
8036
- }
8037
- setIsAdvancedExpanded(!isAdvancedExpanded);
8038
- }, [isAdvancedExpanded, generateCEL]);
8039
- const handleUseAdvancedChange = useCallback((checked) => {
8040
- setUseAdvancedFilter(checked === true);
8041
- }, []);
8042
- // Get display label for time range
8043
- const getTimeRangeLabel = () => {
8044
- if (selectedPreset === 'custom') {
8045
- if (timeRange?.start && timeRange?.end) {
8046
- const start = new Date(timeRange.start);
8047
- const end = new Date(timeRange.end);
8048
- return `${start.toLocaleDateString()} - ${end.toLocaleDateString()}`;
8049
- }
8050
- return 'Custom';
7811
+ };
7812
+ const getVerbBadgeVariant = (verb) => {
7813
+ switch (verb?.toLowerCase()) {
7814
+ case "create":
7815
+ return "success";
7816
+ case "update":
7817
+ case "patch":
7818
+ return "warning";
7819
+ case "delete":
7820
+ return "destructive";
7821
+ case "get":
7822
+ case "list":
7823
+ case "watch":
7824
+ return "default";
7825
+ default:
7826
+ return "secondary";
8051
7827
  }
8052
- const preset = TIME_PRESETS$3.find(p => p.key === selectedPreset);
8053
- return preset?.label || 'Select time range';
8054
7828
  };
8055
- return (jsxs("div", { className: `mb-6 pb-6 border-b border-border ${className}`, children: [jsxs("div", { className: "flex flex-wrap gap-4 items-end", children: [jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Action" }), jsx(Combobox, { options: verbs
8056
- .filter(facet => facet.value)
8057
- .map(facet => ({
8058
- value: facet.value,
8059
- label: facet.value,
8060
- count: facet.count,
8061
- })), value: filters.verb, onValueChange: (value) => handleFilterChange('verb', value), placeholder: "All", searchPlaceholder: "Search actions...", allOptionLabel: "All", disabled: disabled || useAdvancedFilter, loading: facetsLoading, className: "min-w-[130px]" })] }), jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Resource" }), jsx(Combobox, { options: resources
8062
- .filter(facet => facet.value)
8063
- .map(facet => ({
8064
- value: facet.value,
8065
- label: facet.value,
8066
- count: facet.count,
8067
- })), value: filters.resourceType, onValueChange: (value) => handleFilterChange('resourceType', value), placeholder: "All", searchPlaceholder: "Search resources...", allOptionLabel: "All", disabled: disabled || useAdvancedFilter, loading: facetsLoading, className: "min-w-[130px]" })] }), jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Namespace" }), jsx(Combobox, { options: namespaces
8068
- .filter(facet => facet.value)
8069
- .map(facet => ({
8070
- value: facet.value,
8071
- label: facet.value,
8072
- count: facet.count,
8073
- })), value: filters.namespace, onValueChange: (value) => handleFilterChange('namespace', value), placeholder: "All", searchPlaceholder: "Search namespaces...", allOptionLabel: "All", disabled: disabled || useAdvancedFilter, loading: facetsLoading, className: "min-w-[130px]" })] }), jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "User" }), jsx(Combobox, { options: usernames
8074
- .filter(facet => facet.value)
8075
- .map(facet => ({
8076
- value: facet.value,
8077
- label: facet.value,
8078
- count: facet.count,
8079
- })), value: filters.username, onValueChange: (value) => handleFilterChange('username', value), placeholder: "All", searchPlaceholder: "Search users...", allOptionLabel: "All", disabled: disabled || useAdvancedFilter, loading: facetsLoading, className: "min-w-[130px]" })] }), jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Name" }), jsx(Input, { type: "text", value: filters.resourceName, onChange: (e) => handleFilterChange('resourceName', e.target.value), placeholder: "Filter by name...", className: "min-w-[140px]", disabled: disabled || useAdvancedFilter })] }), jsxs("div", { className: "flex flex-col gap-2", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Limit" }), jsx(Input, { type: "number", value: limit, onChange: (e) => handleLimitChange(parseInt(e.target.value) || 100), min: 1, max: 1000, className: "w-[80px]", disabled: disabled })] }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "small", className: "whitespace-nowrap text-muted-foreground hover:text-foreground", onClick: handleAdvancedToggle, "aria-expanded": isAdvancedExpanded, children: isAdvancedExpanded ? '- CEL' : '+ CEL' }), jsxs("div", { className: "flex flex-col gap-2 ml-auto", children: [jsx(Label, { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: "Time Range" }), jsx(TimeRangeDropdown, { presets: TIME_PRESETS$3.map(p => ({ key: p.key, label: p.label })), selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() })] })] }), isAdvancedExpanded && (jsxs("div", { className: "mt-4 pt-4", children: [jsx(Separator, { className: "mb-4" }), jsx("div", { className: "mb-3", children: jsxs("div", { className: "flex items-center gap-2", children: [jsx(Checkbox, { id: "use-cel-expression", checked: useAdvancedFilter, onCheckedChange: handleUseAdvancedChange, disabled: disabled }), jsx(Label, { htmlFor: "use-cel-expression", className: "text-sm font-medium text-foreground cursor-pointer", children: "Use CEL Expression" })] }) }), useAdvancedFilter && (jsxs("div", { className: "mt-3", children: [jsx("p", { className: "m-0 mb-2 text-sm text-muted-foreground", children: "Write your query using CEL syntax for complex filters" }), jsx(Textarea, { value: advancedFilter, onChange: (e) => setAdvancedFilter(e.target.value), placeholder: 'Example: verb == "delete" && objectRef.namespace == "production"', rows: 3, className: "w-full font-mono resize-y", disabled: disabled })] }))] }))] }));
7829
+ if (events.length === 0) {
7830
+ return (jsx("div", { className: `bg-muted rounded-lg border border-border ${className}`, children: jsx("div", { className: "p-12 text-center text-muted-foreground text-sm", children: "No events found" }) }));
7831
+ }
7832
+ return (jsx("div", { className: `bg-muted rounded-lg border border-border ${className}`, children: jsx("div", { className: "p-4", children: events.map((event) => {
7833
+ const auditId = event.auditID || "";
7834
+ const isExpanded = expandedEvents.has(auditId);
7835
+ const tenant = extractTenantFromAnnotations(event);
7836
+ return (jsxs(Card, { className: `p-5 mb-3 cursor-pointer transition-all hover:border-primary/50 hover:shadow-sm hover:-translate-y-px ${selectedEvent?.auditID === auditId
7837
+ ? "border-primary bg-primary/5 shadow-md"
7838
+ : ""}`, onClick: () => handleEventClick(event), children: [jsxs("div", { className: "flex justify-between items-center", children: [jsxs("div", { className: "flex gap-3 items-center flex-1", children: [jsx(Badge, { variant: getVerbBadgeVariant(event.verb), className: "px-3 py-1", children: event.verb?.toUpperCase() || "UNKNOWN" }), jsx("span", { className: "font-semibold", children: event.objectRef?.resource || "N/A" }), event.objectRef?.namespace && (jsxs("span", { className: "text-muted-foreground text-sm", children: ["ns: ", event.objectRef.namespace] })), event.objectRef?.name && (jsx("span", { className: "text-foreground/80 text-sm", children: event.objectRef.name })), tenant && (jsx(TenantBadge, { tenant: tenant, tenantLinkResolver: tenantLinkResolver, size: "compact" }))] }), jsxs("div", { className: "flex gap-4 items-center", children: [jsx("span", { className: "text-foreground/80 text-sm", children: event.user?.username || "N/A" }), jsx("span", { className: "text-muted-foreground text-sm font-mono", children: formatTimestamp(event.stageTimestamp) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", onClick: (e) => {
7839
+ e.stopPropagation();
7840
+ toggleEventExpansion(auditId);
7841
+ }, className: "text-primary", children: isExpanded ? "▼" : "▶" })] })] }), isExpanded && (jsxs("div", { className: "mt-4 pt-4 border-t border-border", children: [jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "Event Information" }), jsxs("dl", { className: "grid grid-cols-[auto_1fr] gap-2 m-0", children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Audit ID:" }), jsx("dd", { className: "m-0 text-foreground", children: event.auditID || "N/A" }), jsx("dt", { className: "font-semibold text-foreground/80", children: "Stage:" }), jsx("dd", { className: "m-0 text-foreground", children: event.stage || "N/A" }), jsx("dt", { className: "font-semibold text-foreground/80", children: "Level:" }), jsx("dd", { className: "m-0 text-foreground", children: event.level || "N/A" }), jsx("dt", { className: "font-semibold text-foreground/80", children: "Request URI:" }), jsx("dd", { className: "m-0 text-foreground font-mono text-sm break-all", children: event.requestURI || "N/A" }), event.userAgent && (jsxs(Fragment, { children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "User Agent:" }), jsx("dd", { className: "m-0 text-foreground font-mono text-sm break-all", children: event.userAgent })] })), event.sourceIPs && event.sourceIPs.length > 0 && (jsxs(Fragment, { children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Source IPs:" }), jsx("dd", { className: "m-0 text-foreground", children: event.sourceIPs.join(", ") })] }))] })] }), tenant && (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "Tenant" }), jsx(TenantBadge, { tenant: tenant, tenantLinkResolver: tenantLinkResolver })] })), event.user && (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "User Information" }), jsxs("dl", { className: "grid grid-cols-[auto_1fr] gap-2 m-0", children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Username:" }), jsx("dd", { className: "m-0 text-foreground", children: event.user.username || "N/A" }), jsx("dt", { className: "font-semibold text-foreground/80", children: "UID:" }), jsx("dd", { className: "m-0 text-foreground", children: event.user.uid || "N/A" }), event.user.groups && event.user.groups.length > 0 && (jsxs(Fragment, { children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Groups:" }), jsx("dd", { className: "m-0 text-foreground", children: event.user.groups.join(", ") })] }))] })] })), event.responseStatus && (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "Response Status" }), jsxs("dl", { className: "grid grid-cols-[auto_1fr] gap-2 m-0", children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Code:" }), jsx("dd", { className: "m-0 text-foreground", children: event.responseStatus.code || "N/A" }), jsx("dt", { className: "font-semibold text-foreground/80", children: "Status:" }), jsx("dd", { className: "m-0 text-foreground", children: event.responseStatus.status || "N/A" }), event.responseStatus.message && (jsxs(Fragment, { children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Message:" }), jsx("dd", { className: "m-0 text-foreground", children: event.responseStatus.message })] }))] })] })), event.annotations &&
7842
+ Object.keys(event.annotations).length > 0 && (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "Annotations" }), jsx("dl", { className: "grid grid-cols-[auto_1fr] gap-2 m-0", children: Object.entries(event.annotations).map(([key, value]) => (jsxs("div", { className: "contents", children: [jsxs("dt", { className: "font-semibold text-foreground/80", children: [key, ":"] }), jsx("dd", { className: "m-0 text-foreground", children: value })] }, key))) })] })), event.requestObject || event.responseObject ? (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "Request/Response Data" }), event.requestObject ? (jsxs("details", { className: "mt-2", children: [jsx("summary", { className: "cursor-pointer font-semibold p-2 bg-muted rounded", children: "Request Object" }), jsx("pre", { className: "mt-2 p-4 bg-muted rounded overflow-x-auto text-sm", children: JSON.stringify(event.requestObject, null, 2) })] })) : null, event.responseObject ? (jsxs("details", { className: "mt-2", children: [jsx("summary", { className: "cursor-pointer font-semibold p-2 bg-muted rounded", children: "Response Object" }), jsx("pre", { className: "mt-2 p-4 bg-muted rounded overflow-x-auto text-sm", children: JSON.stringify(event.responseObject, null, 2) })] })) : null] })) : null] }))] }, auditId));
7843
+ }) }) }));
8080
7844
  }
8081
7845
 
8082
7846
  /**
8083
- * Get icon for tenant type
7847
+ * @license lucide-react v0.577.0 - ISC
7848
+ *
7849
+ * This source code is licensed under the ISC license.
7850
+ * See the LICENSE file in the root directory of this source tree.
7851
+ */
7852
+
7853
+ const mergeClasses = (...classes) => classes.filter((className, index, array) => {
7854
+ return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
7855
+ }).join(" ").trim();
7856
+
7857
+ /**
7858
+ * @license lucide-react v0.577.0 - ISC
7859
+ *
7860
+ * This source code is licensed under the ISC license.
7861
+ * See the LICENSE file in the root directory of this source tree.
7862
+ */
7863
+
7864
+ const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
7865
+
7866
+ /**
7867
+ * @license lucide-react v0.577.0 - ISC
7868
+ *
7869
+ * This source code is licensed under the ISC license.
7870
+ * See the LICENSE file in the root directory of this source tree.
7871
+ */
7872
+
7873
+ const toCamelCase = (string) => string.replace(
7874
+ /^([A-Z])|[\s-_]+(\w)/g,
7875
+ (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()
7876
+ );
7877
+
7878
+ /**
7879
+ * @license lucide-react v0.577.0 - ISC
7880
+ *
7881
+ * This source code is licensed under the ISC license.
7882
+ * See the LICENSE file in the root directory of this source tree.
7883
+ */
7884
+
7885
+
7886
+ const toPascalCase = (string) => {
7887
+ const camelCase = toCamelCase(string);
7888
+ return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
7889
+ };
7890
+
7891
+ /**
7892
+ * @license lucide-react v0.577.0 - ISC
7893
+ *
7894
+ * This source code is licensed under the ISC license.
7895
+ * See the LICENSE file in the root directory of this source tree.
7896
+ */
7897
+
7898
+ var defaultAttributes = {
7899
+ xmlns: "http://www.w3.org/2000/svg",
7900
+ width: 24,
7901
+ height: 24,
7902
+ viewBox: "0 0 24 24",
7903
+ fill: "none",
7904
+ stroke: "currentColor",
7905
+ strokeWidth: 2,
7906
+ strokeLinecap: "round",
7907
+ strokeLinejoin: "round"
7908
+ };
7909
+
7910
+ /**
7911
+ * @license lucide-react v0.577.0 - ISC
7912
+ *
7913
+ * This source code is licensed under the ISC license.
7914
+ * See the LICENSE file in the root directory of this source tree.
7915
+ */
7916
+
7917
+ const hasA11yProp = (props) => {
7918
+ for (const prop in props) {
7919
+ if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
7920
+ return true;
7921
+ }
7922
+ }
7923
+ return false;
7924
+ };
7925
+
7926
+ /**
7927
+ * @license lucide-react v0.577.0 - ISC
7928
+ *
7929
+ * This source code is licensed under the ISC license.
7930
+ * See the LICENSE file in the root directory of this source tree.
7931
+ */
7932
+
7933
+
7934
+ const Icon = forwardRef(
7935
+ ({
7936
+ color = "currentColor",
7937
+ size = 24,
7938
+ strokeWidth = 2,
7939
+ absoluteStrokeWidth,
7940
+ className = "",
7941
+ children,
7942
+ iconNode,
7943
+ ...rest
7944
+ }, ref) => createElement(
7945
+ "svg",
7946
+ {
7947
+ ref,
7948
+ ...defaultAttributes,
7949
+ width: size,
7950
+ height: size,
7951
+ stroke: color,
7952
+ strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
7953
+ className: mergeClasses("lucide", className),
7954
+ ...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
7955
+ ...rest
7956
+ },
7957
+ [
7958
+ ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
7959
+ ...Array.isArray(children) ? children : [children]
7960
+ ]
7961
+ )
7962
+ );
7963
+
7964
+ /**
7965
+ * @license lucide-react v0.577.0 - ISC
7966
+ *
7967
+ * This source code is licensed under the ISC license.
7968
+ * See the LICENSE file in the root directory of this source tree.
7969
+ */
7970
+
7971
+
7972
+ const createLucideIcon = (iconName, iconNode) => {
7973
+ const Component = forwardRef(
7974
+ ({ className, ...props }, ref) => createElement(Icon, {
7975
+ ref,
7976
+ iconNode,
7977
+ className: mergeClasses(
7978
+ `lucide-${toKebabCase(toPascalCase(iconName))}`,
7979
+ `lucide-${iconName}`,
7980
+ className
7981
+ ),
7982
+ ...props
7983
+ })
7984
+ );
7985
+ Component.displayName = toPascalCase(iconName);
7986
+ return Component;
7987
+ };
7988
+
7989
+ /**
7990
+ * @license lucide-react v0.577.0 - ISC
7991
+ *
7992
+ * This source code is licensed under the ISC license.
7993
+ * See the LICENSE file in the root directory of this source tree.
7994
+ */
7995
+
7996
+
7997
+ const __iconNode$o = [
7998
+ [
7999
+ "path",
8000
+ {
8001
+ d: "M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",
8002
+ key: "169zse"
8003
+ }
8004
+ ]
8005
+ ];
8006
+ const Activity = createLucideIcon("activity", __iconNode$o);
8007
+
8008
+ /**
8009
+ * @license lucide-react v0.577.0 - ISC
8010
+ *
8011
+ * This source code is licensed under the ISC license.
8012
+ * See the LICENSE file in the root directory of this source tree.
8013
+ */
8014
+
8015
+
8016
+ const __iconNode$n = [
8017
+ ["path", { d: "M10.268 21a2 2 0 0 0 3.464 0", key: "vwvbt9" }],
8018
+ [
8019
+ "path",
8020
+ {
8021
+ d: "M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326",
8022
+ key: "11g9vi"
8023
+ }
8024
+ ]
8025
+ ];
8026
+ const Bell = createLucideIcon("bell", __iconNode$n);
8027
+
8028
+ /**
8029
+ * @license lucide-react v0.577.0 - ISC
8030
+ *
8031
+ * This source code is licensed under the ISC license.
8032
+ * See the LICENSE file in the root directory of this source tree.
8033
+ */
8034
+
8035
+
8036
+ const __iconNode$m = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
8037
+ const Check = createLucideIcon("check", __iconNode$m);
8038
+
8039
+ /**
8040
+ * @license lucide-react v0.577.0 - ISC
8041
+ *
8042
+ * This source code is licensed under the ISC license.
8043
+ * See the LICENSE file in the root directory of this source tree.
8044
+ */
8045
+
8046
+
8047
+ const __iconNode$l = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
8048
+ const ChevronDown = createLucideIcon("chevron-down", __iconNode$l);
8049
+
8050
+ /**
8051
+ * @license lucide-react v0.577.0 - ISC
8052
+ *
8053
+ * This source code is licensed under the ISC license.
8054
+ * See the LICENSE file in the root directory of this source tree.
8055
+ */
8056
+
8057
+
8058
+ const __iconNode$k = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
8059
+ const ChevronRight = createLucideIcon("chevron-right", __iconNode$k);
8060
+
8061
+ /**
8062
+ * @license lucide-react v0.577.0 - ISC
8063
+ *
8064
+ * This source code is licensed under the ISC license.
8065
+ * See the LICENSE file in the root directory of this source tree.
8066
+ */
8067
+
8068
+
8069
+ const __iconNode$j = [["path", { d: "m18 15-6-6-6 6", key: "153udz" }]];
8070
+ const ChevronUp = createLucideIcon("chevron-up", __iconNode$j);
8071
+
8072
+ /**
8073
+ * @license lucide-react v0.577.0 - ISC
8074
+ *
8075
+ * This source code is licensed under the ISC license.
8076
+ * See the LICENSE file in the root directory of this source tree.
8077
+ */
8078
+
8079
+
8080
+ const __iconNode$i = [
8081
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
8082
+ ["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
8083
+ ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
8084
+ ];
8085
+ const CircleAlert = createLucideIcon("circle-alert", __iconNode$i);
8086
+
8087
+ /**
8088
+ * @license lucide-react v0.577.0 - ISC
8089
+ *
8090
+ * This source code is licensed under the ISC license.
8091
+ * See the LICENSE file in the root directory of this source tree.
8092
+ */
8093
+
8094
+
8095
+ const __iconNode$h = [
8096
+ ["path", { d: "M21.801 10A10 10 0 1 1 17 3.335", key: "yps3ct" }],
8097
+ ["path", { d: "m9 11 3 3L22 4", key: "1pflzl" }]
8098
+ ];
8099
+ const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$h);
8100
+
8101
+ /**
8102
+ * @license lucide-react v0.577.0 - ISC
8103
+ *
8104
+ * This source code is licensed under the ISC license.
8105
+ * See the LICENSE file in the root directory of this source tree.
8106
+ */
8107
+
8108
+
8109
+ const __iconNode$g = [
8110
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
8111
+ ["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
8112
+ ];
8113
+ const CircleCheck = createLucideIcon("circle-check", __iconNode$g);
8114
+
8115
+ /**
8116
+ * @license lucide-react v0.577.0 - ISC
8117
+ *
8118
+ * This source code is licensed under the ISC license.
8119
+ * See the LICENSE file in the root directory of this source tree.
8120
+ */
8121
+
8122
+
8123
+ const __iconNode$f = [
8124
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
8125
+ ["path", { d: "m15 9-6 6", key: "1uzhvr" }],
8126
+ ["path", { d: "m9 9 6 6", key: "z0biqf" }]
8127
+ ];
8128
+ const CircleX = createLucideIcon("circle-x", __iconNode$f);
8129
+
8130
+ /**
8131
+ * @license lucide-react v0.577.0 - ISC
8132
+ *
8133
+ * This source code is licensed under the ISC license.
8134
+ * See the LICENSE file in the root directory of this source tree.
8135
+ */
8136
+
8137
+
8138
+ const __iconNode$e = [
8139
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
8140
+ ["path", { d: "M12 6v6l4 2", key: "mmk7yg" }]
8141
+ ];
8142
+ const Clock = createLucideIcon("clock", __iconNode$e);
8143
+
8144
+ /**
8145
+ * @license lucide-react v0.577.0 - ISC
8146
+ *
8147
+ * This source code is licensed under the ISC license.
8148
+ * See the LICENSE file in the root directory of this source tree.
8084
8149
  */
8085
- function getTenantIcon(type) {
8086
- switch (type) {
8087
- case "organization":
8088
- return (jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" }) }));
8089
- case "project":
8090
- return (jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" }) }));
8091
- case "user":
8092
- return (jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }));
8093
- case "platform":
8094
- return (jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }));
8095
- default:
8096
- return null;
8097
- }
8098
- }
8150
+
8151
+
8152
+ const __iconNode$d = [
8153
+ ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
8154
+ ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
8155
+ ];
8156
+ const Copy = createLucideIcon("copy", __iconNode$d);
8157
+
8099
8158
  /**
8100
- * Get badge variant based on tenant type
8159
+ * @license lucide-react v0.577.0 - ISC
8160
+ *
8161
+ * This source code is licensed under the ISC license.
8162
+ * See the LICENSE file in the root directory of this source tree.
8101
8163
  */
8102
- function getTenantBadgeVariant(type) {
8103
- switch (type) {
8104
- case "organization":
8105
- return "default";
8106
- case "project":
8107
- return "secondary";
8108
- case "user":
8109
- return "default";
8110
- case "platform":
8111
- return "default";
8112
- default:
8113
- return "secondary";
8164
+
8165
+
8166
+ const __iconNode$c = [
8167
+ [
8168
+ "path",
8169
+ {
8170
+ d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",
8171
+ key: "1nclc0"
8114
8172
  }
8115
- }
8173
+ ],
8174
+ ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
8175
+ ];
8176
+ const Eye = createLucideIcon("eye", __iconNode$c);
8177
+
8116
8178
  /**
8117
- * TenantBadge displays tenant information in a compact badge format
8118
- * Renders as a clickable link if tenantLinkResolver is provided and returns a URL
8179
+ * @license lucide-react v0.577.0 - ISC
8180
+ *
8181
+ * This source code is licensed under the ISC license.
8182
+ * See the LICENSE file in the root directory of this source tree.
8119
8183
  */
8120
- function TenantBadge({ tenant, tenantLinkResolver, className = "", size = "default", }) {
8121
- const icon = getTenantIcon(tenant.type);
8122
- const variant = getTenantBadgeVariant(tenant.type);
8123
- const url = tenantLinkResolver?.(tenant);
8124
- const badgeContent = (jsxs(Badge, { variant: variant, className: cn("inline-flex items-center gap-1 whitespace-nowrap", size === "compact" ? "text-xs h-4 py-0 px-1.5" : "text-xs h-5 px-2", url && "cursor-pointer hover:opacity-80 transition-opacity", className), children: [icon, jsx("span", { className: "font-medium capitalize", children: tenant.type }), tenant.type !== "platform" && (jsxs(Fragment, { children: [jsx("span", { className: "text-muted-foreground", children: "/" }), jsx("span", { children: tenant.name })] }))] }));
8125
- if (url) {
8126
- return (jsx("a", { href: url, className: "no-underline", onClick: (e) => e.stopPropagation(), title: `View ${tenant.type}: ${tenant.name}`, children: badgeContent }));
8184
+
8185
+
8186
+ const __iconNode$b = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
8187
+ const LoaderCircle = createLucideIcon("loader-circle", __iconNode$b);
8188
+
8189
+ /**
8190
+ * @license lucide-react v0.577.0 - ISC
8191
+ *
8192
+ * This source code is licensed under the ISC license.
8193
+ * See the LICENSE file in the root directory of this source tree.
8194
+ */
8195
+
8196
+
8197
+ const __iconNode$a = [
8198
+ [
8199
+ "path",
8200
+ {
8201
+ d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
8202
+ key: "1a8usu"
8127
8203
  }
8128
- return badgeContent;
8129
- }
8204
+ ],
8205
+ ["path", { d: "m15 5 4 4", key: "1mk7zo" }]
8206
+ ];
8207
+ const Pencil = createLucideIcon("pencil", __iconNode$a);
8130
8208
 
8131
8209
  /**
8132
- * Extract tenant information from audit event annotations if present
8133
- * Expected annotations: tenant.type and tenant.name
8210
+ * @license lucide-react v0.577.0 - ISC
8211
+ *
8212
+ * This source code is licensed under the ISC license.
8213
+ * See the LICENSE file in the root directory of this source tree.
8134
8214
  */
8135
- function extractTenantFromAnnotations(event) {
8136
- const annotations = event.annotations;
8137
- if (!annotations)
8138
- return undefined;
8139
- const tenantType = annotations["tenant.type"];
8140
- const tenantName = annotations["tenant.name"];
8141
- if (tenantType && tenantName) {
8142
- const validTypes = [
8143
- "platform",
8144
- "organization",
8145
- "project",
8146
- "user",
8147
- ];
8148
- if (validTypes.includes(tenantType)) {
8149
- return {
8150
- type: tenantType,
8151
- name: tenantName,
8152
- };
8153
- }
8215
+
8216
+
8217
+ const __iconNode$9 = [
8218
+ ["path", { d: "M5 12h14", key: "1ays0h" }],
8219
+ ["path", { d: "M12 5v14", key: "s699le" }]
8220
+ ];
8221
+ const Plus = createLucideIcon("plus", __iconNode$9);
8222
+
8223
+ /**
8224
+ * @license lucide-react v0.577.0 - ISC
8225
+ *
8226
+ * This source code is licensed under the ISC license.
8227
+ * See the LICENSE file in the root directory of this source tree.
8228
+ */
8229
+
8230
+
8231
+ const __iconNode$8 = [
8232
+ ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
8233
+ ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
8234
+ ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
8235
+ ["path", { d: "M8 16H3v5", key: "1cv678" }]
8236
+ ];
8237
+ const RefreshCw = createLucideIcon("refresh-cw", __iconNode$8);
8238
+
8239
+ /**
8240
+ * @license lucide-react v0.577.0 - ISC
8241
+ *
8242
+ * This source code is licensed under the ISC license.
8243
+ * See the LICENSE file in the root directory of this source tree.
8244
+ */
8245
+
8246
+
8247
+ const __iconNode$7 = [
8248
+ ["path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8", key: "1p45f6" }],
8249
+ ["path", { d: "M21 3v5h-5", key: "1q7to0" }]
8250
+ ];
8251
+ const RotateCw = createLucideIcon("rotate-cw", __iconNode$7);
8252
+
8253
+ /**
8254
+ * @license lucide-react v0.577.0 - ISC
8255
+ *
8256
+ * This source code is licensed under the ISC license.
8257
+ * See the LICENSE file in the root directory of this source tree.
8258
+ */
8259
+
8260
+
8261
+ const __iconNode$6 = [
8262
+ ["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
8263
+ ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
8264
+ ];
8265
+ const Search = createLucideIcon("search", __iconNode$6);
8266
+
8267
+ /**
8268
+ * @license lucide-react v0.577.0 - ISC
8269
+ *
8270
+ * This source code is licensed under the ISC license.
8271
+ * See the LICENSE file in the root directory of this source tree.
8272
+ */
8273
+
8274
+
8275
+ const __iconNode$5 = [
8276
+ ["path", { d: "M10 5H3", key: "1qgfaw" }],
8277
+ ["path", { d: "M12 19H3", key: "yhmn1j" }],
8278
+ ["path", { d: "M14 3v4", key: "1sua03" }],
8279
+ ["path", { d: "M16 17v4", key: "1q0r14" }],
8280
+ ["path", { d: "M21 12h-9", key: "1o4lsq" }],
8281
+ ["path", { d: "M21 19h-5", key: "1rlt1p" }],
8282
+ ["path", { d: "M21 5h-7", key: "1oszz2" }],
8283
+ ["path", { d: "M8 10v4", key: "tgpxqk" }],
8284
+ ["path", { d: "M8 12H3", key: "a7s4jb" }]
8285
+ ];
8286
+ const SlidersHorizontal = createLucideIcon("sliders-horizontal", __iconNode$5);
8287
+
8288
+ /**
8289
+ * @license lucide-react v0.577.0 - ISC
8290
+ *
8291
+ * This source code is licensed under the ISC license.
8292
+ * See the LICENSE file in the root directory of this source tree.
8293
+ */
8294
+
8295
+
8296
+ const __iconNode$4 = [
8297
+ ["path", { d: "M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7", key: "1m0v6g" }],
8298
+ [
8299
+ "path",
8300
+ {
8301
+ d: "M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z",
8302
+ key: "ohrbg2"
8154
8303
  }
8155
- return undefined;
8156
- }
8304
+ ]
8305
+ ];
8306
+ const SquarePen = createLucideIcon("square-pen", __iconNode$4);
8307
+
8157
8308
  /**
8158
- * AuditEventViewer displays a list of audit events with details
8309
+ * @license lucide-react v0.577.0 - ISC
8310
+ *
8311
+ * This source code is licensed under the ISC license.
8312
+ * See the LICENSE file in the root directory of this source tree.
8159
8313
  */
8160
- function AuditEventViewer({ events, className = "", onEventSelect, tenantLinkResolver, }) {
8161
- const [selectedEvent, setSelectedEvent] = useState(null);
8162
- const [expandedEvents, setExpandedEvents] = useState(new Set());
8163
- const toggleEventExpansion = (auditId) => {
8164
- const newExpanded = new Set(expandedEvents);
8165
- if (expandedEvents.has(auditId)) {
8166
- newExpanded.delete(auditId);
8167
- }
8168
- else {
8169
- newExpanded.add(auditId);
8170
- }
8171
- setExpandedEvents(newExpanded);
8172
- };
8173
- const handleEventClick = (event) => {
8174
- setSelectedEvent(event);
8175
- if (onEventSelect) {
8176
- onEventSelect(event);
8177
- }
8178
- };
8179
- const formatTimestamp = (timestamp) => {
8180
- if (!timestamp)
8181
- return "N/A";
8182
- try {
8183
- return format(new Date(timestamp), "yyyy-MM-dd HH:mm:ss");
8184
- }
8185
- catch {
8186
- return timestamp;
8187
- }
8188
- };
8189
- const getVerbBadgeVariant = (verb) => {
8190
- switch (verb?.toLowerCase()) {
8191
- case "create":
8192
- return "success";
8193
- case "update":
8194
- case "patch":
8195
- return "warning";
8196
- case "delete":
8197
- return "destructive";
8198
- case "get":
8199
- case "list":
8200
- case "watch":
8201
- return "default";
8202
- default:
8203
- return "secondary";
8204
- }
8205
- };
8206
- if (events.length === 0) {
8207
- return (jsx("div", { className: `bg-muted rounded-lg border border-border ${className}`, children: jsx("div", { className: "p-12 text-center text-muted-foreground text-sm", children: "No events found" }) }));
8314
+
8315
+
8316
+ const __iconNode$3 = [
8317
+ ["path", { d: "M10 11v6", key: "nco0om" }],
8318
+ ["path", { d: "M14 11v6", key: "outv1u" }],
8319
+ ["path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6", key: "miytrc" }],
8320
+ ["path", { d: "M3 6h18", key: "d0wm0j" }],
8321
+ ["path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2", key: "e791ji" }]
8322
+ ];
8323
+ const Trash2 = createLucideIcon("trash-2", __iconNode$3);
8324
+
8325
+ /**
8326
+ * @license lucide-react v0.577.0 - ISC
8327
+ *
8328
+ * This source code is licensed under the ISC license.
8329
+ * See the LICENSE file in the root directory of this source tree.
8330
+ */
8331
+
8332
+
8333
+ const __iconNode$2 = [
8334
+ [
8335
+ "path",
8336
+ {
8337
+ d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",
8338
+ key: "wmoenq"
8208
8339
  }
8209
- return (jsx("div", { className: `bg-muted rounded-lg border border-border ${className}`, children: jsx("div", { className: "p-4", children: events.map((event) => {
8210
- const auditId = event.auditID || "";
8211
- const isExpanded = expandedEvents.has(auditId);
8212
- const tenant = extractTenantFromAnnotations(event);
8213
- return (jsxs(Card, { className: `p-5 mb-3 cursor-pointer transition-all hover:border-primary/50 hover:shadow-sm hover:-translate-y-px ${selectedEvent?.auditID === auditId
8214
- ? "border-primary bg-primary/5 shadow-md"
8215
- : ""}`, onClick: () => handleEventClick(event), children: [jsxs("div", { className: "flex justify-between items-center", children: [jsxs("div", { className: "flex gap-3 items-center flex-1", children: [jsx(Badge, { variant: getVerbBadgeVariant(event.verb), className: "px-3 py-1", children: event.verb?.toUpperCase() || "UNKNOWN" }), jsx("span", { className: "font-semibold", children: event.objectRef?.resource || "N/A" }), event.objectRef?.namespace && (jsxs("span", { className: "text-muted-foreground text-sm", children: ["ns: ", event.objectRef.namespace] })), event.objectRef?.name && (jsx("span", { className: "text-foreground/80 text-sm", children: event.objectRef.name })), tenant && (jsx(TenantBadge, { tenant: tenant, tenantLinkResolver: tenantLinkResolver, size: "compact" }))] }), jsxs("div", { className: "flex gap-4 items-center", children: [jsx("span", { className: "text-foreground/80 text-sm", children: event.user?.username || "N/A" }), jsx("span", { className: "text-muted-foreground text-sm font-mono", children: formatTimestamp(event.stageTimestamp) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", onClick: (e) => {
8216
- e.stopPropagation();
8217
- toggleEventExpansion(auditId);
8218
- }, className: "text-primary", children: isExpanded ? "▼" : "▶" })] })] }), isExpanded && (jsxs("div", { className: "mt-4 pt-4 border-t border-border", children: [jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "Event Information" }), jsxs("dl", { className: "grid grid-cols-[auto_1fr] gap-2 m-0", children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Audit ID:" }), jsx("dd", { className: "m-0 text-foreground", children: event.auditID || "N/A" }), jsx("dt", { className: "font-semibold text-foreground/80", children: "Stage:" }), jsx("dd", { className: "m-0 text-foreground", children: event.stage || "N/A" }), jsx("dt", { className: "font-semibold text-foreground/80", children: "Level:" }), jsx("dd", { className: "m-0 text-foreground", children: event.level || "N/A" }), jsx("dt", { className: "font-semibold text-foreground/80", children: "Request URI:" }), jsx("dd", { className: "m-0 text-foreground font-mono text-sm break-all", children: event.requestURI || "N/A" }), event.userAgent && (jsxs(Fragment, { children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "User Agent:" }), jsx("dd", { className: "m-0 text-foreground font-mono text-sm break-all", children: event.userAgent })] })), event.sourceIPs && event.sourceIPs.length > 0 && (jsxs(Fragment, { children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Source IPs:" }), jsx("dd", { className: "m-0 text-foreground", children: event.sourceIPs.join(", ") })] }))] })] }), tenant && (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "Tenant" }), jsx(TenantBadge, { tenant: tenant, tenantLinkResolver: tenantLinkResolver })] })), event.user && (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "User Information" }), jsxs("dl", { className: "grid grid-cols-[auto_1fr] gap-2 m-0", children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Username:" }), jsx("dd", { className: "m-0 text-foreground", children: event.user.username || "N/A" }), jsx("dt", { className: "font-semibold text-foreground/80", children: "UID:" }), jsx("dd", { className: "m-0 text-foreground", children: event.user.uid || "N/A" }), event.user.groups && event.user.groups.length > 0 && (jsxs(Fragment, { children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Groups:" }), jsx("dd", { className: "m-0 text-foreground", children: event.user.groups.join(", ") })] }))] })] })), event.responseStatus && (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "Response Status" }), jsxs("dl", { className: "grid grid-cols-[auto_1fr] gap-2 m-0", children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Code:" }), jsx("dd", { className: "m-0 text-foreground", children: event.responseStatus.code || "N/A" }), jsx("dt", { className: "font-semibold text-foreground/80", children: "Status:" }), jsx("dd", { className: "m-0 text-foreground", children: event.responseStatus.status || "N/A" }), event.responseStatus.message && (jsxs(Fragment, { children: [jsx("dt", { className: "font-semibold text-foreground/80", children: "Message:" }), jsx("dd", { className: "m-0 text-foreground", children: event.responseStatus.message })] }))] })] })), event.annotations &&
8219
- Object.keys(event.annotations).length > 0 && (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "Annotations" }), jsx("dl", { className: "grid grid-cols-[auto_1fr] gap-2 m-0", children: Object.entries(event.annotations).map(([key, value]) => (jsxs("div", { className: "contents", children: [jsxs("dt", { className: "font-semibold text-foreground/80", children: [key, ":"] }), jsx("dd", { className: "m-0 text-foreground", children: value })] }, key))) })] })), event.requestObject || event.responseObject ? (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "mt-0 mb-2 text-base text-foreground/80", children: "Request/Response Data" }), event.requestObject ? (jsxs("details", { className: "mt-2", children: [jsx("summary", { className: "cursor-pointer font-semibold p-2 bg-muted rounded", children: "Request Object" }), jsx("pre", { className: "mt-2 p-4 bg-muted rounded overflow-x-auto text-sm", children: JSON.stringify(event.requestObject, null, 2) })] })) : null, event.responseObject ? (jsxs("details", { className: "mt-2", children: [jsx("summary", { className: "cursor-pointer font-semibold p-2 bg-muted rounded", children: "Response Object" }), jsx("pre", { className: "mt-2 p-4 bg-muted rounded overflow-x-auto text-sm", children: JSON.stringify(event.responseObject, null, 2) })] })) : null] })) : null] }))] }, auditId));
8220
- }) }) }));
8221
- }
8340
+ ],
8341
+ ["path", { d: "M12 9v4", key: "juzpu7" }],
8342
+ ["path", { d: "M12 17h.01", key: "p32p05" }]
8343
+ ];
8344
+ const TriangleAlert = createLucideIcon("triangle-alert", __iconNode$2);
8345
+
8346
+ /**
8347
+ * @license lucide-react v0.577.0 - ISC
8348
+ *
8349
+ * This source code is licensed under the ISC license.
8350
+ * See the LICENSE file in the root directory of this source tree.
8351
+ */
8352
+
8353
+
8354
+ const __iconNode$1 = [
8355
+ ["path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2", key: "975kel" }],
8356
+ ["circle", { cx: "12", cy: "7", r: "4", key: "17ys0d" }]
8357
+ ];
8358
+ const User = createLucideIcon("user", __iconNode$1);
8359
+
8360
+ /**
8361
+ * @license lucide-react v0.577.0 - ISC
8362
+ *
8363
+ * This source code is licensed under the ISC license.
8364
+ * See the LICENSE file in the root directory of this source tree.
8365
+ */
8366
+
8367
+
8368
+ const __iconNode = [
8369
+ ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
8370
+ ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
8371
+ ];
8372
+ const X = createLucideIcon("x", __iconNode);
8222
8373
 
8223
8374
  /**
8224
8375
  * FilterChip - A compact pill/chip showing "Label: Values" with clear button
@@ -8343,7 +8494,7 @@ function FilterChip({ label, values, options = [], onValuesChange, onClear, inpu
8343
8494
  }, [options]);
8344
8495
  // Find selected options for chips display
8345
8496
  const selectedOptions = options.filter((opt) => values.includes(opt.value));
8346
- return (jsxs("div", { className: cn('inline-flex items-center', className), children: [jsxs(Popover.Root, { open: open, onOpenChange: handleOpenChange, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs("button", { type: "button", disabled: disabled, className: cn('flex h-7 items-center gap-2 rounded-l-md border border-r-0 border-border bg-card px-2 text-xs outline-none', 'hover:bg-accent/40 data-[state=open]:bg-accent/40 transition-colors', 'disabled:cursor-not-allowed disabled:opacity-50'), children: [jsxs("span", { className: "font-medium text-foreground", children: [label, ":"] }), jsx("span", { className: "text-foreground truncate max-w-[120px]", children: displayValue }), jsx(ChevronDown, { className: "h-3 w-3 text-muted-foreground ml-1" })] }) }), jsx(Popover.Portal, { children: jsx(Popover.Content, { className: cn('z-50 min-w-[var(--radix-popover-trigger-width)] max-w-[320px] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md', 'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2'), sideOffset: 4, align: "start", children: inputMode === 'typeahead' ? (jsxs(Command, { filter: filterOptions, className: "w-full", children: [jsx("div", { className: "flex items-center border-b px-3", children: jsx(CommandInput, { placeholder: searchPlaceholder, value: search, onValueChange: setSearch, className: "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50" }) }), values.length > 0 && (jsx("div", { className: "flex flex-wrap gap-1 p-2 border-b", children: selectedOptions.map((option) => (jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-accent text-accent-foreground text-xs", children: [option.label, jsx("button", { type: "button", onClick: (e) => handleRemoveValue(e, option.value), className: "rounded-sm hover:bg-accent-foreground/20", children: jsx(X, { className: "h-3 w-3" }) })] }, option.value))) })), jsxs(CommandList, { className: "max-h-[300px] overflow-y-auto p-1", children: [jsx(CommandEmpty, { className: "py-6 text-center text-sm text-muted-foreground", children: "No results found." }), jsx(CommandGroup, { children: options.map((option) => (jsxs(CommandItem, { value: option.value, onSelect: () => handleSelect(option.value), className: cn('relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', 'data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground', 'hover:bg-accent hover:text-accent-foreground'), children: [jsx("div", { className: cn('mr-2 h-4 w-4 shrink-0 rounded-sm border border-border', values.includes(option.value) && 'bg-primary border-primary'), children: values.includes(option.value) && (jsx("svg", { viewBox: "0 0 12 12", fill: "none", className: "h-full w-full p-0.5 text-primary-foreground", children: jsx("path", { d: "M2 6l3 3 5-5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })) }), jsx("span", { className: "flex-1 truncate", children: option.label }), option.count !== undefined && (jsxs("span", { className: "ml-2 text-xs text-muted-foreground", children: ["(", option.count, ")"] }))] }, option.value))) })] })] })) : (jsx("div", { className: "p-3", children: jsx(Input, { type: "text", value: textValue, onChange: handleTextChange, placeholder: placeholder, className: "w-full", autoFocus: true }) })) }) })] }), jsx("button", { type: "button", onClick: handleClearAll, disabled: disabled, className: cn('flex h-7 items-center rounded-r-md border border-border bg-card px-2 outline-none', 'hover:bg-accent/40 transition-colors', 'disabled:cursor-not-allowed disabled:opacity-50'), "aria-label": `Clear ${label} filter`, children: jsx(X, { className: "h-3 w-3 text-muted-foreground" }) })] }));
8497
+ return (jsxs(ButtonGroup, { "aria-label": `${label} filter`, className: className, children: [jsxs(Popover, { open: open, onOpenChange: handleOpenChange, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button, { htmlType: "button", type: "quaternary", theme: "outline", size: "small", disabled: disabled, className: "gap-2 font-normal data-[state=open]:bg-btn-neutral-bg", children: [jsxs("span", { className: "font-medium", children: [label, ":"] }), jsx("span", { className: "truncate max-w-[120px]", children: displayValue }), jsx(ChevronDown, { className: "h-3 w-3 text-muted-foreground" })] }) }), jsx(PopoverContent, { className: "w-auto min-w-[var(--radix-popover-trigger-width)] max-w-[320px] p-0", sideOffset: 4, align: "start", children: inputMode === 'typeahead' ? (jsxs(Command, { filter: filterOptions, children: [jsx(CommandInput, { placeholder: searchPlaceholder, value: search, onValueChange: setSearch }), values.length > 0 && (jsx("div", { className: "flex flex-wrap gap-1 p-2 border-b", children: selectedOptions.map((option) => (jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-accent text-accent-foreground text-xs", children: [option.label, jsx("button", { type: "button", onClick: (e) => handleRemoveValue(e, option.value), className: "rounded-sm hover:bg-accent-foreground/20", children: jsx(X, { className: "h-3 w-3" }) })] }, option.value))) })), jsxs(CommandList, { children: [jsx(CommandEmpty, { children: "No results found." }), jsx(CommandGroup, { children: options.map((option) => (jsxs(CommandItem, { value: option.value, onSelect: () => handleSelect(option.value), children: [jsx("div", { className: cn('mr-2 h-4 w-4 shrink-0 rounded-sm border border-border', values.includes(option.value) && 'bg-primary border-primary'), children: values.includes(option.value) && (jsx("svg", { viewBox: "0 0 12 12", fill: "none", className: "h-full w-full p-0.5 text-primary-foreground", children: jsx("path", { d: "M2 6l3 3 5-5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })) }), jsx("span", { className: "flex-1 truncate", children: option.label }), option.count !== undefined && (jsxs("span", { className: "ml-2 text-xs text-muted-foreground", children: ["(", option.count, ")"] }))] }, option.value))) })] })] })) : (jsx("div", { className: "p-3", children: jsx(Input, { type: "text", value: textValue, onChange: handleTextChange, placeholder: placeholder, className: "w-full", autoFocus: true }) })) })] }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "outline", size: "small", onClick: handleClearAll, disabled: disabled, "aria-label": `Clear ${label} filter`, children: jsx(X, { className: "h-3 w-3 text-muted-foreground" }) })] }));
8347
8498
  }
8348
8499
 
8349
8500
  /**
@@ -8360,12 +8511,12 @@ function AddFilterDropdown({ availableFilters, activeFilterIds, onAddFilter, has
8360
8511
  const isFilterActive = (filterId) => activeFilterIds.includes(filterId);
8361
8512
  // Get button label
8362
8513
  const buttonLabel = hasActiveFilters ? 'Filters' : 'Add Filters';
8363
- return (jsxs(Popover.Root, { open: open, onOpenChange: setOpen, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs("button", { type: "button", disabled: disabled, className: cn('flex h-7 items-center gap-2 rounded-md border border-dashed border-border bg-background px-2 text-xs', 'text-muted-foreground hover:text-foreground hover:border-foreground/50 transition-colors', 'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', 'disabled:cursor-not-allowed disabled:opacity-50', className), children: [jsx(Plus, { className: "h-4 w-4" }), jsx("span", { className: "font-medium", children: buttonLabel })] }) }), jsx(Popover.Portal, { children: jsx(Popover.Content, { className: cn('z-50 min-w-[180px] overflow-hidden rounded-md border shadow-md', 'bg-white dark:bg-slate-900 text-foreground', 'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2'), sideOffset: 4, align: "start", children: jsx("div", { className: "p-1", children: availableFilters.map((filter) => {
8364
- const active = isFilterActive(filter.id);
8365
- return (jsxs("button", { type: "button", onClick: () => !active && handleFilterClick(filter.id), disabled: active, className: cn('relative flex w-full items-center gap-2 rounded-sm px-3 py-2 text-sm outline-none transition-colors', active
8366
- ? 'cursor-not-allowed opacity-40'
8367
- : 'cursor-pointer hover:bg-accent hover:text-accent-foreground'), children: [filter.icon && jsx("span", { className: "text-muted-foreground", children: filter.icon }), jsx("span", { children: filter.label }), active && (jsx("span", { className: "ml-auto text-xs text-muted-foreground", children: "(active)" }))] }, filter.id));
8368
- }) }) }) })] }));
8514
+ return (jsxs(Popover, { open: open, onOpenChange: setOpen, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button, { htmlType: "button", type: "quaternary", theme: "outline", size: "small", disabled: disabled, className: cn('gap-1.5 border-dashed text-muted-foreground hover:text-foreground', className), children: [jsx(Plus, { className: "h-4 w-4" }), jsx("span", { className: "font-medium", children: buttonLabel })] }) }), jsx(PopoverContent, { className: "w-auto min-w-[180px] p-0", sideOffset: 4, align: "start", children: jsx("div", { className: "p-1", children: availableFilters.map((filter) => {
8515
+ const active = isFilterActive(filter.id);
8516
+ return (jsxs("button", { type: "button", onClick: () => !active && handleFilterClick(filter.id), disabled: active, className: cn('relative flex w-full items-center gap-2 rounded-sm px-3 py-2 text-sm outline-none transition-colors', active
8517
+ ? 'cursor-not-allowed opacity-40'
8518
+ : 'cursor-pointer hover:bg-accent hover:text-accent-foreground'), children: [filter.icon && jsx("span", { className: "text-muted-foreground", children: filter.icon }), jsx("span", { children: filter.label }), active && (jsx("span", { className: "ml-auto text-xs text-muted-foreground", children: "(active)" }))] }, filter.id));
8519
+ }) }) })] }));
8369
8520
  }
8370
8521
 
8371
8522
  /**
@@ -8389,10 +8540,10 @@ function ActionMultiSelect({ value, onChange, className = '', disabled = false,
8389
8540
  return '1 action';
8390
8541
  return `${value.length} actions`;
8391
8542
  }, [value.length]);
8392
- return (jsxs(Popover.Root, { open: open, onOpenChange: setOpen, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs("button", { type: "button", disabled: disabled || isLoading, className: cn('flex h-7 items-center justify-between gap-2 rounded-md border border-input bg-background px-2 text-xs min-w-[100px]', 'hover:bg-accent hover:text-accent-foreground transition-colors', 'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', 'disabled:cursor-not-allowed disabled:opacity-50', className), children: [jsx("span", { className: "font-medium", children: displayText }), jsx(ChevronDown, { className: "h-3 w-3 text-muted-foreground shrink-0" })] }) }), jsx(Popover.Portal, { children: jsx(Popover.Content, { className: cn('z-50 min-w-[160px] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md', 'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2'), sideOffset: 4, align: "start", children: jsx("div", { className: "p-1", children: isLoading ? (jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "Loading..." })) : options.length === 0 ? (jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "No actions found" })) : (options.map((option) => {
8393
- const checked = value.includes(option.value);
8394
- return (jsxs("label", { className: cn('flex items-center gap-2 rounded-sm px-3 py-2 text-sm cursor-pointer', 'hover:bg-accent hover:text-accent-foreground transition-colors'), children: [jsx(Checkbox, { checked: checked, onCheckedChange: () => handleToggle(option.value), className: "h-4 w-4" }), jsx("span", { className: "flex-1", children: option.label }), option.count !== undefined && (jsxs("span", { className: "text-xs text-muted-foreground", children: ["(", option.count, ")"] }))] }, option.value));
8395
- })) }) }) })] }));
8543
+ return (jsxs(Popover, { open: open, onOpenChange: setOpen, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button, { htmlType: "button", type: "quaternary", theme: "outline", size: "small", disabled: disabled || isLoading, className: cn('min-w-[100px] justify-between gap-2', className), children: [jsx("span", { className: "font-medium", children: displayText }), jsx(ChevronDown, { className: "h-3 w-3 text-muted-foreground shrink-0" })] }) }), jsx(PopoverContent, { className: "w-auto min-w-[160px] p-0", sideOffset: 4, align: "start", children: jsx("div", { className: "p-1", children: isLoading ? (jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "Loading..." })) : options.length === 0 ? (jsx("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "No actions found" })) : (options.map((option) => {
8544
+ const checked = value.includes(option.value);
8545
+ return (jsxs("label", { className: cn('flex items-center gap-2 rounded-sm px-3 py-2 text-sm cursor-pointer', 'hover:bg-accent hover:text-accent-foreground transition-colors'), children: [jsx(Checkbox, { checked: checked, onCheckedChange: () => handleToggle(option.value), className: "h-4 w-4" }), jsx("span", { className: "flex-1", children: option.label }), option.count !== undefined && (jsxs("span", { className: "text-xs text-muted-foreground", children: ["(", option.count, ")"] }))] }, option.value));
8546
+ })) }) })] }));
8396
8547
  }
8397
8548
 
8398
8549
  /**
@@ -8424,7 +8575,7 @@ function UserSelect({ value, options, onChange, className = '', disabled = false
8424
8575
  e.stopPropagation();
8425
8576
  onChange(undefined);
8426
8577
  }, [onChange]);
8427
- return (jsxs(Popover.Root, { open: open, onOpenChange: setOpen, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", disabled: disabled, className: cn('h-7 text-xs font-medium gap-1.5 px-2', value && 'pr-1', className), children: [jsx(User, { className: "h-3.5 w-3.5" }), jsx("span", { children: displayLabel }), value && (jsx("button", { onClick: handleClear, className: "ml-1 rounded-sm opacity-70 hover:opacity-100 hover:bg-muted p-0.5", "aria-label": "Clear user filter", children: jsx(X, { className: "h-3 w-3" }) }))] }) }), jsx(Popover.Portal, { children: jsxs(Popover.Content, { className: "z-50 w-64 rounded-md border bg-popover p-0 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", align: "start", sideOffset: 4, children: [jsx("div", { className: "border-b p-2", children: jsx("input", { type: "text", placeholder: "Search users...", value: search, onChange: (e) => setSearch(e.target.value), className: "w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-0", autoFocus: true }) }), jsxs("div", { className: "max-h-64 overflow-y-auto p-1", children: [jsxs("button", { onClick: () => handleSelect(undefined), className: cn('w-full flex items-center justify-between rounded-sm px-2 py-1.5 text-xs cursor-pointer hover:bg-accent hover:text-accent-foreground', !value && 'bg-accent text-accent-foreground'), children: [jsx("span", { className: "font-medium", children: "All Users" }), options.length > 0 && (jsx("span", { className: "text-muted-foreground", children: options.reduce((sum, opt) => sum + (opt.count || 0), 0) }))] }), isLoading && (jsx("div", { className: "px-2 py-4 text-center text-xs text-muted-foreground", children: "Loading users..." })), !isLoading && filteredOptions.length > 0 && (jsx("div", { className: "mt-1 border-t pt-1", children: filteredOptions.map((option) => (jsxs("button", { onClick: () => handleSelect(option.value), className: cn('w-full flex items-center justify-between rounded-sm px-2 py-1.5 text-xs cursor-pointer hover:bg-accent hover:text-accent-foreground', value === option.value && 'bg-accent text-accent-foreground'), children: [jsx("span", { className: "truncate font-medium", children: option.label }), option.count !== undefined && (jsx("span", { className: "text-muted-foreground ml-2 flex-shrink-0", children: option.count }))] }, option.value))) })), !isLoading && filteredOptions.length === 0 && search && (jsx("div", { className: "px-2 py-4 text-center text-xs text-muted-foreground", children: "No users found" })), !isLoading && options.length === 0 && !search && (jsx("div", { className: "px-2 py-4 text-center text-xs text-muted-foreground", children: "No users available" }))] })] }) })] }));
8578
+ return (jsxs(Popover, { open: open, onOpenChange: setOpen, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", disabled: disabled, className: cn('gap-1.5', value && 'pr-1.5', className), children: [jsx(User, { className: "h-3.5 w-3.5" }), jsx("span", { children: displayLabel }), value && (jsx("button", { onClick: handleClear, className: "ml-1 rounded-sm opacity-70 hover:opacity-100 hover:bg-muted p-0.5", "aria-label": "Clear user filter", children: jsx(X, { className: "h-3 w-3" }) }))] }) }), jsxs(PopoverContent, { className: "w-64 p-0", align: "start", sideOffset: 4, children: [jsx("div", { className: "border-b p-2", children: jsx("input", { type: "text", placeholder: "Search users...", value: search, onChange: (e) => setSearch(e.target.value), className: "w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-0", autoFocus: true }) }), jsxs("div", { className: "max-h-64 overflow-y-auto p-1", children: [jsxs("button", { onClick: () => handleSelect(undefined), className: cn('w-full flex items-center justify-between rounded-sm px-2 py-1.5 text-xs cursor-pointer hover:bg-accent hover:text-accent-foreground', !value && 'bg-accent text-accent-foreground'), children: [jsx("span", { className: "font-medium", children: "All Users" }), options.length > 0 && (jsx("span", { className: "text-muted-foreground", children: options.reduce((sum, opt) => sum + (opt.count || 0), 0) }))] }), isLoading && (jsx("div", { className: "px-2 py-4 text-center text-xs text-muted-foreground", children: "Loading users..." })), !isLoading && filteredOptions.length > 0 && (jsx("div", { className: "mt-1 border-t pt-1", children: filteredOptions.map((option) => (jsxs("button", { onClick: () => handleSelect(option.value), className: cn('w-full flex items-center justify-between rounded-sm px-2 py-1.5 text-xs cursor-pointer hover:bg-accent hover:text-accent-foreground', value === option.value && 'bg-accent text-accent-foreground'), children: [jsx("span", { className: "truncate font-medium", children: option.label }), option.count !== undefined && (jsx("span", { className: "text-muted-foreground ml-2 flex-shrink-0", children: option.count }))] }, option.value))) })), !isLoading && filteredOptions.length === 0 && search && (jsx("div", { className: "px-2 py-4 text-center text-xs text-muted-foreground", children: "No users found" })), !isLoading && options.length === 0 && !search && (jsx("div", { className: "px-2 py-4 text-center text-xs text-muted-foreground", children: "No users available" }))] })] })] }));
8428
8579
  }
8429
8580
 
8430
8581
  /**
@@ -8771,7 +8922,7 @@ function AuditLogFilters({ client, filters, timeRange, onFiltersChange, onTimeRa
8771
8922
  label: facet.value,
8772
8923
  count: facet.count,
8773
8924
  }));
8774
- return (jsx("div", { className: `border-b border-border py-4 pt-0 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [jsx(ActionMultiSelect, { value: getActionValues(), onChange: handleActionChange, options: actionOptions, disabled: disabled, isLoading: !verbs.length && !facetsError, className: "h-7 text-xs min-w-[180px]" }), jsx(UserSelect, { value: getCurrentUser(), options: userOptions, onChange: handleUserChange, disabled: disabled, isLoading: !usernames.length && !facetsError }), activeFilterIds.map((filterId) => {
8925
+ return (jsx("div", { className: `border-b border-border py-4 pt-0 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [jsx(ActionMultiSelect, { value: getActionValues(), onChange: handleActionChange, options: actionOptions, disabled: disabled, isLoading: !verbs.length && !facetsError, className: "min-w-[180px]" }), jsx(UserSelect, { value: getCurrentUser(), options: userOptions, onChange: handleUserChange, disabled: disabled, isLoading: !usernames.length && !facetsError }), activeFilterIds.map((filterId) => {
8775
8926
  const config = FILTER_CONFIGS$2[filterId];
8776
8927
  return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
8777
8928
  ? getFilterOptions(filterId)
@@ -8907,7 +9058,7 @@ function CopyButton({ value, label }) {
8907
9058
  console.error('Failed to copy:', err);
8908
9059
  }
8909
9060
  };
8910
- return (jsxs(Tooltip, { delayDuration: 400, children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { type: "button", onClick: handleCopy, className: "inline-flex items-center justify-center p-0.5 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-opacity cursor-pointer ml-1 opacity-0 group-hover:opacity-100", "aria-label": `Copy ${label}`, children: isCopied ? (jsx(Check, { className: "h-3 w-3 text-green-600 dark:text-green-400" })) : (jsx(Copy, { className: "h-3 w-3 text-gray-500 dark:text-gray-400" })) }) }), jsx(TooltipContent, { side: "top", children: jsx("p", { className: "text-xs", children: isCopied ? 'Copied!' : `Copy ${label}` }) })] }));
9061
+ return (jsxs(Tooltip, { delayDuration: 400, children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { type: "button", onClick: handleCopy, className: "inline-flex items-center justify-center p-0.5 rounded hover:bg-muted transition-opacity cursor-pointer ml-1 opacity-0 group-hover:opacity-100", "aria-label": `Copy ${label}`, children: isCopied ? (jsx(Check, { className: "h-3 w-3 text-[var(--success-500)]" })) : (jsx(Copy, { className: "h-3 w-3 text-muted-foreground" })) }) }), jsx(TooltipContent, { side: "top", children: jsx("p", { className: "text-xs", children: isCopied ? 'Copied!' : `Copy ${label}` }) })] }));
8911
9062
  }
8912
9063
  /**
8913
9064
  * A label / value row inside a Section. The value is truncated to one
@@ -8985,8 +9136,8 @@ function AuditLogExpandedDetails({ event, compact = true }) {
8985
9136
  const status = event.responseStatus;
8986
9137
  const isOk = status?.code != null && status.code >= 200 && status.code < 300;
8987
9138
  const body = (jsxs(Fragment, { children: [jsxs(DetailGrid, { children: [jsxs(Section, { title: "Request", children: [jsx(Field, { label: "Verb", value: event.verb || 'Unknown', copyValue: event.verb || undefined, copyLabel: "verb" }), event.requestURI ? (jsx(Field, { label: "URI", value: event.requestURI, copyValue: event.requestURI, mono: true })) : null] }), status ? (jsxs(Section, { title: "Response", children: [status.code != null ? (jsx(Field, { label: "Status code", value: jsxs("span", { className: isOk
8988
- ? 'text-green-600 dark:text-green-400'
8989
- : 'text-red-600 dark:text-red-400', children: [isOk ? '✓ ' : '✗ ', status.code] }), copyValue: String(status.code), copyLabel: "status code" })) : null, status.status ? jsx(Field, { label: "Status", value: status.status }) : null, status.reason ? jsx(Field, { label: "Reason", value: status.reason }) : null, status.message ? (jsx(Field, { label: "Message", value: status.message, copyValue: status.message })) : null] })) : null, jsx(Section, { title: "When", children: jsx(Field, { label: "Timestamp", value: jsx(Timestamp, { value: timestamp, variant: "iso-utc" }), copyValue: timestamp || '', copyLabel: "timestamp", disableTooltip: true }) }), event.user ? (jsxs(Section, { title: "User", children: [event.user.username ? (jsx(Field, { label: "Username", value: event.user.username, copyValue: event.user.username })) : null, event.user.uid ? (jsx(Field, { label: "UID", value: event.user.uid, copyValue: event.user.uid, mono: true })) : null, event.user.groups && event.user.groups.length > 0 ? (jsx(Field, { label: "Groups", value: event.user.groups.join(', ') })) : null] })) : null, event.objectRef ? (jsxs(Section, { title: "Resource", children: [jsx(Field, { label: "Kind", value: jsxs("span", { children: [event.objectRef.resource || 'Unknown', event.objectRef.apiGroup ? (jsxs("span", { className: "ml-1 text-muted-foreground", children: ["\u00B7 ", event.objectRef.apiGroup] })) : null] }), copyValue: event.objectRef.resource, copyLabel: "resource kind" }), event.objectRef.name ? (jsx(Field, { label: "Name", value: event.objectRef.name, copyValue: event.objectRef.name, copyLabel: "resource name" })) : null, event.objectRef.namespace ? (jsx(Field, { label: "Namespace", value: event.objectRef.namespace, copyValue: event.objectRef.namespace })) : null, event.objectRef.apiVersion ? (jsx(Field, { label: "API version", value: event.objectRef.apiVersion })) : null, event.objectRef.subresource ? (jsx(Field, { label: "Subresource", value: event.objectRef.subresource })) : null, event.objectRef.uid ? (jsx(Field, { label: "UID", value: event.objectRef.uid, copyValue: event.objectRef.uid, mono: true })) : null] })) : null, event.userAgent || (event.sourceIPs && event.sourceIPs.length > 0) ? (jsxs(Section, { title: "Source", children: [event.sourceIPs && event.sourceIPs.length > 0 ? (jsx(Field, { label: "Source IPs", value: event.sourceIPs.join(', ') })) : null, event.userAgent ? (jsx(Field, { label: "User agent", value: event.userAgent, copyValue: event.userAgent, mono: true })) : null] })) : null] }), event.auditID || event.stage || event.level ||
9139
+ ? 'text-[var(--success-500)]'
9140
+ : 'text-destructive', children: [isOk ? '✓ ' : '✗ ', status.code] }), copyValue: String(status.code), copyLabel: "status code" })) : null, status.status ? jsx(Field, { label: "Status", value: status.status }) : null, status.reason ? jsx(Field, { label: "Reason", value: status.reason }) : null, status.message ? (jsx(Field, { label: "Message", value: status.message, copyValue: status.message })) : null] })) : null, jsx(Section, { title: "When", children: jsx(Field, { label: "Timestamp", value: jsx(Timestamp, { value: timestamp, variant: "iso-utc" }), copyValue: timestamp || '', copyLabel: "timestamp", disableTooltip: true }) }), event.user ? (jsxs(Section, { title: "User", children: [event.user.username ? (jsx(Field, { label: "Username", value: event.user.username, copyValue: event.user.username })) : null, event.user.uid ? (jsx(Field, { label: "UID", value: event.user.uid, copyValue: event.user.uid, mono: true })) : null, event.user.groups && event.user.groups.length > 0 ? (jsx(Field, { label: "Groups", value: event.user.groups.join(', ') })) : null] })) : null, event.objectRef ? (jsxs(Section, { title: "Resource", children: [jsx(Field, { label: "Kind", value: jsxs("span", { children: [event.objectRef.resource || 'Unknown', event.objectRef.apiGroup ? (jsxs("span", { className: "ml-1 text-muted-foreground", children: ["\u00B7 ", event.objectRef.apiGroup] })) : null] }), copyValue: event.objectRef.resource, copyLabel: "resource kind" }), event.objectRef.name ? (jsx(Field, { label: "Name", value: event.objectRef.name, copyValue: event.objectRef.name, copyLabel: "resource name" })) : null, event.objectRef.namespace ? (jsx(Field, { label: "Namespace", value: event.objectRef.namespace, copyValue: event.objectRef.namespace })) : null, event.objectRef.apiVersion ? (jsx(Field, { label: "API version", value: event.objectRef.apiVersion })) : null, event.objectRef.subresource ? (jsx(Field, { label: "Subresource", value: event.objectRef.subresource })) : null, event.objectRef.uid ? (jsx(Field, { label: "UID", value: event.objectRef.uid, copyValue: event.objectRef.uid, mono: true })) : null] })) : null, event.userAgent || (event.sourceIPs && event.sourceIPs.length > 0) ? (jsxs(Section, { title: "Source", children: [event.sourceIPs && event.sourceIPs.length > 0 ? (jsx(Field, { label: "Source IPs", value: event.sourceIPs.join(', ') })) : null, event.userAgent ? (jsx(Field, { label: "User agent", value: event.userAgent, copyValue: event.userAgent, mono: true })) : null] })) : null] }), event.auditID || event.stage || event.level ||
8990
9141
  (event.annotations && Object.keys(event.annotations).length > 0) ? (jsxs("details", { className: "group mt-5", children: [jsx("summary", { className: "cursor-pointer list-none", children: jsxs("h4", { className: "inline-flex items-center m-0 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground/80 hover:text-foreground", children: [jsx("span", { className: "mr-1 group-open:rotate-90 transition-transform", children: "\u25B8" }), "Advanced"] }) }), jsx("div", { className: "mt-3 pl-3", children: jsxs(DetailGrid, { children: [jsxs(Section, { title: "Audit", children: [event.auditID ? (jsx(Field, { label: "Audit ID", value: event.auditID, copyValue: event.auditID, mono: true })) : null, event.stage ? jsx(Field, { label: "Stage", value: event.stage }) : null, event.level ? jsx(Field, { label: "Level", value: event.level }) : null] }), event.annotations && Object.keys(event.annotations).length > 0 ? (jsx(Section, { title: "Annotations", children: Object.entries(event.annotations).map(([key, value]) => (jsx(Field, { label: key, value: value, mono: true }, key))) })) : null] }) })] })) : null, event.requestObject || event.responseObject ? (jsxs("details", { className: "group mt-3", children: [jsx("summary", { className: "cursor-pointer list-none", children: jsxs("h4", { className: "inline-flex items-center m-0 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground/80 hover:text-foreground", children: [jsx("span", { className: "mr-1 group-open:rotate-90 transition-transform", children: "\u25B8" }), "Raw objects"] }) }), jsxs("div", { className: "mt-3 space-y-3", children: [event.requestObject ? (jsxs("div", { children: [jsx("div", { className: "mb-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Request" }), jsx("pre", { className: "m-0 p-3 bg-muted rounded overflow-x-auto text-xs font-mono", children: JSON.stringify(event.requestObject, null, 2) })] })) : null, event.responseObject ? (jsxs("div", { children: [jsx("div", { className: "mb-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Response" }), jsx("pre", { className: "m-0 p-3 bg-muted rounded overflow-x-auto text-xs font-mono", children: JSON.stringify(event.responseObject, null, 2) })] })) : null] })] })) : null] }));
8991
9142
  return (jsx(TooltipProvider, { children: compact ? (jsx(DetailPanelShell, { children: body })) : (jsx("div", { className: "mt-4 pt-4 border-t border-border", children: body })) }));
8992
9143
  }
@@ -8994,22 +9145,21 @@ function AuditLogExpandedDetails({ event, compact = true }) {
8994
9145
  // Number of columns rendered for the audit log table. Used by the colSpan
8995
9146
  // on the expanded-detail row so it spans the full width.
8996
9147
  const AUDIT_LOG_COLUMN_COUNT = 5;
9148
+ const VERB_BADGE_CLASSES = 'text-[0.55rem] h-5 px-2 py-1 leading-3';
8997
9149
  /**
8998
- * Get Tailwind classes for verb badge
9150
+ * Get the badge variant for a verb
8999
9151
  */
9000
- function getVerbBadgeClasses(verb) {
9001
- const baseClasses = 'text-[0.55rem] h-5 px-2 py-1 leading-3';
9002
- const normalized = verb?.toLowerCase();
9003
- switch (normalized) {
9152
+ function getVerbBadgeVariant(verb) {
9153
+ switch (verb?.toLowerCase()) {
9004
9154
  case 'create':
9005
- return cn(baseClasses, 'bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300');
9155
+ return 'success';
9006
9156
  case 'update':
9007
9157
  case 'patch':
9008
- return cn(baseClasses, 'bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300');
9158
+ return 'warning';
9009
9159
  case 'delete':
9010
- return cn(baseClasses, 'bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300');
9160
+ return 'destructive';
9011
9161
  default:
9012
- return cn(baseClasses, 'bg-muted text-muted-foreground');
9162
+ return 'outline';
9013
9163
  }
9014
9164
  }
9015
9165
  /**
@@ -9020,9 +9170,9 @@ function getResponseStatusIndicator(code) {
9020
9170
  return { icon: '?', className: 'text-muted-foreground' };
9021
9171
  }
9022
9172
  if (code >= 200 && code < 300) {
9023
- return { icon: '✓', className: 'text-green-600 dark:text-green-400' };
9173
+ return { icon: '✓', className: 'text-[var(--success-500)]' };
9024
9174
  }
9025
- return { icon: '✗', className: 'text-red-600 dark:text-red-400' };
9175
+ return { icon: '✗', className: 'text-destructive' };
9026
9176
  }
9027
9177
  /**
9028
9178
  * Build human-readable summary
@@ -9063,14 +9213,14 @@ function AuditLogFeedItem({ event, onEventClick, isSelected = false, className =
9063
9213
  verb === 'delete' ? Trash2 :
9064
9214
  verb === 'update' || verb === 'patch' ? Pencil :
9065
9215
  Activity;
9066
- const iconBg = verb === 'create' ? 'bg-green-50 dark:bg-green-950' :
9067
- verb === 'delete' ? 'bg-red-50 dark:bg-red-950' :
9068
- verb === 'update' || verb === 'patch' ? 'bg-amber-50 dark:bg-amber-950' :
9069
- 'bg-slate-100 dark:bg-slate-800';
9070
- const iconColor = verb === 'create' ? 'text-green-600 dark:text-green-400' :
9071
- verb === 'delete' ? 'text-red-500 dark:text-red-400' :
9072
- verb === 'update' || verb === 'patch' ? 'text-amber-600 dark:text-amber-400' :
9073
- 'text-slate-500 dark:text-slate-400';
9216
+ const iconBg = verb === 'create' ? 'bg-[var(--success-100)]' :
9217
+ verb === 'delete' ? 'bg-destructive/10' :
9218
+ verb === 'update' || verb === 'patch' ? 'bg-[color-mix(in_oklab,var(--badge-warning)_15%,transparent)]' :
9219
+ 'bg-muted';
9220
+ const iconColor = verb === 'create' ? 'text-[var(--success-500)]' :
9221
+ verb === 'delete' ? 'text-destructive' :
9222
+ verb === 'update' || verb === 'patch' ? 'text-[var(--badge-warning)]' :
9223
+ 'text-muted-foreground';
9074
9224
  return (jsxs("div", { className: cn(!isLast && !isExpanded && 'border-b border-border', className), children: [jsxs("div", { className: cn('flex items-center gap-3 cursor-pointer group', compact ? 'py-2' : 'py-3', isSelected && 'bg-muted/40'), onClick: toggleExpand, children: [jsx("div", { className: cn('w-8 h-8 rounded-md shrink-0 flex items-center justify-center', iconBg, iconColor), title: event.verb, children: jsx(Icon, { size: 16, strokeWidth: 2 }) }), jsx("div", { className: "flex-1 min-w-0 text-sm leading-snug", style: { minWidth: 0 }, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("div", { className: "text-foreground", style: {
9075
9225
  display: 'block',
9076
9226
  overflow: 'hidden',
@@ -9079,10 +9229,10 @@ function AuditLogFeedItem({ event, onEventClick, isSelected = false, className =
9079
9229
  cursor: 'help',
9080
9230
  }, children: summary }) }), jsx(TooltipContent, { className: "max-w-[480px] whitespace-normal break-words", children: summary })] }) }), jsxs("span", { className: cn('inline-flex items-center gap-1 text-xs font-semibold shrink-0', statusIndicator.className), children: [jsx("span", { children: statusIndicator.icon }), event.responseStatus?.code ? jsx("span", { children: event.responseStatus.code }) : null] }), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: jsx(Timestamp, { value: timestamp }) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", htmlType: "button", className: "h-5 py-0 px-1 text-base text-muted-foreground hover:text-foreground shrink-0", onClick: toggleExpand, "aria-expanded": isExpanded, "aria-label": isExpanded ? 'Collapse details' : 'Expand details', children: isExpanded ? '−' : '+' })] }), isExpanded ? jsx(AuditLogExpandedDetails, { event: event }) : null] }));
9081
9231
  }
9082
- return (jsxs(Fragment, { children: [jsxs(TableRow, { "data-state": isSelected ? 'selected' : undefined, className: cn('cursor-pointer', isNew && 'bg-green-50/40 dark:bg-green-950/20', className), onClick: (e) => {
9232
+ return (jsxs(Fragment, { children: [jsxs(TableRow, { "data-state": isSelected ? 'selected' : undefined, className: cn('cursor-pointer', isNew && 'bg-[var(--success-100)]/60', className), onClick: (e) => {
9083
9233
  toggleExpand(e);
9084
9234
  handleClick();
9085
- }, "aria-expanded": isExpanded, children: [jsx(TableCell, { className: "py-2 align-middle whitespace-nowrap", children: jsx(Badge, { className: getVerbBadgeClasses(event.verb), children: event.verb?.toUpperCase() || 'UNKNOWN' }) }), jsx(TableCell, { className: "py-2 align-middle", style: { width: '100%', maxWidth: 0, overflow: 'hidden' }, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("div", { className: "text-sm leading-snug", style: {
9235
+ }, "aria-expanded": isExpanded, children: [jsx(TableCell, { className: "py-2 align-middle whitespace-nowrap", children: jsx(Badge, { variant: getVerbBadgeVariant(event.verb), className: VERB_BADGE_CLASSES, children: event.verb?.toUpperCase() || 'UNKNOWN' }) }), jsx(TableCell, { className: "py-2 align-middle", style: { width: '100%', maxWidth: 0, overflow: 'hidden' }, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("div", { className: "text-sm leading-snug", style: {
9086
9236
  overflow: 'hidden',
9087
9237
  textOverflow: 'ellipsis',
9088
9238
  whiteSpace: 'nowrap',
@@ -9092,7 +9242,7 @@ function AuditLogFeedItem({ event, onEventClick, isSelected = false, className =
9092
9242
  /**
9093
9243
  * React hook for executing audit log queries
9094
9244
  */
9095
- function useAuditLogQuery({ client, autoExecute = false, }) {
9245
+ function useAuditLogQuery({ client, }) {
9096
9246
  const [query, setQuery] = useState(null);
9097
9247
  const [events, setEvents] = useState([]);
9098
9248
  const [isLoading, setIsLoading] = useState(false);
@@ -9634,6 +9784,14 @@ function ActionToggle({ value, onChange, className = '', disabled = false, }) {
9634
9784
 
9635
9785
  // Debounce delay for filter changes (ms)
9636
9786
  const FILTER_DEBOUNCE_MS$1 = 300;
9787
+ // Backoff for watch stream reconnects. Without this, a persistently failing
9788
+ // upstream gets re-opened on every render with no delay — see
9789
+ // https://github.com/datum-cloud/infra/issues/5060, where a single browser
9790
+ // tab reconnected roughly 2.5 times a minute against a stream that kept
9791
+ // aborting, driving the proxy's memory up until the process was OOMKilled.
9792
+ const RECONNECT_BASE_DELAY_MS = 1000;
9793
+ const RECONNECT_MAX_DELAY_MS = 30000;
9794
+ const MAX_RECONNECT_ATTEMPTS = 8;
9637
9795
  /**
9638
9796
  * Escape a value for safe interpolation into a CEL string literal.
9639
9797
  * Prevents injection via values containing backslashes or double quotes.
@@ -9728,6 +9886,12 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
9728
9886
  const resourceVersionRef = useRef();
9729
9887
  // Track the watch stop function
9730
9888
  const watchStopRef = useRef(null);
9889
+ // Consecutive watch failures since the last successful event, used to back
9890
+ // off reconnect attempts. Reset on any real event (including BOOKMARK) and
9891
+ // on a manual startStreaming() call.
9892
+ const reconnectAttemptsRef = useRef(0);
9893
+ // Pending scheduled reconnect, if any.
9894
+ const reconnectTimerRef = useRef(null);
9731
9895
  // Track whether streaming should restart after filter change
9732
9896
  const shouldRestartStreamingRef = useRef(false);
9733
9897
  // Track if we've done the initial load
@@ -9802,6 +9966,9 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
9802
9966
  console.error('Watch error:', event.object);
9803
9967
  return;
9804
9968
  }
9969
+ // A real event (including a BOOKMARK keep-alive) means the connection is
9970
+ // healthy — reset backoff so a later failure starts from the base delay.
9971
+ reconnectAttemptsRef.current = 0;
9805
9972
  if (event.type === 'BOOKMARK') {
9806
9973
  // Update resource version for resume capability
9807
9974
  if (event.object.metadata?.resourceVersion) {
@@ -9867,15 +10034,13 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
9867
10034
  setActivities((prev) => prev.filter((a) => a.metadata?.name !== event.object.metadata?.name));
9868
10035
  }
9869
10036
  }, [filters]);
9870
- // Start watching for real-time updates
9871
- const startStreaming = useCallback(() => {
10037
+ // Open the watch connection. Does not touch userPaused/backoff state —
10038
+ // callers (startStreaming, scheduleReconnect) own that.
10039
+ const connectWatch = useCallback(() => {
9872
10040
  if (watchStopRef.current) {
9873
10041
  // Already watching
9874
10042
  return;
9875
10043
  }
9876
- // Explicit start clears the user-paused flag so the auto-restart
9877
- // effects can keep the stream alive after subsequent filter changes.
9878
- setUserPaused(false);
9879
10044
  // Clear any previous watch error when starting a new stream
9880
10045
  setWatchError(null);
9881
10046
  const params = buildWatchParams();
@@ -9889,6 +10054,7 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
9889
10054
  setWatchError(streamError);
9890
10055
  setIsStreaming(false);
9891
10056
  watchStopRef.current = null;
10057
+ reconnectAttemptsRef.current += 1;
9892
10058
  },
9893
10059
  onClose: () => {
9894
10060
  setIsStreaming(false);
@@ -9899,11 +10065,50 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
9899
10065
  setIsStreaming(true);
9900
10066
  setNewActivitiesCount(0);
9901
10067
  }, [client, buildWatchParams, handleWatchEvent]);
10068
+ // Reconnect with exponential backoff. The first attempt (no prior
10069
+ // failures) connects immediately; each subsequent failure doubles the
10070
+ // delay up to RECONNECT_MAX_DELAY_MS. After MAX_RECONNECT_ATTEMPTS
10071
+ // consecutive failures, this stops retrying automatically — the watch
10072
+ // stays stopped (with watchError set) until the user calls
10073
+ // startStreaming() again, which resets the attempt count.
10074
+ const scheduleReconnect = useCallback(() => {
10075
+ if (reconnectTimerRef.current) {
10076
+ // A reconnect is already scheduled.
10077
+ return;
10078
+ }
10079
+ if (reconnectAttemptsRef.current >= MAX_RECONNECT_ATTEMPTS) {
10080
+ return;
10081
+ }
10082
+ const attempt = reconnectAttemptsRef.current;
10083
+ const delay = attempt === 0 ? 0 : Math.min(RECONNECT_MAX_DELAY_MS, RECONNECT_BASE_DELAY_MS * 2 ** (attempt - 1));
10084
+ reconnectTimerRef.current = setTimeout(() => {
10085
+ reconnectTimerRef.current = null;
10086
+ connectWatch();
10087
+ }, delay);
10088
+ }, [connectWatch]);
10089
+ // Start watching for real-time updates. This is the user/effect-facing
10090
+ // entry point: it resets backoff and any pending scheduled reconnect
10091
+ // before connecting, so a manual retry always starts from a clean slate.
10092
+ const startStreaming = useCallback(() => {
10093
+ // Explicit start clears the user-paused flag so the auto-restart
10094
+ // effects can keep the stream alive after subsequent filter changes.
10095
+ setUserPaused(false);
10096
+ reconnectAttemptsRef.current = 0;
10097
+ if (reconnectTimerRef.current) {
10098
+ clearTimeout(reconnectTimerRef.current);
10099
+ reconnectTimerRef.current = null;
10100
+ }
10101
+ connectWatch();
10102
+ }, [connectWatch]);
9902
10103
  // Stop watching. Marks the user as paused so the auto-start effects
9903
10104
  // below don't immediately re-open the stream this call just closed.
9904
10105
  const stopStreaming = useCallback(() => {
9905
10106
  setUserPaused(true);
9906
10107
  setIsStreaming(false);
10108
+ if (reconnectTimerRef.current) {
10109
+ clearTimeout(reconnectTimerRef.current);
10110
+ reconnectTimerRef.current = null;
10111
+ }
9907
10112
  if (watchStopRef.current) {
9908
10113
  watchStopRef.current();
9909
10114
  watchStopRef.current = null;
@@ -10066,9 +10271,10 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
10066
10271
  }
10067
10272
  };
10068
10273
  }, [filters, timeRange]); // eslint-disable-line react-hooks/exhaustive-deps
10069
- // Auto-start streaming after initial load when enabled. Skipped when
10070
- // the user has explicitly paused — otherwise clicking Pause would be
10071
- // immediately undone by this effect on the next render.
10274
+ // Auto-start streaming after initial load when enabled, and reconnect
10275
+ // (with backoff — see scheduleReconnect) whenever a connection drops.
10276
+ // Skipped when the user has explicitly paused — otherwise clicking Pause
10277
+ // would be immediately undone by this effect on the next render.
10072
10278
  useEffect(() => {
10073
10279
  if (enableStreaming &&
10074
10280
  autoStartStreaming &&
@@ -10076,9 +10282,9 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
10076
10282
  activities.length > 0 &&
10077
10283
  !isStreaming &&
10078
10284
  !isLoading) {
10079
- startStreaming();
10285
+ scheduleReconnect();
10080
10286
  }
10081
- }, [enableStreaming, autoStartStreaming, userPaused, activities.length, isStreaming, isLoading, startStreaming]);
10287
+ }, [enableStreaming, autoStartStreaming, userPaused, activities.length, isStreaming, isLoading, scheduleReconnect]);
10082
10288
  // Restart streaming after filter change refresh completes. Also skipped
10083
10289
  // when the user has paused — a filter change shouldn't silently resume
10084
10290
  // a stream the user intentionally stopped.
@@ -10090,15 +10296,18 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
10090
10296
  !isStreaming &&
10091
10297
  !isLoading) {
10092
10298
  shouldRestartStreamingRef.current = false;
10093
- startStreaming();
10299
+ scheduleReconnect();
10094
10300
  }
10095
- }, [enableStreaming, userPaused, activities.length, isStreaming, isLoading, startStreaming]);
10301
+ }, [enableStreaming, userPaused, activities.length, isStreaming, isLoading, scheduleReconnect]);
10096
10302
  // Cleanup on unmount
10097
10303
  useEffect(() => {
10098
10304
  return () => {
10099
10305
  if (watchStopRef.current) {
10100
10306
  watchStopRef.current();
10101
10307
  }
10308
+ if (reconnectTimerRef.current) {
10309
+ clearTimeout(reconnectTimerRef.current);
10310
+ }
10102
10311
  if (filterDebounceRef.current) {
10103
10312
  clearTimeout(filterDebounceRef.current);
10104
10313
  }
@@ -10259,40 +10468,10 @@ function ActivityExpandedDetails({ activity, compact = false, }) {
10259
10468
  const timestamp = metadata?.creationTimestamp;
10260
10469
  const actorDisplay = actor.displayName || actor.name;
10261
10470
  const actorIsUser = actor.type === 'user';
10262
- const body = (jsxs(Fragment, { children: [changes && changes.length > 0 ? (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "m-0 mb-2 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground/80", children: "Changes" }), jsx("div", { className: "flex flex-col gap-1.5", children: changes.map((change, index) => (jsxs("div", { className: "rounded bg-muted px-2.5 py-1.5 text-xs", children: [jsx("span", { className: "block font-mono text-[11px] font-semibold text-foreground", children: change.field }), change.old ? (jsxs("span", { className: "block ml-2 text-red-600 dark:text-red-400", children: [jsx("span", { className: "mr-1 font-medium", children: "\u2212" }), jsx("span", { className: "line-through", children: change.old })] })) : null, change.new ? (jsxs("span", { className: "block ml-2 text-green-600 dark:text-green-400", children: [jsx("span", { className: "mr-1 font-medium", children: "+" }), change.new] })) : null] }, index))) })] })) : null, jsxs(DetailGrid, { children: [jsx(Section, { title: "When", children: jsx(Field, { label: "Timestamp", value: jsx(Timestamp, { value: timestamp, variant: "iso-utc" }), copyValue: timestamp ?? '', copyLabel: "timestamp", disableTooltip: true }) }), jsxs(Section, { title: "Actor", children: [jsx(Field, { label: "Name", value: jsxs("span", { children: [actorDisplay, actor.type ? (jsxs("span", { className: "ml-1 text-muted-foreground", children: ["(", actor.type, ")"] })) : null] }), copyValue: actorDisplay, copyLabel: "actor name" }), actorIsUser && actor.email ? (jsx(Field, { label: "Email", value: actor.email, copyValue: actor.email, mono: true })) : null, actor.uid ? (jsx(Field, { label: "UID", value: actor.uid, copyValue: actor.uid, mono: true })) : null] }), jsxs(Section, { title: "Resource", children: [jsx(Field, { label: "Kind", value: jsxs("span", { children: [resource.kind, resource.apiGroup ? (jsxs("span", { className: "ml-1 text-muted-foreground", children: ["\u00B7 ", resource.apiGroup] })) : null] }), copyValue: resource.kind, copyLabel: "resource kind" }), resource.name ? (jsx(Field, { label: "Name", value: resource.name, copyValue: resource.name, copyLabel: "resource name" })) : null, resource.namespace ? (jsx(Field, { label: "Namespace", value: resource.namespace, copyValue: resource.namespace })) : null, resource.uid ? (jsx(Field, { label: "UID", value: resource.uid, copyValue: resource.uid, mono: true })) : null] }), jsxs(Section, { title: "Origin", children: [jsx(Field, { label: "Source", value: origin.type, copyValue: origin.type, copyLabel: "origin type" }), origin.id ? (jsx(Field, { label: "ID", value: origin.id, copyValue: origin.id, mono: true })) : null] })] })] }));
10471
+ const body = (jsxs(Fragment, { children: [changes && changes.length > 0 ? (jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "m-0 mb-2 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground/80", children: "Changes" }), jsx("div", { className: "flex flex-col gap-1.5", children: changes.map((change, index) => (jsxs("div", { className: "rounded bg-muted px-2.5 py-1.5 text-xs", children: [jsx("span", { className: "block font-mono text-[11px] font-semibold text-foreground", children: change.field }), change.old ? (jsxs("span", { className: "block ml-2 text-destructive", children: [jsx("span", { className: "mr-1 font-medium", children: "\u2212" }), jsx("span", { className: "line-through", children: change.old })] })) : null, change.new ? (jsxs("span", { className: "block ml-2 text-[var(--success-500)]", children: [jsx("span", { className: "mr-1 font-medium", children: "+" }), change.new] })) : null] }, index))) })] })) : null, jsxs(DetailGrid, { children: [jsx(Section, { title: "When", children: jsx(Field, { label: "Timestamp", value: jsx(Timestamp, { value: timestamp, variant: "iso-utc" }), copyValue: timestamp ?? '', copyLabel: "timestamp", disableTooltip: true }) }), jsxs(Section, { title: "Actor", children: [jsx(Field, { label: "Name", value: jsxs("span", { children: [actorDisplay, actor.type ? (jsxs("span", { className: "ml-1 text-muted-foreground", children: ["(", actor.type, ")"] })) : null] }), copyValue: actorDisplay, copyLabel: "actor name" }), actorIsUser && actor.email ? (jsx(Field, { label: "Email", value: actor.email, copyValue: actor.email, mono: true })) : null, actor.uid ? (jsx(Field, { label: "UID", value: actor.uid, copyValue: actor.uid, mono: true })) : null] }), jsxs(Section, { title: "Resource", children: [jsx(Field, { label: "Kind", value: jsxs("span", { children: [resource.kind, resource.apiGroup ? (jsxs("span", { className: "ml-1 text-muted-foreground", children: ["\u00B7 ", resource.apiGroup] })) : null] }), copyValue: resource.kind, copyLabel: "resource kind" }), resource.name ? (jsx(Field, { label: "Name", value: resource.name, copyValue: resource.name, copyLabel: "resource name" })) : null, resource.namespace ? (jsx(Field, { label: "Namespace", value: resource.namespace, copyValue: resource.namespace })) : null, resource.uid ? (jsx(Field, { label: "UID", value: resource.uid, copyValue: resource.uid, mono: true })) : null] }), jsxs(Section, { title: "Origin", children: [jsx(Field, { label: "Source", value: origin.type, copyValue: origin.type, copyLabel: "origin type" }), origin.id ? (jsx(Field, { label: "ID", value: origin.id, copyValue: origin.id, mono: true })) : null] })] })] }));
10263
10472
  return (jsx(TooltipProvider, { children: compact ? (jsx(DetailPanelShell, { children: body })) : (jsx("div", { className: "mt-4 pt-4 border-t border-border", children: body })) }));
10264
10473
  }
10265
10474
 
10266
- // Number of columns rendered for the feed variant. Used as the colSpan on
10267
- // the expanded-detail row so it stretches across the full width.
10268
- const ACTIVITY_FEED_COLUMN_COUNT = 5;
10269
- /**
10270
- * Get avatar initials from actor name
10271
- */
10272
- function getActorInitials(name) {
10273
- const parts = name.split(/[@\s.]+/).filter(Boolean);
10274
- if (parts.length === 0)
10275
- return "?";
10276
- if (parts.length === 1)
10277
- return parts[0].charAt(0).toUpperCase();
10278
- return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
10279
- }
10280
- /**
10281
- * Get Tailwind classes for actor avatar based on actor type
10282
- */
10283
- function getActorAvatarClasses(actorType, compact) {
10284
- const baseClasses = cn("rounded-full flex items-center justify-center shrink-0 font-semibold", compact ? "w-5 h-5 text-1xs" : "w-6 h-6 text-1xs");
10285
- switch (actorType) {
10286
- case "user":
10287
- return cn(baseClasses, "bg-primary text-primary-foreground");
10288
- case "controller":
10289
- return cn(baseClasses, "bg-secondary text-secondary-foreground");
10290
- case "machine account":
10291
- return cn(baseClasses, "bg-muted text-muted-foreground");
10292
- default:
10293
- return cn(baseClasses, "bg-muted text-muted-foreground");
10294
- }
10295
- }
10296
10475
  /**
10297
10476
  * Extract verb from activity summary (e.g., "alice created HTTPProxy" -> "created")
10298
10477
  */
@@ -10301,24 +10480,24 @@ function extractVerb(summary) {
10301
10480
  if (words.length >= 2) {
10302
10481
  return words[1].toLowerCase();
10303
10482
  }
10304
- return "unknown";
10483
+ return 'unknown';
10305
10484
  }
10306
10485
  /**
10307
- * Normalize verb to a canonical form for coloring
10486
+ * Normalize a verb to a canonical form used for coloring/icons/grouping.
10308
10487
  */
10309
10488
  function normalizeVerb(verb) {
10310
10489
  const normalized = verb.toLowerCase();
10311
- if (normalized.includes("create") || normalized.includes("add"))
10312
- return "create";
10313
- if (normalized.includes("delete") || normalized.includes("remove"))
10314
- return "delete";
10315
- if (normalized.includes("update") ||
10316
- normalized.includes("patch") ||
10317
- normalized.includes("modify") ||
10318
- normalized.includes("change") ||
10319
- normalized.includes("edit"))
10320
- return "update";
10321
- return "other";
10490
+ if (normalized.includes('create') || normalized.includes('add'))
10491
+ return 'create';
10492
+ if (normalized.includes('delete') || normalized.includes('remove'))
10493
+ return 'delete';
10494
+ if (normalized.includes('update') ||
10495
+ normalized.includes('patch') ||
10496
+ normalized.includes('modify') ||
10497
+ normalized.includes('change') ||
10498
+ normalized.includes('edit'))
10499
+ return 'update';
10500
+ return 'other';
10322
10501
  }
10323
10502
  /**
10324
10503
  * Get icon container + icon color classes based on verb
@@ -10326,25 +10505,25 @@ function normalizeVerb(verb) {
10326
10505
  function getActionIconClasses(verb) {
10327
10506
  const normalizedVerb = normalizeVerb(verb);
10328
10507
  switch (normalizedVerb) {
10329
- case "create":
10508
+ case 'create':
10330
10509
  return {
10331
- container: "bg-blue-50 dark:bg-blue-950",
10332
- icon: "text-blue-500 dark:text-blue-400",
10510
+ container: 'bg-blue-50 dark:bg-blue-950',
10511
+ icon: 'text-blue-500 dark:text-blue-400',
10333
10512
  };
10334
- case "update":
10513
+ case 'update':
10335
10514
  return {
10336
- container: "bg-green-50 dark:bg-green-950",
10337
- icon: "text-green-600 dark:text-green-400",
10515
+ container: 'bg-green-50 dark:bg-green-950',
10516
+ icon: 'text-green-600 dark:text-green-400',
10338
10517
  };
10339
- case "delete":
10518
+ case 'delete':
10340
10519
  return {
10341
- container: "bg-red-50 dark:bg-red-950",
10342
- icon: "text-red-500 dark:text-red-400",
10520
+ container: 'bg-red-50 dark:bg-red-950',
10521
+ icon: 'text-red-500 dark:text-red-400',
10343
10522
  };
10344
10523
  default:
10345
10524
  return {
10346
- container: "bg-slate-100 dark:bg-slate-800",
10347
- icon: "text-slate-500 dark:text-slate-400",
10525
+ container: 'bg-slate-100 dark:bg-slate-800',
10526
+ icon: 'text-slate-500 dark:text-slate-400',
10348
10527
  };
10349
10528
  }
10350
10529
  }
@@ -10354,20 +10533,51 @@ function getActionIconClasses(verb) {
10354
10533
  function getTimelineIcon(verb) {
10355
10534
  const normalizedVerb = normalizeVerb(verb);
10356
10535
  switch (normalizedVerb) {
10357
- case "create":
10536
+ case 'create':
10358
10537
  return Plus;
10359
- case "update":
10538
+ case 'update':
10360
10539
  return Pencil;
10361
- case "delete":
10540
+ case 'delete':
10362
10541
  return Trash2;
10363
10542
  default:
10364
10543
  return Activity;
10365
10544
  }
10366
10545
  }
10546
+
10547
+ // Number of columns rendered for the feed variant. Used as the colSpan on
10548
+ // the expanded-detail row so it stretches across the full width.
10549
+ const ACTIVITY_FEED_COLUMN_COUNT = 5;
10550
+ /**
10551
+ * Get avatar initials from actor name
10552
+ */
10553
+ function getActorInitials(name) {
10554
+ const parts = name.split(/[@\s.]+/).filter(Boolean);
10555
+ if (parts.length === 0)
10556
+ return "?";
10557
+ if (parts.length === 1)
10558
+ return parts[0].charAt(0).toUpperCase();
10559
+ return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
10560
+ }
10561
+ /**
10562
+ * Get Tailwind classes for actor avatar based on actor type
10563
+ */
10564
+ function getActorAvatarClasses(actorType, compact) {
10565
+ const baseClasses = cn("rounded-full flex items-center justify-center shrink-0 font-semibold", compact ? "w-5 h-5 text-1xs" : "w-6 h-6 text-1xs");
10566
+ switch (actorType) {
10567
+ case "user":
10568
+ return cn(baseClasses, "bg-primary text-primary-foreground");
10569
+ case "controller":
10570
+ return cn(baseClasses, "bg-secondary text-secondary-foreground");
10571
+ case "machine account":
10572
+ return cn(baseClasses, "bg-muted text-muted-foreground");
10573
+ default:
10574
+ return cn(baseClasses, "bg-muted text-muted-foreground");
10575
+ }
10576
+ }
10367
10577
  /**
10368
10578
  * ActivityFeedItem renders a single activity in the feed or timeline
10369
10579
  */
10370
- function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isSelected = false, className = "", compact = false, isNew = false, variant = "feed", isLast = false, defaultExpanded = false, }) {
10580
+ function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isSelected = false, className = "", compact = false, isNew = false, variant = "feed", isLast = false, defaultExpanded = false, density = "default", }) {
10371
10581
  const [isExpanded, setIsExpanded] = useState(defaultExpanded);
10372
10582
  const { spec, metadata } = activity;
10373
10583
  const { actor, summary, links, tenant } = spec;
@@ -10388,7 +10598,7 @@ function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, ten
10388
10598
  if (isTimeline) {
10389
10599
  const { container: iconBg, icon: iconColor } = getActionIconClasses(verb);
10390
10600
  const Icon = getTimelineIcon(verb);
10391
- return (jsxs("div", { className: cn(!isLast && !isExpanded && "border-b border-border", className), children: [jsxs("div", { className: cn("flex items-center gap-3 py-3 cursor-pointer group", isSelected && "bg-muted/40"), onClick: toggleExpand, children: [jsx("div", { className: cn("w-8 h-8 rounded-md shrink-0 flex items-center justify-center", iconBg, iconColor), children: jsx(Icon, { size: 16, strokeWidth: 2 }) }), jsx("div", { className: "flex-1 min-w-0 text-sm text-foreground leading-snug", children: jsx(ActivityFeedSummary, { summary: summary, links: links, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, resourceLinkContext: { tenant } }) }), tenant && (jsx("div", { className: "shrink-0", children: tenantRenderer ? (tenantRenderer(tenant)) : (jsx(TenantBadge, { tenant: tenant, tenantLinkResolver: tenantLinkResolver, size: "compact" })) })), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: jsx(Timestamp, { value: timestamp }) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", className: "h-5 py-0 px-1 text-base text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity shrink-0", onClick: toggleExpand, "aria-expanded": isExpanded, children: isExpanded ? "−" : "+" })] }), isExpanded && (jsx(ActivityExpandedDetails, { activity: activity, tenantLinkResolver: tenantLinkResolver, compact: true }))] }));
10601
+ return (jsxs("div", { className: cn(!isLast && !isExpanded && "border-b border-border", className), children: [jsxs("div", { className: cn("flex items-center gap-3 cursor-pointer group", density === "compact" ? "py-1.5" : "py-3", isSelected && "bg-muted/40"), onClick: toggleExpand, children: [jsx("div", { className: cn("rounded-md shrink-0 flex items-center justify-center", density === "compact" ? "w-6 h-6" : "w-8 h-8", iconBg, iconColor), children: jsx(Icon, { size: 16, strokeWidth: 2 }) }), jsx("div", { className: "flex-1 min-w-0 text-sm text-foreground leading-snug", children: jsx(ActivityFeedSummary, { summary: summary, links: links, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, resourceLinkContext: { tenant } }) }), tenant && (jsx("div", { className: "shrink-0", children: tenantRenderer ? (tenantRenderer(tenant)) : (jsx(TenantBadge, { tenant: tenant, tenantLinkResolver: tenantLinkResolver, size: "compact" })) })), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: jsx(Timestamp, { value: timestamp }) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", className: "h-5 py-0 px-1 text-base text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity shrink-0", onClick: toggleExpand, "aria-expanded": isExpanded, children: isExpanded ? "−" : "+" })] }), isExpanded && (jsx(ActivityExpandedDetails, { activity: activity, tenantLinkResolver: tenantLinkResolver, compact: true }))] }));
10392
10602
  }
10393
10603
  // Feed variant — flat-column table row. Columns: Actor | Summary |
10394
10604
  // Tenant | When | expand toggle. Renders an additional TableRow with a
@@ -10402,7 +10612,7 @@ function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, ten
10402
10612
  // surfaced via a hover tooltip on the avatar so the column stays narrow.
10403
10613
  const actorTooltipBody = (jsxs("div", { className: "flex flex-col gap-0.5 text-xs", children: [jsx("span", { className: "font-medium", children: actorVisible }), actor.email && actor.email !== actorVisible ? (jsx("span", { className: "font-mono", children: actor.email })) : null, actor.uid ? (jsx("span", { className: "font-mono opacity-70", children: actor.uid })) : null] }));
10404
10614
  const actorCell = (jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: avatar }), jsx(TooltipContent, { children: actorTooltipBody })] }));
10405
- return (jsxs(Fragment, { children: [jsxs(TableRow, { "data-state": isSelected ? "selected" : undefined, className: cn("cursor-pointer", isNew && "bg-green-50/40 dark:bg-green-950/20", className), onClick: (e) => {
10615
+ return (jsxs(Fragment, { children: [jsxs(TableRow, { "data-state": isSelected ? "selected" : undefined, className: cn("cursor-pointer", isNew && "bg-[var(--success-100)]/60", className), onClick: (e) => {
10406
10616
  // Row-level click toggles expansion. Inner interactive elements
10407
10617
  // (links, avatar, expand button) call e.stopPropagation() so
10408
10618
  // they don't double-fire. If a parent passed onActivityClick,
@@ -10573,6 +10783,18 @@ function useFacets(client, timeRange, filters = {}) {
10573
10783
  };
10574
10784
  }
10575
10785
 
10786
+ /**
10787
+ * SegmentedToggle - a single-select control built on datum-ui's ButtonGroup.
10788
+ * The active segment is a solid primary Button; inactive segments sit on the
10789
+ * neutral (muted) button surface so the control reads in both themes.
10790
+ */
10791
+ function SegmentedToggle({ options, value, onChange, ariaLabel, className, disabled = false, }) {
10792
+ return (jsx(ButtonGroup, { "aria-label": ariaLabel, className: className, children: options.map((option) => {
10793
+ const active = value === option.value;
10794
+ return (jsx(Button, { htmlType: "button", size: "small", type: active ? 'primary' : 'quaternary', theme: active ? 'solid' : 'light', className: cn('font-medium', !active && 'border border-border'), onClick: () => onChange(option.value), disabled: disabled, "aria-pressed": active, title: option.description, children: option.label }, option.value));
10795
+ }) }));
10796
+ }
10797
+
10576
10798
  /**
10577
10799
  * Options for the change source toggle
10578
10800
  */
@@ -10596,17 +10818,8 @@ const OPTIONS$1 = [
10596
10818
  /**
10597
10819
  * ChangeSourceToggle provides a segmented control for filtering by change source
10598
10820
  */
10599
- function ChangeSourceToggle({ value, onChange, className = "", disabled = false, }) {
10600
- return (jsx("div", { className: cn("inline-flex border border-input rounded-md overflow-hidden", className), role: "group", "aria-label": "Filter by change source", children: OPTIONS$1.map((option, index) => {
10601
- const active = value === option.value;
10602
- return (jsx(Button, { htmlType: "button", type: active ? 'primary' : 'quaternary', theme: active ? 'solid' : 'borderless', className: cn('px-2 h-7 text-xs font-medium transition-all duration-200', index < OPTIONS$1.length - 1 && 'border-r border-input', !active && 'bg-muted text-foreground hover:bg-muted/80'),
10603
- // Inline styles win over datum-ui's baked-in `rounded-lg` and
10604
- // (for the primary/solid active button) its own `border`. The
10605
- // outer wrapper's rounded-md + overflow-hidden draws the
10606
- // segmented control's outer corners; per-button border-right
10607
- // (className) draws the separators between inactive buttons.
10608
- style: active ? { borderRadius: 0, border: 0 } : { borderRadius: 0 }, onClick: () => onChange(option.value), disabled: disabled, "aria-pressed": active, title: option.description, children: option.label }, option.value));
10609
- }) }));
10821
+ function ChangeSourceToggle({ value, onChange, className, disabled = false, }) {
10822
+ return (jsx(SegmentedToggle, { options: OPTIONS$1, value: value, onChange: onChange, ariaLabel: "Filter by change source", className: className, disabled: disabled }));
10610
10823
  }
10611
10824
 
10612
10825
  /**
@@ -10678,7 +10891,7 @@ const getSelectedPreset$1 = (timeRange) => {
10678
10891
  /**
10679
10892
  * ActivityFeedFilters provides filter controls for the activity feed
10680
10893
  */
10681
- function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTimeRangeChange, disabled = false, hiddenFilters = [], className = "", extraFilters, }) {
10894
+ function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTimeRangeChange, disabled = false, hiddenFilters = [], className = "", extraFilters, layout = "inline", }) {
10682
10895
  const { resourceKinds, actorNames, apiGroups, resourceNamespaces, error: facetsError, } = useFacets(client, timeRange, filters);
10683
10896
  // Log facets error for debugging
10684
10897
  if (facetsError) {
@@ -10898,12 +11111,186 @@ function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTi
10898
11111
  clearTimeout(searchDebounceRef.current);
10899
11112
  onFiltersChangeRef.current({ ...filtersRef.current, search: undefined });
10900
11113
  }, []);
10901
- return (jsx("div", { className: `border-b border-border py-4 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [!hiddenFilters.includes("changeSource") && (jsx(ChangeSourceToggle, { value: filters.changeSource || "all", onChange: handleChangeSourceChange, disabled: disabled })), jsxs("div", { className: "relative min-w-[200px] flex-1 max-w-xs", children: [jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), jsx(Input, { type: "text", placeholder: "Search activities...", value: searchInputValue, onChange: handleSearchChange, className: "pl-8 h-7 text-xs pr-6" }), searchInputValue && (jsx("button", { onClick: handleSearchClear, className: "absolute right-1.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors", "aria-label": "Clear search", children: jsx(X, { className: "h-3.5 w-3.5" }) }))] }), activeFilterIds.map((filterId) => {
11114
+ const searchInput = (jsxs("div", { className: "relative min-w-[200px] flex-1 max-w-xs", children: [jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), jsx(Input, { type: "text", placeholder: "Search activities...", value: searchInputValue, onChange: handleSearchChange, className: "pl-8 h-7 text-xs pr-6" }), searchInputValue && (jsx("button", { onClick: handleSearchClear, className: "absolute right-1.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors", "aria-label": "Clear search", children: jsx(X, { className: "h-3.5 w-3.5" }) }))] }));
11115
+ const changeSourceControl = !hiddenFilters.includes("changeSource") && (jsx(ChangeSourceToggle, { value: filters.changeSource || "all", onChange: handleChangeSourceChange, disabled: disabled }));
11116
+ const filterChips = activeFilterIds.map((filterId) => {
11117
+ const config = FILTER_CONFIGS$1[filterId];
11118
+ return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
11119
+ ? getFilterOptions(filterId)
11120
+ : undefined, onValuesChange: (values) => handleFilterChange(filterId, values), onClear: () => handleFilterClear(filterId), onPopoverClose: () => handlePopoverClose(filterId), inputMode: config.inputMode, placeholder: config.placeholder, searchPlaceholder: config.searchPlaceholder, autoOpen: pendingFilter === filterId, disabled: disabled }, filterId));
11121
+ });
11122
+ const addFilterControl = (jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }));
11123
+ const timeRangeControl = (jsx(TimeRangeDropdown, { presets: TIME_PRESETS$1, selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() }));
11124
+ if (layout === "compact") {
11125
+ const activeFilterCount = activeFilterIds.length +
11126
+ (!hiddenFilters.includes("changeSource") &&
11127
+ filters.changeSource &&
11128
+ filters.changeSource !== "all"
11129
+ ? 1
11130
+ : 0);
11131
+ return (jsx("div", { className: `border-b border-border py-4 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [searchInput, jsxs(Popover, { children: [jsx(PopoverTrigger, { asChild: true, children: jsxs("button", { type: "button", disabled: disabled, className: cn("flex h-7 items-center gap-1.5 rounded-md border border-border bg-background px-2 text-xs", "text-muted-foreground hover:text-foreground hover:border-foreground/50 transition-colors", "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", "disabled:cursor-not-allowed disabled:opacity-50"), children: [jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }), jsx("span", { className: "font-medium", children: "Filters" }), activeFilterCount > 0 && (jsx("span", { className: "ml-0.5 rounded-full bg-primary text-primary-foreground text-[10px] font-semibold leading-none px-1.5 py-0.5", children: activeFilterCount }))] }) }), jsx(PopoverContent, { className: "w-[320px] p-3", sideOffset: 4, align: "start", children: jsxs("div", { className: "flex flex-col gap-3", children: [changeSourceControl, jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [filterChips, addFilterControl] }), extraFilters, timeRangeControl] }) })] })] }) }));
11132
+ }
11133
+ return (jsx("div", { className: `border-b border-border py-4 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [!hiddenFilters.includes("changeSource") && (jsx(ChangeSourceToggle, { value: filters.changeSource || "all", onChange: handleChangeSourceChange, disabled: disabled })), jsxs("div", { className: "relative min-w-[200px] flex-1 max-w-xs", children: [jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), jsx(Input, { type: "text", placeholder: "Search activities...", value: searchInputValue, onChange: handleSearchChange, className: "h-9 pl-9 pr-9 text-xs md:text-xs" }), searchInputValue && (jsx("button", { onClick: handleSearchClear, className: "absolute right-0 top-0 flex h-9 w-9 items-center justify-center rounded-r-lg text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "aria-label": "Clear search", children: jsx(X, { className: "h-3.5 w-3.5" }) }))] }), activeFilterIds.map((filterId) => {
10902
11134
  const config = FILTER_CONFIGS$1[filterId];
10903
11135
  return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
10904
11136
  ? getFilterOptions(filterId)
10905
11137
  : undefined, onValuesChange: (values) => handleFilterChange(filterId, values), onClear: () => handleFilterClear(filterId), onPopoverClose: () => handlePopoverClose(filterId), inputMode: config.inputMode, placeholder: config.placeholder, searchPlaceholder: config.searchPlaceholder, autoOpen: pendingFilter === filterId, disabled: disabled }, filterId));
10906
- }), jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }), extraFilters, jsx("div", { className: "flex-1 min-w-[20px]" }), jsx(TimeRangeDropdown, { presets: TIME_PRESETS$1, selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() })] }) }));
11138
+ }), jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }), extraFilters, jsx("div", { className: "flex-1 min-w-[20px]" }), timeRangeControl] }) }));
11139
+ }
11140
+
11141
+ const KIND_PLURALS = {
11142
+ DNSRecordSet: "DNS records",
11143
+ };
11144
+ function pluralizeKind(kind, count) {
11145
+ if (count === 1)
11146
+ return kind;
11147
+ return KIND_PLURALS[kind] ?? `${kind}s`;
11148
+ }
11149
+ const VERB_LABEL = {
11150
+ create: "created",
11151
+ update: "updated",
11152
+ delete: "deleted",
11153
+ other: "changed",
11154
+ };
11155
+ /**
11156
+ * ActivityFeedGroupItem renders a run of consecutive same verb/kind/actor
11157
+ * activities (see `groupActivities`) as a single collapsed row in the
11158
+ * "digest" timeline. A group of size 1 renders exactly like a normal
11159
+ * `ActivityFeedItem` — no visual change for ungrouped activities. Larger
11160
+ * groups render a synthesized summary ("{actor} created 12 DNS records")
11161
+ * that expands to reveal the individual activities underneath.
11162
+ */
11163
+ function ActivityFeedGroupItem({ group, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isLast = false, }) {
11164
+ const [isExpanded, setIsExpanded] = useState(false);
11165
+ if (group.count === 1) {
11166
+ return (jsx(ActivityFeedItem, { activity: group.activities[0], resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: onActorClick, onActivityClick: onActivityClick, variant: "timeline", density: "compact", isLast: isLast }));
11167
+ }
11168
+ const { container: iconBg, icon: iconColor } = getActionIconClasses(group.verb);
11169
+ const Icon = getTimelineIcon(group.verb);
11170
+ const actorVisible = group.actorDisplayName || group.actorName;
11171
+ const summary = `${actorVisible} ${VERB_LABEL[group.verb]} ${group.count} ${pluralizeKind(group.kind, group.count)}`;
11172
+ const toggleExpand = (e) => {
11173
+ e.stopPropagation();
11174
+ setIsExpanded((prev) => !prev);
11175
+ };
11176
+ return (jsxs("div", { className: cn(!isLast && !isExpanded && "border-b border-border"), children: [jsxs("div", { className: "flex items-center gap-3 py-1.5 cursor-pointer group", onClick: toggleExpand, children: [jsxs("div", { className: cn("w-6 h-6 rounded-md shrink-0 flex items-center justify-center relative", iconBg, iconColor), children: [jsx(Icon, { size: 14, strokeWidth: 2 }), jsx("span", { className: "absolute -bottom-1 -right-1 rounded-full bg-background border border-border px-1 text-[10px] font-semibold leading-tight text-foreground", children: group.count })] }), jsx("div", { className: "flex-1 min-w-0 text-xs text-foreground leading-normal", children: summary }), group.tenant && (jsx("div", { className: "shrink-0", children: tenantRenderer ? (tenantRenderer(group.tenant)) : (jsx(TenantBadge, { tenant: group.tenant, tenantLinkResolver: tenantLinkResolver, size: "compact" })) })), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: jsx(Timestamp, { value: group.latestTimestamp }) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", className: "h-5 py-0 px-1 text-base text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity shrink-0", onClick: toggleExpand, "aria-expanded": isExpanded, children: isExpanded ? "−" : "+" })] }), isExpanded && (jsx("div", { className: "pl-11 flex flex-col", children: group.activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: onActorClick, onActivityClick: onActivityClick, compact: true, density: "compact", variant: "timeline", isLast: index === group.activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name))) }))] }));
11177
+ }
11178
+
11179
+ /**
11180
+ * Thin stat strip summarizing the activities currently loaded in the feed —
11181
+ * e.g. "18 changes · 3 people · 1 system". Computed client-side over the
11182
+ * loaded/paginated activities; it is a quick-glance headline, not a
11183
+ * full-range aggregate (that would require a backend facet query).
11184
+ */
11185
+ function ActivityDigestSummary({ activities, className = "" }) {
11186
+ const stats = useMemo(() => {
11187
+ const humanActors = new Set();
11188
+ const systemActors = new Set();
11189
+ for (const activity of activities) {
11190
+ const { actor, changeSource } = activity.spec;
11191
+ if (changeSource === "human") {
11192
+ humanActors.add(actor.name);
11193
+ }
11194
+ else {
11195
+ systemActors.add(actor.name);
11196
+ }
11197
+ }
11198
+ return {
11199
+ total: activities.length,
11200
+ people: humanActors.size,
11201
+ systems: systemActors.size,
11202
+ };
11203
+ }, [activities]);
11204
+ if (stats.total === 0)
11205
+ return null;
11206
+ const parts = [`${stats.total} ${stats.total === 1 ? "change" : "changes"}`];
11207
+ if (stats.people > 0)
11208
+ parts.push(`${stats.people} ${stats.people === 1 ? "person" : "people"}`);
11209
+ if (stats.systems > 0)
11210
+ parts.push(`${stats.systems} ${stats.systems === 1 ? "system" : "systems"}`);
11211
+ return (jsx("div", { className: `px-1 pt-4 pb-2 text-xs text-muted-foreground ${className}`, children: parts.join(" · ") }));
11212
+ }
11213
+
11214
+ function tenantKey(tenant) {
11215
+ if (!tenant)
11216
+ return '';
11217
+ return `${tenant.type}:${tenant.name}`;
11218
+ }
11219
+ function groupSignature(activity) {
11220
+ const { spec } = activity;
11221
+ const verb = normalizeVerb(extractVerb(spec.summary));
11222
+ return [verb, spec.resource.kind, spec.actor.name, tenantKey(spec.tenant)].join('|');
11223
+ }
11224
+ /**
11225
+ * Group consecutive activities that share verb + resource kind + actor +
11226
+ * tenant. Activities are assumed to already be sorted newest-first (as
11227
+ * returned by `useActivityFeed`); only adjacent items are merged, so a
11228
+ * burst of matching activities collapses into one group without needing
11229
+ * any time-window heuristics.
11230
+ */
11231
+ function groupActivities(activities) {
11232
+ const groups = [];
11233
+ for (const activity of activities) {
11234
+ const signature = groupSignature(activity);
11235
+ const last = groups[groups.length - 1];
11236
+ if (last && last.key === signature) {
11237
+ last.activities.push(activity);
11238
+ last.count += 1;
11239
+ continue;
11240
+ }
11241
+ const { spec } = activity;
11242
+ groups.push({
11243
+ key: signature,
11244
+ verb: normalizeVerb(extractVerb(spec.summary)),
11245
+ kind: spec.resource.kind,
11246
+ actorName: spec.actor.name,
11247
+ actorDisplayName: spec.actor.displayName,
11248
+ tenant: spec.tenant,
11249
+ activities: [activity],
11250
+ count: 1,
11251
+ latestTimestamp: activity.metadata?.creationTimestamp,
11252
+ });
11253
+ }
11254
+ return groups;
11255
+ }
11256
+ function dayLabel(timestamp) {
11257
+ if (!timestamp)
11258
+ return 'Unknown date';
11259
+ const date = new Date(timestamp);
11260
+ if (Number.isNaN(date.getTime()))
11261
+ return 'Unknown date';
11262
+ if (isToday(date))
11263
+ return 'Today';
11264
+ if (isYesterday(date))
11265
+ return 'Yesterday';
11266
+ return format(date, 'MMMM d, yyyy');
11267
+ }
11268
+ /**
11269
+ * Bucket activities into calendar-day sections (Today / Yesterday / a
11270
+ * formatted date), then group consecutive matching activities within each
11271
+ * day. Bucketing by day first (rather than grouping across the whole list)
11272
+ * keeps a group from silently spanning a day boundary.
11273
+ */
11274
+ function groupActivitiesByDay(activities) {
11275
+ const sections = [];
11276
+ let currentLabel = null;
11277
+ let currentBucket = [];
11278
+ const flush = () => {
11279
+ if (currentLabel !== null && currentBucket.length > 0) {
11280
+ sections.push({ label: currentLabel, groups: groupActivities(currentBucket) });
11281
+ }
11282
+ };
11283
+ for (const activity of activities) {
11284
+ const label = dayLabel(activity.metadata?.creationTimestamp);
11285
+ if (label !== currentLabel) {
11286
+ flush();
11287
+ currentLabel = label;
11288
+ currentBucket = [];
11289
+ }
11290
+ currentBucket.push(activity);
11291
+ }
11292
+ flush();
11293
+ return sections;
10907
11294
  }
10908
11295
 
10909
11296
  /**
@@ -10973,26 +11360,28 @@ function ActivityFeed({ client, initialFilters = { changeSource: "human" }, init
10973
11360
  useEffect(() => {
10974
11361
  isLoadingRef.current = isLoading;
10975
11362
  }, [isLoading]);
10976
- // Infinite scroll using Intersection Observer
11363
+ // Infinite scroll using Intersection Observer.
11364
+ // The list container is the root only when it actually scrolls. Host apps
11365
+ // such as cloud-portal scroll at the page level, so the container never
11366
+ // overflows; in that case observe against the viewport instead.
10977
11367
  useEffect(() => {
10978
11368
  if (!infiniteScroll || !hasMore || !loadMoreTriggerRef.current)
10979
11369
  return;
11370
+ const container = scrollContainerRef.current;
11371
+ const containerScrolls = !!container && container.scrollHeight > container.clientHeight + 1;
10980
11372
  const observer = new IntersectionObserver((entries) => {
10981
11373
  const entry = entries[0];
10982
11374
  if (entry.isIntersecting && !isLoadingRef.current) {
10983
- // Call through the ref to always use the latest function
10984
11375
  loadMoreRef.current();
10985
11376
  }
10986
11377
  }, {
10987
- root: scrollContainerRef.current,
11378
+ root: containerScrolls ? container : null,
10988
11379
  rootMargin: `${loadMoreThreshold}px`,
10989
11380
  threshold: 0,
10990
11381
  });
10991
11382
  observer.observe(loadMoreTriggerRef.current);
10992
- return () => {
10993
- observer.disconnect();
10994
- };
10995
- }, [infiniteScroll, loadMoreThreshold, hasMore]);
11383
+ return () => observer.disconnect();
11384
+ }, [infiniteScroll, loadMoreThreshold, hasMore, activities.length]);
10996
11385
  // Handle filter changes - refresh is automatic via the hook
10997
11386
  const handleFiltersChange = useCallback((newFilters) => {
10998
11387
  setFilters(newFilters);
@@ -11032,10 +11421,10 @@ function ActivityFeed({ client, initialFilters = { changeSource: "human" }, init
11032
11421
  // Parent containers must have proper height constraints (h-screen/h-full + overflow-hidden)
11033
11422
  const effectiveMaxHeight = maxHeight === "none" ? undefined : maxHeight;
11034
11423
  const listClasses = "flex-1 min-h-0 overflow-y-auto flex flex-col";
11035
- return (jsx(TooltipProvider, { delayDuration: 200, children: jsxs(Card, { className: containerClasses, children: [enableStreaming && (jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center", children: [isStreaming && !watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsxs("span", { className: "relative flex h-2 w-2", children: [jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 dark:bg-green-500 opacity-75" }), jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-green-500 dark:bg-green-400" })] }), jsx("span", { className: "text-xs text-muted-foreground", children: "Streaming activity..." })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "New activities will appear automatically" }) })] }) })), watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "relative flex h-2 w-2", children: jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-red-500 dark:bg-red-400" }) }), jsx("span", { className: "text-xs text-destructive", children: "Connection error" })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "Stream connection lost" }) })] }) })), newActivitiesCount > 0 && !watchError && (jsxs(Badge, { variant: "secondary", className: "text-xs", children: ["+", newActivitiesCount, " new"] }))] }), jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: handleStreamingToggle, children: watchError ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", strokeWidth: "2", children: [jsx("path", { d: "M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" }), jsx("circle", { cx: "12", cy: "12", r: "3" })] }), "Retry"] })) : isStreaming ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsx("rect", { x: "6", y: "4", width: "4", height: "16" }), jsx("rect", { x: "14", y: "4", width: "4", height: "16" })] }), "Pause"] })) : (jsxs(Fragment, { children: [jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("polygon", { points: "5,3 19,12 5,21", fill: "currentColor" }) }), "Resume"] })) })] })), showFilters && (jsx(ActivityFeedFilters, { client: client, filters: filters, timeRange: timeRange, onFiltersChange: handleFiltersChange, onTimeRangeChange: handleTimeRangeChange, disabled: isLoading, hiddenFilters: hiddenFilters })), jsx(ApiErrorAlert, { error: error, onRetry: refresh, className: "mb-4", errorFormatter: errorFormatter }), jsx(ApiErrorAlert, { error: watchError, onRetry: startStreaming, className: "mb-4", errorFormatter: errorFormatter }), !policiesLoading && hasPolicies === false && (jsxs("div", { className: "flex flex-col items-center py-12 px-8 text-center bg-muted border border-dashed border-border rounded-xl mb-4", children: [jsx("div", { className: "flex justify-center mb-4 text-muted-foreground", children: jsxs("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2" }), jsx("rect", { x: "9", y: "3", width: "6", height: "4", rx: "1" }), jsx("path", { d: "M9 12h6" }), jsx("path", { d: "M9 16h6" })] }) }), jsx("h3", { className: "m-0 mb-2 text-lg font-semibold text-foreground leading-snug", children: "Get started with activity logging" }), jsx("p", { className: "m-0 mb-6 text-sm leading-relaxed text-muted-foreground max-w-[400px]", children: "Activity policies define which resources to track and how to summarize changes. Create your first policy to start seeing activity logs here." }), onCreatePolicy && (jsx(Button, { onClick: onCreatePolicy, children: "Create Policy" }))] })), jsxs("div", { className: listClasses, ref: scrollContainerRef, style: effectiveMaxHeight ? { maxHeight: effectiveMaxHeight } : undefined, children: [!isLoading &&
11424
+ return (jsx(TooltipProvider, { delayDuration: 200, children: jsxs(Card, { className: containerClasses, children: [enableStreaming && (jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center", children: [isStreaming && !watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsxs("span", { className: "relative flex h-2 w-2", children: [jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--success-500)] opacity-75" }), jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-[var(--success-500)]" })] }), jsx("span", { className: "text-xs text-muted-foreground", children: "Streaming activity..." })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "New activities will appear automatically" }) })] }) })), watchError && (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "relative flex h-2 w-2", children: jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-destructive" }) }), jsx("span", { className: "text-xs text-destructive", children: "Connection error" })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "Stream connection lost" }) })] }) })), newActivitiesCount > 0 && !watchError && (jsxs(Badge, { variant: "secondary", className: "text-xs", children: ["+", newActivitiesCount, " new"] }))] }), jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: handleStreamingToggle, children: watchError ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", strokeWidth: "2", children: [jsx("path", { d: "M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" }), jsx("circle", { cx: "12", cy: "12", r: "3" })] }), "Retry"] })) : isStreaming ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsx("rect", { x: "6", y: "4", width: "4", height: "16" }), jsx("rect", { x: "14", y: "4", width: "4", height: "16" })] }), "Pause"] })) : (jsxs(Fragment, { children: [jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("polygon", { points: "5,3 19,12 5,21", fill: "currentColor" }) }), "Resume"] })) })] })), showFilters && (jsx(ActivityFeedFilters, { client: client, filters: filters, timeRange: timeRange, onFiltersChange: handleFiltersChange, onTimeRangeChange: handleTimeRangeChange, disabled: isLoading, hiddenFilters: hiddenFilters, layout: variant === "digest" ? "compact" : "inline" })), variant === "digest" && !isLoading && activities.length > 0 && (jsx(ActivityDigestSummary, { activities: activities })), jsx(ApiErrorAlert, { error: error, onRetry: refresh, className: "mb-4", errorFormatter: errorFormatter }), jsx(ApiErrorAlert, { error: watchError, onRetry: startStreaming, className: "mb-4", errorFormatter: errorFormatter }), !policiesLoading && hasPolicies === false && (jsxs("div", { className: "flex flex-col items-center py-12 px-8 text-center bg-muted border border-dashed border-border rounded-xl mb-4", children: [jsx("div", { className: "flex justify-center mb-4 text-muted-foreground", children: jsxs("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2" }), jsx("rect", { x: "9", y: "3", width: "6", height: "4", rx: "1" }), jsx("path", { d: "M9 12h6" }), jsx("path", { d: "M9 16h6" })] }) }), jsx("h3", { className: "m-0 mb-2 text-lg font-semibold text-foreground leading-snug", children: "Get started with activity logging" }), jsx("p", { className: "m-0 mb-6 text-sm leading-relaxed text-muted-foreground max-w-[400px]", children: "Activity policies define which resources to track and how to summarize changes. Create your first policy to start seeing activity logs here." }), onCreatePolicy && (jsx(Button, { onClick: onCreatePolicy, children: "Create Policy" }))] })), jsxs("div", { className: listClasses, ref: scrollContainerRef, style: effectiveMaxHeight ? { maxHeight: effectiveMaxHeight } : undefined, children: [!isLoading &&
11036
11425
  activities.length === 0 &&
11037
11426
  hasPolicies !== false &&
11038
- variant === "timeline" && (jsxs("div", { className: "py-12 text-center text-muted-foreground", children: [jsx("p", { className: "m-0", children: "No activities found" }), jsx("p", { className: "text-sm text-muted-foreground mt-2 m-0", children: "Try adjusting your filters or time range" })] })), variant === "timeline" ? (jsxs(Fragment, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsx(ActivityFeedItemSkeleton, { compact: compact }, index))) })), activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, compact: compact, isNew: enableStreaming && index < newActivitiesCount, variant: variant, isLast: index === activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name)))] })) : (jsxs(Table, { className: "w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: "w-12", "aria-label": "Actor" }), jsx(TableHead, { children: "Summary" }), jsx(TableHead, { children: "Tenant" }), jsx(TableHead, { className: "w-[170px]", children: "When" }), jsx(TableHead, { className: "w-10", "aria-label": "Expand" })] }) }), jsxs(TableBody, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsxs(TableRow, { children: [jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-3/4" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-5 w-32 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-24" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6" }) })] }, `sk-${index}`))) })), !isLoading &&
11427
+ (variant === "timeline" || variant === "digest") && (jsxs("div", { className: "py-12 text-center text-muted-foreground", children: [jsx("p", { className: "m-0", children: "No activities found" }), jsx("p", { className: "text-sm text-muted-foreground mt-2 m-0", children: "Try adjusting your filters or time range" })] })), variant === "digest" ? (jsxs(Fragment, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsx(ActivityFeedItemSkeleton, { compact: compact }, index))) })), groupActivitiesByDay(activities).map((section) => (jsxs("div", { className: "flex flex-col", children: [jsx("div", { className: "sticky top-0 z-10 bg-card pt-2 pb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: section.label }), section.groups.map((group, index) => (jsx(ActivityFeedGroupItem, { group: group, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, isLast: index === section.groups.length - 1 }, `${section.label}-${group.key}-${index}`)))] }, section.label)))] })) : variant === "timeline" ? (jsxs(Fragment, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsx(ActivityFeedItemSkeleton, { compact: compact }, index))) })), activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, compact: compact, isNew: enableStreaming && index < newActivitiesCount, variant: variant, isLast: index === activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name)))] })) : (jsxs(Table, { className: "w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: "w-12", "aria-label": "Actor" }), jsx(TableHead, { children: "Summary" }), jsx(TableHead, { children: "Tenant" }), jsx(TableHead, { className: "w-[170px]", children: "When" }), jsx(TableHead, { className: "w-10", "aria-label": "Expand" })] }) }), jsxs(TableBody, { children: [isLoading && activities.length === 0 && (jsx(Fragment, { children: Array.from({ length: 8 }).map((_, index) => (jsxs(TableRow, { children: [jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-3/4" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-5 w-32 rounded-full" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-24" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6" }) })] }, `sk-${index}`))) })), !isLoading &&
11039
11428
  activities.length === 0 &&
11040
11429
  hasPolicies !== false && (jsx(TableRow, { children: jsxs(TableCell, { colSpan: ACTIVITY_FEED_COLUMN_COUNT, className: "text-center py-12 text-muted-foreground", children: [jsx("div", { children: "No activities found" }), jsx("div", { className: "text-sm mt-2", children: "Try adjusting your filters or time range" })] }) })), activities.map((activity, index) => (jsx(ActivityFeedItem, { activity: activity, onResourceClick: onResourceClick, resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: handleActorClick, onActivityClick: onActivityClick, compact: compact, isNew: enableStreaming && index < newActivitiesCount, variant: variant, isLast: index === activities.length - 1 }, activity.metadata?.uid || activity.metadata?.name)))] })] })), infiniteScroll && hasMore && (jsx("div", { ref: loadMoreTriggerRef, className: "h-px mt-4" })), !infiniteScroll && activities.length > 0 ? (jsxs("div", { className: "flex items-center justify-between gap-4 px-4 py-3 border-t border-border text-sm text-muted-foreground", children: [jsxs("span", { children: [activities.length, " ", activities.length === 1 ? "activity" : "activities", hasMore ? " so far" : ""] }), hasMore ? (jsx(Button, { type: "tertiary", theme: "outline", size: "small", htmlType: "button", onClick: handleLoadMoreClick, disabled: isLoading, children: isLoading ? "Loading…" : "Load more" })) : (jsx("span", { children: "End of results" }))] })) : null] })] }) }));
11041
11430
  }
@@ -11686,7 +12075,7 @@ function useEventsFeed({ client, initialFilters = {}, initialTimeRange = { start
11686
12075
  function getRegarding$1(event) {
11687
12076
  return event.regarding || event.involvedObject || {};
11688
12077
  }
11689
- function getReportingController$1(event) {
12078
+ function getReportingController(event) {
11690
12079
  return event.reportingController || event.reportingComponent || event.source?.component;
11691
12080
  }
11692
12081
  function getReportingInstance(event) {
@@ -11699,7 +12088,7 @@ function getReportingInstance(event) {
11699
12088
  */
11700
12089
  function EventExpandedDetails({ event, compact = true }) {
11701
12090
  const regarding = getRegarding$1(event);
11702
- const reportingController = getReportingController$1(event);
12091
+ const reportingController = getReportingController(event);
11703
12092
  const reportingInstance = getReportingInstance(event);
11704
12093
  const { eventTime, action, metadata, related } = event;
11705
12094
  const firstTimestamp = event.firstTimestamp || event.deprecatedFirstTimestamp;
@@ -11726,12 +12115,6 @@ function getRegarding(event) {
11726
12115
  function getNote(event) {
11727
12116
  return event.note || event.message;
11728
12117
  }
11729
- /**
11730
- * Get the reporting controller (handling both new and deprecated field names)
11731
- */
11732
- function getReportingController(event) {
11733
- return event.reportingController || event.source?.component;
11734
- }
11735
12118
  /**
11736
12119
  * Get the event count (handling both new and deprecated field names)
11737
12120
  */
@@ -11767,7 +12150,6 @@ function EventFeedItem({ event, onEventClick, onResourceClick, isSelected = fals
11767
12150
  const note = getNote(event);
11768
12151
  const count = getCount(event);
11769
12152
  const timestamp = getTimestamp(event);
11770
- getReportingController(event);
11771
12153
  const { type, reason } = event;
11772
12154
  const handleClick = () => {
11773
12155
  onEventClick?.(event);
@@ -11810,11 +12192,11 @@ function EventFeedItem({ event, onEventClick, onResourceClick, isSelected = fals
11810
12192
  if (variant === 'timeline') {
11811
12193
  const TypeIcon = isWarning ? TriangleAlert : Bell;
11812
12194
  const iconBg = isWarning
11813
- ? 'bg-red-50 dark:bg-red-950'
11814
- : 'bg-blue-50 dark:bg-blue-950';
12195
+ ? 'bg-destructive/10'
12196
+ : 'bg-[var(--info-100)]';
11815
12197
  const iconColor = isWarning
11816
- ? 'text-red-500 dark:text-red-400'
11817
- : 'text-blue-500 dark:text-blue-400';
12198
+ ? 'text-destructive'
12199
+ : 'text-[var(--info-500)]';
11818
12200
  const objectLabel = regarding.namespace
11819
12201
  ? `${regarding.kind || 'Unknown'} · ${regarding.namespace}/${regarding.name || ''}`
11820
12202
  : `${regarding.kind || 'Unknown'} · ${regarding.name || ''}`;
@@ -11827,18 +12209,16 @@ function EventFeedItem({ event, onEventClick, onResourceClick, isSelected = fals
11827
12209
  cursor: 'help',
11828
12210
  }, children: summary }) }), jsx(TooltipContent, { className: "max-w-[480px] whitespace-normal break-words", children: summary })] })] }), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0 max-w-[200px] truncate", title: objectLabel, children: regarding.name }), jsx("span", { className: "text-xs text-muted-foreground whitespace-nowrap shrink-0", children: jsx(Timestamp, { value: timestamp }) }), jsx(Button, { type: "quaternary", theme: "borderless", size: "small", htmlType: "button", className: "h-5 py-0 px-1 text-muted-foreground hover:text-foreground shrink-0", onClick: toggleExpand, "aria-expanded": isExpanded, children: isExpanded ? jsx(ChevronDown, { className: "h-4 w-4" }) : jsx(ChevronRight, { className: "h-4 w-4" }) })] }), isExpanded ? jsx(EventExpandedDetails, { event: event }) : null] }));
11829
12211
  }
11830
- return (jsxs(Fragment, { children: [jsxs(TableRow, { "data-state": isSelected ? 'selected' : undefined, className: cn('cursor-pointer', isNew && 'bg-green-50/40 dark:bg-green-950/20', isWarning && !isSelected && 'border-l-2 border-l-red-400', className), onClick: (e) => {
12212
+ return (jsxs(Fragment, { children: [jsxs(TableRow, { "data-state": isSelected ? 'selected' : undefined, className: cn('cursor-pointer', isNew && 'bg-[var(--success-100)]/60', isWarning && !isSelected && 'border-l-2 border-l-destructive', className), onClick: (e) => {
11831
12213
  toggleExpand(e);
11832
12214
  handleClick();
11833
- }, "aria-expanded": isExpanded, children: [jsx(TableCell, { className: "py-2 align-middle whitespace-nowrap", children: jsx("span", { className: cn('inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium', isWarning
11834
- ? 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300'
11835
- : 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300'), children: type || 'Unknown' }) }), jsx(TableCell, { className: "py-2 align-middle whitespace-nowrap text-sm font-medium", children: reason || '' }), jsx(TableCell, { className: "py-2 align-middle", style: { width: '100%', maxWidth: 0, overflow: 'hidden' }, children: note ? (jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("div", { className: "text-sm text-muted-foreground leading-snug", style: {
12215
+ }, "aria-expanded": isExpanded, children: [jsx(TableCell, { className: "py-2 align-middle whitespace-nowrap", children: jsx(Badge$1, { type: isWarning ? 'danger' : 'info', theme: "light", className: "px-1.5 py-0.5 text-xs font-medium", children: type || 'Unknown' }) }), jsx(TableCell, { className: "py-2 align-middle whitespace-nowrap text-sm font-medium", children: reason || '' }), jsx(TableCell, { className: "py-2 align-middle", style: { width: '100%', maxWidth: 0, overflow: 'hidden' }, children: note ? (jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("div", { className: "text-sm text-muted-foreground leading-snug", style: {
11836
12216
  overflow: 'hidden',
11837
12217
  textOverflow: 'ellipsis',
11838
12218
  whiteSpace: 'nowrap',
11839
12219
  }, children: noteWithCount }) }), jsx(TooltipContent, { className: "max-w-[480px] whitespace-normal break-words", children: noteWithCount })] })) : null }), jsx(TableCell, { className: "py-2 align-middle", children: jsxs("div", { className: "flex items-center gap-1 min-w-0", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { className: cn('text-sm font-medium text-foreground truncate', onResourceClick && 'cursor-pointer hover:underline hover:text-primary'), onClick: onResourceClick ? handleResourceClick : undefined, style: { maxWidth: 200 }, children: regarding.name || 'Unknown' }) }), jsx(TooltipContent, { children: regarding.namespace
11840
12220
  ? `${regarding.kind || 'Unknown'} · ${regarding.namespace}/${regarding.name}`
11841
- : `${regarding.kind || 'Unknown'} · ${regarding.name}` })] }), jsx("button", { onClick: handleCopyResourceName, className: "inline-flex items-center justify-center p-0.5 rounded opacity-0 group-hover:opacity-100 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer", "aria-label": "Copy resource name", type: "button", children: isCopied ? (jsx(Check, { className: "h-3 w-3 text-green-600 dark:text-green-400" })) : (jsx(Copy, { className: "h-3 w-3 text-gray-500 dark:text-gray-400" })) })] }) }), jsx(TableCell, { className: "py-2 align-middle whitespace-nowrap text-sm text-muted-foreground", children: jsx(Timestamp, { value: timestamp }) }), jsx(TableCell, { className: "py-2 align-middle w-10", children: jsx(Button, { type: "quaternary", theme: "borderless", size: "small", htmlType: "button", className: "h-6 w-6 p-0 text-muted-foreground hover:text-foreground", onClick: toggleExpand, "aria-expanded": isExpanded, "aria-label": isExpanded ? 'Collapse details' : 'Expand details', children: isExpanded ? (jsx(ChevronDown, { className: "h-4 w-4" })) : (jsx(ChevronRight, { className: "h-4 w-4" })) }) })] }), isExpanded ? (jsx(TableRow, { className: "bg-muted/30 hover:bg-muted/30", children: jsx(TableCell, { colSpan: EVENT_COLUMN_COUNT, className: "p-0", children: jsx(EventExpandedDetails, { event: event }) }) })) : null] }));
12221
+ : `${regarding.kind || 'Unknown'} · ${regarding.name}` })] }), jsx("button", { onClick: handleCopyResourceName, className: "inline-flex items-center justify-center p-0.5 rounded opacity-0 group-hover:opacity-100 hover:bg-muted cursor-pointer", "aria-label": "Copy resource name", type: "button", children: isCopied ? (jsx(Check, { className: "h-3 w-3 text-[var(--success-500)]" })) : (jsx(Copy, { className: "h-3 w-3 text-muted-foreground" })) })] }) }), jsx(TableCell, { className: "py-2 align-middle whitespace-nowrap text-sm text-muted-foreground", children: jsx(Timestamp, { value: timestamp }) }), jsx(TableCell, { className: "py-2 align-middle w-10", children: jsx(Button, { type: "quaternary", theme: "borderless", size: "small", htmlType: "button", className: "h-6 w-6 p-0 text-muted-foreground hover:text-foreground", onClick: toggleExpand, "aria-expanded": isExpanded, "aria-label": isExpanded ? 'Collapse details' : 'Expand details', children: isExpanded ? (jsx(ChevronDown, { className: "h-4 w-4" })) : (jsx(ChevronRight, { className: "h-4 w-4" })) }) })] }), isExpanded ? (jsx(TableRow, { className: "bg-muted/30 hover:bg-muted/30", children: jsx(TableCell, { colSpan: EVENT_COLUMN_COUNT, className: "p-0", children: jsx(EventExpandedDetails, { event: event }) }) })) : null] }));
11842
12222
  }
11843
12223
 
11844
12224
  /**
@@ -11951,11 +12331,8 @@ const OPTIONS = [
11951
12331
  /**
11952
12332
  * EventTypeToggle provides a segmented control for filtering by event type
11953
12333
  */
11954
- function EventTypeToggle({ value, onChange, className = '', disabled = false, }) {
11955
- return (jsx("div", { className: cn('inline-flex border border-input rounded-md overflow-hidden', className), role: "group", "aria-label": "Filter by event type", children: OPTIONS.map((option, index) => {
11956
- const active = value === option.value;
11957
- return (jsx(Button, { htmlType: "button", type: active ? 'primary' : 'quaternary', theme: active ? 'solid' : 'borderless', className: cn('px-2 h-7 text-xs font-medium transition-all duration-200', index < OPTIONS.length - 1 && 'border-r border-input', !active && 'bg-muted text-foreground hover:bg-muted/80'), style: active ? { borderRadius: 0, border: 0 } : { borderRadius: 0 }, onClick: () => onChange(option.value), disabled: disabled, "aria-pressed": active, title: option.description, children: option.label }, option.value));
11958
- }) }));
12334
+ function EventTypeToggle({ value, onChange, className, disabled = false, }) {
12335
+ return (jsx(SegmentedToggle, { options: OPTIONS, value: value, onChange: onChange, ariaLabel: "Filter by event type", className: className, disabled: disabled }));
11959
12336
  }
11960
12337
 
11961
12338
  /**
@@ -12199,7 +12576,7 @@ function EventsFeedFilters({ client, filters, timeRange, onFiltersChange, onTime
12199
12576
  search: value || undefined,
12200
12577
  });
12201
12578
  }, [filters, onFiltersChange]);
12202
- return (jsx("div", { className: `border-b border-border py-4 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [!hiddenFilters.includes('eventType') && (jsx(EventTypeToggle, { value: eventTypeValue, onChange: handleEventTypeChange, disabled: disabled })), jsxs("div", { className: "relative min-w-[200px] flex-1 max-w-xs", children: [jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), jsx(Input, { type: "text", placeholder: "Search events...", value: filters.search || '', onChange: handleSearchChange, disabled: disabled, className: "pl-8 h-7 text-xs pr-6" }), filters.search ? (jsx("button", { type: "button", onClick: () => onFiltersChange({ ...filters, search: undefined }), className: "absolute right-1.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors", "aria-label": "Clear search", children: jsx(X, { className: "h-3.5 w-3.5" }) })) : null] }), activeFilterIds.map((filterId) => {
12579
+ return (jsx("div", { className: `border-b border-border py-4 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [!hiddenFilters.includes('eventType') && (jsx(EventTypeToggle, { value: eventTypeValue, onChange: handleEventTypeChange, disabled: disabled })), jsxs("div", { className: "relative min-w-[200px] flex-1 max-w-xs", children: [jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), jsx(Input, { type: "text", placeholder: "Search events...", value: filters.search || '', onChange: handleSearchChange, disabled: disabled, className: "h-9 pl-9 pr-9 text-xs md:text-xs" }), filters.search ? (jsx("button", { type: "button", onClick: () => onFiltersChange({ ...filters, search: undefined }), className: "absolute right-0 top-0 flex h-9 w-9 items-center justify-center rounded-r-lg text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "aria-label": "Clear search", children: jsx(X, { className: "h-3.5 w-3.5" }) })) : null] }), activeFilterIds.map((filterId) => {
12203
12580
  const config = FILTER_CONFIGS[filterId];
12204
12581
  return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === 'typeahead' ? getFilterOptions(filterId) : undefined, onValuesChange: (values) => handleFilterChange(filterId, values), onClear: () => handleFilterClear(filterId), onPopoverClose: () => handlePopoverClose(filterId), inputMode: config.inputMode, placeholder: config.placeholder, searchPlaceholder: config.searchPlaceholder, autoOpen: pendingFilter === filterId, disabled: disabled }, filterId));
12205
12582
  }), jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }), jsx("div", { className: "flex-1 min-w-[20px]" }), jsx(TimeRangeDropdown, { presets: TIME_PRESETS, selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() })] }) }));
@@ -12267,7 +12644,7 @@ function EventsFeed({ client, initialFilters = {}, initialTimeRange = { start: "
12267
12644
  : `flex-1 min-h-0 flex flex-col p-3 gap-0 ${className}`;
12268
12645
  // Build list classes - use flex-1 min-h-0 for flex-based scrolling
12269
12646
  const listClasses = "flex-1 min-h-0 overflow-y-auto pr-2";
12270
- return (jsxs(Card, { className: containerClasses, children: [enableStreaming && (jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center", children: [isStreaming ? (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsxs("span", { className: "relative flex h-2 w-2", children: [jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 dark:bg-green-500 opacity-75" }), jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-green-500 dark:bg-green-400" })] }), jsx("span", { className: "text-xs text-muted-foreground", children: "Streaming events..." })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "New events will appear automatically" }) })] }) })) : null, newEventsCount > 0 ? (jsxs(Badge, { variant: "secondary", className: "text-xs ml-2", children: ["+", newEventsCount, " new"] })) : null] }), jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: handleStreamingToggle, children: isStreaming ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsx("rect", { x: "6", y: "4", width: "4", height: "16" }), jsx("rect", { x: "14", y: "4", width: "4", height: "16" })] }), "Pause"] })) : (jsxs(Fragment, { children: [jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("polygon", { points: "5,3 19,12 5,21", fill: "currentColor" }) }), "Resume"] })) })] })), showFilters && (jsx(EventsFeedFilters, { client: client, filters: filters, timeRange: timeRange, onFiltersChange: handleFiltersChange, onTimeRangeChange: handleTimeRangeChange, disabled: isLoading, namespace: namespace, hiddenFilters: hiddenFilters })), jsx(ApiErrorAlert, { error: error, onRetry: refresh, className: "mb-4", errorFormatter: errorFormatter }), jsxs("div", { className: listClasses, ref: scrollContainerRef, children: [jsx(TooltipProvider, { delayDuration: 200, children: variant === 'timeline' ? (jsxs("div", { children: [(isLoading || isRefreshing) && events.length === 0
12647
+ return (jsxs(Card, { className: containerClasses, children: [enableStreaming && (jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center", children: [isStreaming ? (jsx(TooltipProvider, { delayDuration: 300, children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("div", { className: "flex items-center gap-2", children: [jsxs("span", { className: "relative flex h-2 w-2", children: [jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--success-500)] opacity-75" }), jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-[var(--success-500)]" })] }), jsx("span", { className: "text-xs text-muted-foreground", children: "Streaming events..." })] }) }), jsx(TooltipContent, { className: "text-xs", children: jsx("p", { children: "New events will appear automatically" }) })] }) })) : null, newEventsCount > 0 ? (jsxs(Badge, { variant: "secondary", className: "text-xs ml-2", children: ["+", newEventsCount, " new"] })) : null] }), jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: handleStreamingToggle, children: isStreaming ? (jsxs(Fragment, { children: [jsxs("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsx("rect", { x: "6", y: "4", width: "4", height: "16" }), jsx("rect", { x: "14", y: "4", width: "4", height: "16" })] }), "Pause"] })) : (jsxs(Fragment, { children: [jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("polygon", { points: "5,3 19,12 5,21", fill: "currentColor" }) }), "Resume"] })) })] })), showFilters && (jsx(EventsFeedFilters, { client: client, filters: filters, timeRange: timeRange, onFiltersChange: handleFiltersChange, onTimeRangeChange: handleTimeRangeChange, disabled: isLoading, namespace: namespace, hiddenFilters: hiddenFilters })), jsx(ApiErrorAlert, { error: error, onRetry: refresh, className: "mb-4", errorFormatter: errorFormatter }), jsxs("div", { className: listClasses, ref: scrollContainerRef, children: [jsx(TooltipProvider, { delayDuration: 200, children: variant === 'timeline' ? (jsxs("div", { children: [(isLoading || isRefreshing) && events.length === 0
12271
12648
  ? Array.from({ length: 8 }).map((_, index) => (jsx("div", { className: cn(index < 7 && 'border-b border-border', compact ? 'py-2' : 'py-3'), children: jsxs("div", { className: "flex items-center gap-3", children: [jsx(Skeleton, { className: "w-8 h-8 rounded-md shrink-0" }), jsxs("div", { className: "flex-1 min-w-0 flex flex-col gap-1", children: [jsx(Skeleton, { className: "h-4 w-24" }), jsx(Skeleton, { className: "h-3 w-3/4" })] }), jsx(Skeleton, { className: "h-4 w-32 shrink-0" }), jsx(Skeleton, { className: "h-4 w-24 shrink-0" }), jsx(Skeleton, { className: "h-5 w-5 shrink-0" })] }) }, `sk-${index}`)))
12272
12649
  : null, !isLoading && !isRefreshing && events.length === 0 ? (jsxs("div", { className: "py-12 text-center text-muted-foreground", children: [jsx("div", { children: "No events found" }), jsx("div", { className: "text-sm mt-2", children: "Try adjusting your filters or time range" })] })) : null, events.map((event, index) => (jsx(EventFeedItem, { event: event, onEventClick: onEventClick, onResourceClick: onResourceClick, compact: compact, isNew: enableStreaming && index < newEventsCount, variant: "timeline", isLast: index === events.length - 1 }, event.metadata?.uid || event.metadata?.name)))] })) : (jsxs(Table, { className: "w-full", children: [jsx(TableHeader, { children: jsxs(TableRow, { children: [jsx(TableHead, { className: "w-[90px]", children: "Type" }), jsx(TableHead, { className: "w-[160px]", children: "Reason" }), jsx(TableHead, { children: "Note" }), jsx(TableHead, { className: "w-[220px]", children: "Object" }), jsx(TableHead, { className: "w-[170px]", children: "When" }), jsx(TableHead, { className: "w-10", "aria-label": "Expand" })] }) }), jsxs(TableBody, { children: [(isLoading || isRefreshing) && events.length === 0
12273
12650
  ? Array.from({ length: 8 }).map((_, index) => (jsxs(TableRow, { children: [jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-5 w-14 rounded" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-24" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-3/4" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-32" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-4 w-24" }) }), jsx(TableCell, { className: "py-2", children: jsx(Skeleton, { className: "h-6 w-6" }) })] }, `sk-${index}`)))
@@ -12447,14 +12824,14 @@ function PolicyList({ client, onViewPolicy, onEditPolicy, onCreatePolicy, groupB
12447
12824
  policyList.policies.length === 0 && (jsxs("div", { className: "text-center py-8 px-6 text-muted-foreground", children: [jsx("div", { className: "text-4xl mb-3", children: "\uD83D\uDCCB" }), jsx("h3", { className: "m-0 mb-1.5 text-foreground", children: "No policies found" }), jsx("p", { className: "m-0 mb-4 max-w-[400px] mx-auto", children: "Activity policies define how audit events and Kubernetes events are translated into human-readable activity summaries." }), onCreatePolicy && (jsx(Button, { size: "small", onClick: onCreatePolicy, className: "bg-primary text-primary-foreground hover:bg-primary/90 h-7 text-xs", children: "Create your first policy" }))] })), policyList.groups.length > 0 && (jsx("div", { className: "flex flex-col gap-2.5", children: policyList.groups.map((group) => (jsxs("div", { className: "border border-input rounded-lg overflow-hidden", children: [jsxs("button", { type: "button", className: "w-full flex items-center gap-2.5 px-3 py-2 bg-muted border-none cursor-pointer text-left text-xs font-medium text-foreground transition-colors duration-200 hover:bg-accent", onClick: () => toggleGroup(group.apiGroup), children: [jsx("span", { className: `text-xs text-muted-foreground transition-transform duration-200 ${expandedGroups.has(group.apiGroup) ? "rotate-90" : ""}`, children: "\u25B6" }), jsx("span", { className: "flex-1 font-mono", children: group.apiGroup }), jsx(Badge, { variant: "secondary", className: "rounded-full", children: group.policies.length })] }), expandedGroups.has(group.apiGroup) && (jsx("div", { className: "p-1.5", children: jsxs("table", { className: "w-full border-collapse text-sm", children: [jsx("thead", { children: jsxs("tr", { children: [jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap", children: "Kind" }), jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input w-24 text-xs whitespace-nowrap", children: "Audit Rules" }), jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input w-24 text-xs whitespace-nowrap", children: "Event Rules" })] }) }), jsx("tbody", { children: group.policies.map((policy) => {
12448
12825
  const rules = countRules(policy);
12449
12826
  const policyStatus = getPolicyStatus(policy);
12450
- return (jsxs("tr", { className: `hover:bg-muted transition-colors ${handlePolicyClick ? "cursor-pointer" : ""}`, onClick: () => handlePolicyClick?.(policy.metadata?.name || ""), children: [jsx("td", { className: "px-3 py-2 border-b border-border last:border-b-0", children: jsxs("div", { className: "flex items-center gap-2", children: [policyStatus.status === "ready" ? (jsx("div", { className: "w-2 h-2 rounded-full bg-green-500", title: policyStatus.message })) : (jsx("div", { title: policyStatus.message, children: jsx(TriangleAlert, { className: `w-4 h-4 ${policyStatus.status === "error"
12451
- ? "text-red-500 dark:text-red-400"
12827
+ return (jsxs("tr", { className: `hover:bg-muted transition-colors ${handlePolicyClick ? "cursor-pointer" : ""}`, onClick: () => handlePolicyClick?.(policy.metadata?.name || ""), children: [jsx("td", { className: "px-3 py-2 border-b border-border last:border-b-0", children: jsxs("div", { className: "flex items-center gap-2", children: [policyStatus.status === "ready" ? (jsx("div", { className: "w-2 h-2 rounded-full bg-[var(--success-500)]", title: policyStatus.message })) : (jsx("div", { title: policyStatus.message, children: jsx(TriangleAlert, { className: `w-4 h-4 ${policyStatus.status === "error"
12828
+ ? "text-destructive"
12452
12829
  : policyStatus.status === "pending"
12453
- ? "text-amber-600 dark:text-amber-400"
12454
- : "text-gray-400"}` }) })), jsx("span", { className: "text-xs font-medium", children: policy.spec.resource.kind })] }) }), jsx("td", { className: "px-3 py-2 border-b border-border last:border-b-0 text-center w-24", children: jsx(Badge, { variant: rules.audit > 0 ? "success" : "secondary", className: rules.audit === 0
12455
- ? "bg-gray-100 text-gray-400 dark:bg-gray-800 dark:text-gray-500"
12830
+ ? "text-[var(--badge-warning)]"
12831
+ : "text-muted-foreground"}` }) })), jsx("span", { className: "text-xs font-medium", children: policy.spec.resource.kind })] }) }), jsx("td", { className: "px-3 py-2 border-b border-border last:border-b-0 text-center w-24", children: jsx(Badge, { variant: rules.audit > 0 ? "success" : "secondary", className: rules.audit === 0
12832
+ ? "bg-muted text-muted-foreground"
12456
12833
  : "", children: rules.audit }) }), jsx("td", { className: "px-3 py-2 border-b border-border last:border-b-0 text-center w-24", children: jsx(Badge, { variant: rules.event > 0 ? "success" : "secondary", className: rules.event === 0
12457
- ? "bg-gray-100 text-gray-400 dark:bg-gray-800 dark:text-gray-500"
12834
+ ? "bg-muted text-muted-foreground"
12458
12835
  : "", children: rules.event }) })] }, policy.metadata?.name));
12459
12836
  }) })] }) }))] }, group.apiGroup))) }))] })] }));
12460
12837
  }
@@ -12902,7 +13279,7 @@ function PolicyRuleListItem({ rule, ruleType, index, onEdit, onDelete, onMoveUp,
12902
13279
  flex items-center gap-2 p-2 rounded-md border transition-all duration-200
12903
13280
  border-border bg-background hover:bg-muted/50
12904
13281
  ${className}
12905
- `, children: [jsxs("div", { className: "flex-1 min-w-0", children: [jsx("div", { className: "flex items-center gap-2 mb-0.5", children: jsx("span", { className: "font-medium text-xs text-foreground truncate", children: rule.name || `${ruleType === 'audit' ? 'Audit' : 'Event'} Rule #${index + 1}` }) }), rule.description && (jsx("div", { className: "text-[11px] text-muted-foreground mb-0.5 truncate", children: rule.description })), jsx("div", { className: "text-[11px] font-mono text-muted-foreground truncate", title: rule.match, children: truncatedMatch })] }), jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [jsxs("div", { className: "flex flex-col gap-0.5", children: [jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "icon", className: "h-5 w-5 p-0", onClick: onMoveUp, disabled: !canMoveUp, title: "Move up", "aria-label": "Move rule up", children: jsx(ChevronUp, { className: "h-3.5 w-3.5" }) }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "icon", className: "h-5 w-5 p-0", onClick: onMoveDown, disabled: !canMoveDown, title: "Move down", "aria-label": "Move rule down", children: jsx(ChevronDown, { className: "h-3.5 w-3.5" }) })] }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "icon", className: "h-6 w-6 text-muted-foreground hover:text-foreground hover:bg-muted", onClick: onEdit, title: "Edit rule", "aria-label": "Edit rule", children: jsx(SquarePen, { className: "h-3.5 w-3.5" }) }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "icon", className: "h-6 w-6 text-muted-foreground hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-950/50", onClick: onDelete, title: "Delete rule", "aria-label": "Delete rule", children: jsx(Trash2, { className: "h-3.5 w-3.5" }) })] })] }));
13282
+ `, children: [jsxs("div", { className: "flex-1 min-w-0", children: [jsx("div", { className: "flex items-center gap-2 mb-0.5", children: jsx("span", { className: "font-medium text-xs text-foreground truncate", children: rule.name || `${ruleType === 'audit' ? 'Audit' : 'Event'} Rule #${index + 1}` }) }), rule.description && (jsx("div", { className: "text-[11px] text-muted-foreground mb-0.5 truncate", children: rule.description })), jsx("div", { className: "text-[11px] font-mono text-muted-foreground truncate", title: rule.match, children: truncatedMatch })] }), jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [jsxs("div", { className: "flex flex-col gap-0.5", children: [jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "icon", className: "h-5 w-5 p-0", onClick: onMoveUp, disabled: !canMoveUp, title: "Move up", "aria-label": "Move rule up", children: jsx(ChevronUp, { className: "h-3.5 w-3.5" }) }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "icon", className: "h-5 w-5 p-0", onClick: onMoveDown, disabled: !canMoveDown, title: "Move down", "aria-label": "Move rule down", children: jsx(ChevronDown, { className: "h-3.5 w-3.5" }) })] }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "icon", className: "h-6 w-6 text-muted-foreground hover:text-foreground hover:bg-muted", onClick: onEdit, title: "Edit rule", "aria-label": "Edit rule", children: jsx(SquarePen, { className: "h-3.5 w-3.5" }) }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "icon", className: "h-6 w-6 text-muted-foreground hover:text-destructive hover:bg-destructive/10", onClick: onDelete, title: "Delete rule", "aria-label": "Delete rule", children: jsx(Trash2, { className: "h-3.5 w-3.5" }) })] })] }));
12906
13283
  }
12907
13284
 
12908
13285
  /**
@@ -13023,7 +13400,7 @@ const registeredLanguages = new Set();
13023
13400
  * Lazy-loaded Monaco editor for CEL expressions and templates
13024
13401
  * Provides autocomplete based on available field paths
13025
13402
  */
13026
- function CelEditor({ value, onChange, language = 'cel', availableFields = [], placeholder = '', height = '100px', className, error = false, readOnly = false, 'data-testid': testId, }) {
13403
+ function CelEditor({ value, onChange, language = 'cel', availableFields = [], height = '100px', className, error = false, readOnly = false, 'data-testid': testId, }) {
13027
13404
  const [Editor, setEditor] = useState(null);
13028
13405
  const [loadError, setLoadError] = useState(null);
13029
13406
  const [isLoading, setIsLoading] = useState(true);
@@ -13182,7 +13559,7 @@ function CelEditor({ value, onChange, language = 'cel', availableFields = [], pl
13182
13559
  */
13183
13560
  function requiresBracketNotation(key) {
13184
13561
  // Keys with dots, slashes, or other special characters need bracket notation
13185
- return /[.\/\-:]/.test(key) || /^\d/.test(key);
13562
+ return /[./\-:]/.test(key) || /^\d/.test(key);
13186
13563
  }
13187
13564
  /**
13188
13565
  * Build the CEL path for a key, using bracket notation when needed
@@ -13389,7 +13766,7 @@ function PolicyRuleEditorDialog({ open, onOpenChange, rule, ruleType, existingNa
13389
13766
  const [previewError, setPreviewError] = useState(null);
13390
13767
  // Sample data for autocomplete (fetched once when dialog opens)
13391
13768
  const [sampleData, setSampleData] = useState([]);
13392
- const [sampleDataLoading, setSampleDataLoading] = useState(false);
13769
+ const [, setSampleDataLoading] = useState(false);
13393
13770
  // Extract field paths from sample data OR preview data for autocomplete
13394
13771
  const availableFields = useMemo(() => {
13395
13772
  // First try to use preview data if available
@@ -13838,10 +14215,10 @@ function PolicyEditor({ client, policyName, onSaveSuccess, onCancel, onResourceC
13838
14215
  };
13839
14216
  const policyStatus = getPolicyStatus();
13840
14217
  return (jsx(TooltipProvider, { delayDuration: 0, children: jsxs(Card, { className: `rounded-xl ${className}`, children: [jsxs(CardHeader, { className: "flex flex-row justify-between items-center p-4 border-b border-border space-y-0", children: [jsx("div", { className: "flex items-center gap-3", children: editor.isNew ? (jsxs("div", { className: "flex flex-col gap-0.5", children: [jsx(Label, { htmlFor: "policy-name", className: "text-xs text-muted-foreground", children: "Policy Name" }), jsx(Input, { id: "policy-name", type: "text", className: "w-[300px] text-base font-medium", value: editor.name, onChange: (e) => editor.setName(e.target.value), placeholder: "e.g., httpproxy-policy" })] })) : (jsxs("div", { className: "flex flex-col gap-0.5", children: [jsxs("div", { className: "flex items-center gap-1.5", children: [policyStatus && (jsxs(Tooltip, { delayDuration: 300, children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { className: `w-2 h-2 rounded-full ${policyStatus.status === 'ready'
13841
- ? 'bg-green-500'
14218
+ ? 'bg-[var(--success-500)]'
13842
14219
  : policyStatus.status === 'error'
13843
- ? 'bg-red-500'
13844
- : 'bg-yellow-500'}`, "aria-label": `Policy status: ${policyStatus.status}` }) }), jsx(TooltipContent, { children: jsx("p", { className: "text-xs", children: policyStatus.message }) })] })), jsx("h2", { className: "m-0 text-xl font-semibold text-foreground leading-tight", children: editor.spec.resource.kind || 'Untitled Policy' })] }), jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [editor.spec.resource.apiGroup && (jsxs(Fragment, { children: [jsxs("span", { children: ["API Group: ", editor.spec.resource.apiGroup] }), jsx("span", { className: "text-muted-foreground/50", children: "\u2022" })] })), jsxs("span", { children: ["Resource: ", editor.name] }), jsxs(Tooltip, { delayDuration: 500, children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { onClick: handleCopyResourceName, className: "inline-flex items-center justify-center p-0.5 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-opacity cursor-pointer", "aria-label": "Copy resource name", children: isCopied ? (jsx(Check, { className: "h-3 w-3 text-green-600 dark:text-green-400" })) : (jsx(Copy, { className: "h-3 w-3 text-gray-500 dark:text-gray-400" })) }) }), jsx(TooltipContent, { children: jsx("p", { className: "text-xs", children: "Click to copy" }) })] })] })] })) }), jsxs("div", { className: "flex gap-2", children: [onCancel && (jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", onClick: onCancel, disabled: editor.isSaving, className: "h-7 text-xs", children: "Cancel" })), jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", onClick: () => handleSave(true), disabled: !canSave || editor.isSaving, title: "Validate without saving", className: "h-7 text-xs", children: "Validate" }), jsx(Button, { htmlType: "button", size: "small", onClick: () => handleSave(false), disabled: !canSave || editor.isSaving || !editor.isDirty, className: "bg-primary text-primary-foreground border-primary hover:bg-primary/90 hover:border-primary/90 h-7 text-xs", children: editor.isSaving ? (jsxs(Fragment, { children: [jsx("span", { className: "w-3.5 h-3.5 border-2 border-border border-t-[#BF9595] rounded-full animate-spin" }), "Saving..."] })) : ('Save Policy') })] })] }), jsx(ApiErrorAlert, { error: editor.error, className: "mx-4 mt-3", errorFormatter: errorFormatter }), editor.isLoading && (jsxs("div", { className: "flex items-center justify-center gap-2 py-8 text-muted-foreground", children: [jsx("span", { className: "w-5 h-5 border-[3px] border-border border-t-[#BF9595] rounded-full animate-spin" }), "Loading policy..."] })), !editor.isLoading && (jsx(CardContent, { className: "p-4", children: jsxs(Tabs, { defaultValue: "activity", value: activeTab, onValueChange: setActiveTab, className: "w-full", children: [jsxs(TabsList, { className: "grid w-full grid-cols-2 mb-4", children: [jsx(TabsTrigger, { value: "activity", children: "Activity" }), jsx(TabsTrigger, { value: "editor", children: "Editor" })] }), jsx(TabsContent, { value: "activity", className: "mt-0", children: jsx(PolicyActivityView, { client: client, policyResource: editor.spec.resource, policyName: editor.name, onResourceClick: onResourceClick, errorFormatter: errorFormatter }) }), jsx(TabsContent, { value: "editor", className: "mt-0", children: jsxs("div", { className: "flex flex-col gap-4", children: [jsx(PolicyResourceForm, { resource: editor.spec.resource, onChange: editor.setResource, client: client, isEditMode: !editor.isNew }), jsx(PolicyRuleList, { auditRules: editor.spec.auditRules || [], eventRules: editor.spec.eventRules || [], policyResource: editor.spec.resource, apiClient: client, onAuditRulesChange: editor.setAuditRules, onEventRulesChange: editor.setEventRules }), !editor.isNew && (jsx("div", { className: "mt-6 pt-4 border-t border-border", children: jsx("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-4", children: jsx("div", { className: "flex items-start gap-3", children: jsxs("div", { className: "flex-1", children: [jsxs("h3", { className: "text-base font-semibold text-foreground mb-1.5 flex items-center gap-2", children: [jsx(TriangleAlert, { className: "h-5 w-5 text-destructive" }), "Danger Zone"] }), jsxs("p", { className: "text-sm text-muted-foreground mb-3", children: ["Deleting this policy will stop translating audit logs and events for", ' ', jsx("strong", { className: "text-foreground", children: editor.spec.resource.kind }), ' ', "resources. Existing activities will be preserved, but no new activities will be generated."] }), jsxs(Button, { type: "danger", theme: "solid", size: "small", onClick: () => setShowDeleteDialog(true), className: "h-8 text-xs", children: [jsx(Trash2, { className: "h-3.5 w-3.5 mr-1.5" }), "Delete Policy"] })] }) }) }) }))] }) })] }) })), jsx(Dialog, { open: showDeleteDialog, onOpenChange: setShowDeleteDialog, children: jsxs(DialogContent, { className: "max-w-[500px]", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: "Delete Policy" }), jsxs(DialogDescription, { children: ["Are you sure you want to delete", ' ', jsx("strong", { className: "text-foreground", children: editor.name }), "?"] })] }), jsxs(Alert, { variant: "destructive", className: "mt-2", children: [jsx(CircleAlert, { className: "h-4 w-4" }), jsxs(AlertDescription, { children: [jsx("strong", { children: "This action cannot be undone." }), jsx("br", {}), "Activities already generated by this policy will remain in the system, but no new activities will be created for ", editor.spec.resource.kind, " resources."] })] }), jsxs(DialogFooter, { children: [jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: () => setShowDeleteDialog(false), disabled: isDeleting, className: "h-8 text-xs", children: "Cancel" }), jsx(Button, { type: "danger", theme: "solid", size: "small", onClick: handleDelete, disabled: isDeleting, className: "h-8 text-xs", children: isDeleting ? (jsxs(Fragment, { children: [jsx("span", { className: "w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin mr-2" }), "Deleting..."] })) : (jsxs(Fragment, { children: [jsx(Trash2, { className: "h-3.5 w-3.5 mr-1.5" }), "Delete Policy"] })) })] })] }) })] }) }));
14220
+ ? 'bg-destructive'
14221
+ : 'bg-[var(--badge-warning)]'}`, "aria-label": `Policy status: ${policyStatus.status}` }) }), jsx(TooltipContent, { children: jsx("p", { className: "text-xs", children: policyStatus.message }) })] })), jsx("h2", { className: "m-0 text-xl font-semibold text-foreground leading-tight", children: editor.spec.resource.kind || 'Untitled Policy' })] }), jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [editor.spec.resource.apiGroup && (jsxs(Fragment, { children: [jsxs("span", { children: ["API Group: ", editor.spec.resource.apiGroup] }), jsx("span", { className: "text-muted-foreground/50", children: "\u2022" })] })), jsxs("span", { children: ["Resource: ", editor.name] }), jsxs(Tooltip, { delayDuration: 500, children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { onClick: handleCopyResourceName, className: "inline-flex items-center justify-center p-0.5 rounded hover:bg-muted transition-opacity cursor-pointer", "aria-label": "Copy resource name", children: isCopied ? (jsx(Check, { className: "h-3 w-3 text-[var(--success-500)]" })) : (jsx(Copy, { className: "h-3 w-3 text-muted-foreground" })) }) }), jsx(TooltipContent, { children: jsx("p", { className: "text-xs", children: "Click to copy" }) })] })] })] })) }), jsxs("div", { className: "flex gap-2", children: [onCancel && (jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", onClick: onCancel, disabled: editor.isSaving, className: "h-7 text-xs", children: "Cancel" })), jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", onClick: () => handleSave(true), disabled: !canSave || editor.isSaving, title: "Validate without saving", className: "h-7 text-xs", children: "Validate" }), jsx(Button, { htmlType: "button", size: "small", onClick: () => handleSave(false), disabled: !canSave || editor.isSaving || !editor.isDirty, className: "bg-primary text-primary-foreground border-primary hover:bg-primary/90 hover:border-primary/90 h-7 text-xs", children: editor.isSaving ? (jsxs(Fragment, { children: [jsx("span", { className: "w-3.5 h-3.5 border-2 border-border border-t-[#BF9595] rounded-full animate-spin" }), "Saving..."] })) : ('Save Policy') })] })] }), jsx(ApiErrorAlert, { error: editor.error, className: "mx-4 mt-3", errorFormatter: errorFormatter }), editor.isLoading && (jsxs("div", { className: "flex items-center justify-center gap-2 py-8 text-muted-foreground", children: [jsx("span", { className: "w-5 h-5 border-[3px] border-border border-t-[#BF9595] rounded-full animate-spin" }), "Loading policy..."] })), !editor.isLoading && (jsx(CardContent, { className: "p-4", children: jsxs(Tabs, { defaultValue: "activity", value: activeTab, onValueChange: setActiveTab, className: "w-full", children: [jsxs(TabsList, { className: "grid w-full grid-cols-2 mb-4", children: [jsx(TabsTrigger, { value: "activity", children: "Activity" }), jsx(TabsTrigger, { value: "editor", children: "Editor" })] }), jsx(TabsContent, { value: "activity", className: "mt-0", children: jsx(PolicyActivityView, { client: client, policyResource: editor.spec.resource, policyName: editor.name, onResourceClick: onResourceClick, errorFormatter: errorFormatter }) }), jsx(TabsContent, { value: "editor", className: "mt-0", children: jsxs("div", { className: "flex flex-col gap-4", children: [jsx(PolicyResourceForm, { resource: editor.spec.resource, onChange: editor.setResource, client: client, isEditMode: !editor.isNew }), jsx(PolicyRuleList, { auditRules: editor.spec.auditRules || [], eventRules: editor.spec.eventRules || [], policyResource: editor.spec.resource, apiClient: client, onAuditRulesChange: editor.setAuditRules, onEventRulesChange: editor.setEventRules }), !editor.isNew && (jsx("div", { className: "mt-6 pt-4 border-t border-border", children: jsx("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-4", children: jsx("div", { className: "flex items-start gap-3", children: jsxs("div", { className: "flex-1", children: [jsxs("h3", { className: "text-base font-semibold text-foreground mb-1.5 flex items-center gap-2", children: [jsx(TriangleAlert, { className: "h-5 w-5 text-destructive" }), "Danger Zone"] }), jsxs("p", { className: "text-sm text-muted-foreground mb-3", children: ["Deleting this policy will stop translating audit logs and events for", ' ', jsx("strong", { className: "text-foreground", children: editor.spec.resource.kind }), ' ', "resources. Existing activities will be preserved, but no new activities will be generated."] }), jsxs(Button, { type: "danger", theme: "solid", size: "small", onClick: () => setShowDeleteDialog(true), className: "h-8 text-xs", children: [jsx(Trash2, { className: "h-3.5 w-3.5 mr-1.5" }), "Delete Policy"] })] }) }) }) }))] }) })] }) })), jsx(Dialog, { open: showDeleteDialog, onOpenChange: setShowDeleteDialog, children: jsxs(DialogContent, { className: "max-w-[500px]", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: "Delete Policy" }), jsxs(DialogDescription, { children: ["Are you sure you want to delete", ' ', jsx("strong", { className: "text-foreground", children: editor.name }), "?"] })] }), jsxs(Alert, { variant: "destructive", className: "mt-2", children: [jsx(CircleAlert, { className: "h-4 w-4" }), jsxs(AlertDescription, { children: [jsx("strong", { children: "This action cannot be undone." }), jsx("br", {}), "Activities already generated by this policy will remain in the system, but no new activities will be created for ", editor.spec.resource.kind, " resources."] })] }), jsxs(DialogFooter, { children: [jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: () => setShowDeleteDialog(false), disabled: isDeleting, className: "h-8 text-xs", children: "Cancel" }), jsx(Button, { type: "danger", theme: "solid", size: "small", onClick: handleDelete, disabled: isDeleting, className: "h-8 text-xs", children: isDeleting ? (jsxs(Fragment, { children: [jsx("span", { className: "w-3.5 h-3.5 border-2 border-current border-t-transparent rounded-full animate-spin mr-2" }), "Deleting..."] })) : (jsxs(Fragment, { children: [jsx(Trash2, { className: "h-3.5 w-3.5 mr-1.5" }), "Delete Policy"] })) })] })] }) })] }) }));
13845
14222
  }
13846
14223
 
13847
14224
  /**
@@ -13925,12 +14302,12 @@ function PolicyDetailView({ client, policyName, onEdit, onResourceClick, classNa
13925
14302
  const policyStatus = getPolicyStatus();
13926
14303
  const isUnhealthy = policyStatus && policyStatus.status !== 'ready';
13927
14304
  return (jsx(TooltipProvider, { delayDuration: 0, children: jsxs(Card, { className: `rounded-xl ${className}`, children: [jsxs(CardHeader, { className: "flex flex-row justify-between items-center p-6 border-b border-border space-y-0", children: [jsxs("div", { className: "flex items-center gap-4", children: [isLoading && (jsxs("div", { className: "flex flex-col gap-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Skeleton, { className: "w-2 h-2 rounded-full" }), jsx(Skeleton, { className: "h-6 w-32" })] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Skeleton, { className: "h-3 w-24" }), jsx(Skeleton, { className: "h-3 w-32" })] })] })), !isLoading && policy && (jsxs("div", { className: "flex flex-col gap-1", children: [jsxs("h2", { className: "m-0 text-xl font-semibold text-foreground leading-tight flex items-center gap-2", children: [jsxs(Tooltip, { delayDuration: 500, children: [jsx(TooltipTrigger, { asChild: true, children: jsx("span", { className: `w-2 h-2 rounded-full ${policyStatus?.status === 'ready'
13928
- ? 'bg-green-500'
14305
+ ? 'bg-[var(--success-500)]'
13929
14306
  : policyStatus?.status === 'error'
13930
- ? 'bg-red-500'
14307
+ ? 'bg-destructive'
13931
14308
  : policyStatus?.status === 'pending'
13932
- ? 'bg-yellow-500'
13933
- : 'bg-green-500'}` }) }), jsx(TooltipContent, { children: jsx("p", { className: "text-xs", children: policyStatus?.message || 'Policy is active' }) })] }), policy.spec.resource.kind || 'Untitled Policy'] }), jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [policy.spec.resource.apiGroup && (jsxs(Fragment, { children: [jsxs("span", { children: ["API Group: ", policy.spec.resource.apiGroup] }), jsx("span", { className: "text-muted-foreground/50", children: "\u2022" })] })), jsxs("span", { children: ["Resource: ", policyName] }), jsxs(Tooltip, { delayDuration: 500, children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { onClick: handleCopyResourceName, className: "inline-flex items-center justify-center p-0.5 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-opacity cursor-pointer", "aria-label": "Copy resource name", children: isCopied ? (jsx(Check, { className: "h-3 w-3 text-green-600 dark:text-green-400" })) : (jsx(Copy, { className: "h-3 w-3 text-gray-500 dark:text-gray-400" })) }) }), jsx(TooltipContent, { children: jsx("p", { className: "text-xs", children: "Click to copy" }) })] })] })] }))] }), jsx("div", { className: "flex gap-3", children: onEdit && (jsxs(Button, { htmlType: "button", size: "small", onClick: onEdit, className: "bg-primary text-primary-foreground border-primary hover:bg-primary/90 hover:border-primary/90 h-7 text-xs", children: [jsx(SquarePen, { className: "h-3.5 w-3.5 mr-1.5" }), "Edit Policy"] })) })] }), jsx(ApiErrorAlert, { error: error, className: "mx-6 mt-4", errorFormatter: errorFormatter }), isUnhealthy && policyStatus && (jsxs(Alert, { variant: policyStatus.status === 'error' ? 'destructive' : 'warning', className: "mx-6 mt-4", children: [policyStatus.status === 'error' ? (jsx(CircleAlert, { className: "h-4 w-4" })) : (jsx(TriangleAlert, { className: "h-4 w-4" })), jsxs(AlertDescription, { children: [jsx("strong", { children: policyStatus.status === 'error' ? 'Policy Error: ' : 'Policy Pending: ' }), policyStatus.message] })] })), isLoading && (jsx(CardContent, { className: "p-6", children: jsx(PolicyActivityViewSkeleton, {}) })), !isLoading && policy && (jsx(CardContent, { className: "p-6", children: jsx(PolicyActivityView, { client: client, policyResource: policy.spec.resource, policyName: policyName, onResourceClick: onResourceClick, errorFormatter: errorFormatter }) }))] }) }));
14309
+ ? 'bg-[var(--badge-warning)]'
14310
+ : 'bg-[var(--success-500)]'}` }) }), jsx(TooltipContent, { children: jsx("p", { className: "text-xs", children: policyStatus?.message || 'Policy is active' }) })] }), policy.spec.resource.kind || 'Untitled Policy'] }), jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [policy.spec.resource.apiGroup && (jsxs(Fragment, { children: [jsxs("span", { children: ["API Group: ", policy.spec.resource.apiGroup] }), jsx("span", { className: "text-muted-foreground/50", children: "\u2022" })] })), jsxs("span", { children: ["Resource: ", policyName] }), jsxs(Tooltip, { delayDuration: 500, children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { onClick: handleCopyResourceName, className: "inline-flex items-center justify-center p-0.5 rounded hover:bg-muted transition-opacity cursor-pointer", "aria-label": "Copy resource name", children: isCopied ? (jsx(Check, { className: "h-3 w-3 text-[var(--success-500)]" })) : (jsx(Copy, { className: "h-3 w-3 text-muted-foreground" })) }) }), jsx(TooltipContent, { children: jsx("p", { className: "text-xs", children: "Click to copy" }) })] })] })] }))] }), jsx("div", { className: "flex gap-3", children: onEdit && (jsxs(Button, { htmlType: "button", size: "small", onClick: onEdit, className: "bg-primary text-primary-foreground border-primary hover:bg-primary/90 hover:border-primary/90 h-7 text-xs", children: [jsx(SquarePen, { className: "h-3.5 w-3.5 mr-1.5" }), "Edit Policy"] })) })] }), jsx(ApiErrorAlert, { error: error, className: "mx-6 mt-4", errorFormatter: errorFormatter }), isUnhealthy && policyStatus && (jsxs(Alert, { variant: policyStatus.status === 'error' ? 'destructive' : 'warning', className: "mx-6 mt-4", children: [policyStatus.status === 'error' ? (jsx(CircleAlert, { className: "h-4 w-4" })) : (jsx(TriangleAlert, { className: "h-4 w-4" })), jsxs(AlertDescription, { children: [jsx("strong", { children: policyStatus.status === 'error' ? 'Policy Error: ' : 'Policy Pending: ' }), policyStatus.message] })] })), isLoading && (jsx(CardContent, { className: "p-6", children: jsx(PolicyActivityViewSkeleton, {}) })), !isLoading && policy && (jsx(CardContent, { className: "p-6", children: jsx(PolicyActivityView, { client: client, policyResource: policy.spec.resource, policyName: policyName, onResourceClick: onResourceClick, errorFormatter: errorFormatter }) }))] }) }));
13934
14311
  }
13935
14312
 
13936
14313
  /**
@@ -14030,7 +14407,7 @@ function PolicyEditView({ client, policyName, onSaveSuccess, onCancel, className
14030
14407
  };
14031
14408
  const policyStatus = getPolicyStatus();
14032
14409
  const isUnhealthy = policyStatus && policyStatus.status !== 'ready';
14033
- return (jsx(TooltipProvider, { delayDuration: 0, children: jsxs(Card, { className: `rounded-xl ${className}`, children: [jsxs(CardHeader, { className: "flex flex-row justify-between items-center p-4 border-b border-border space-y-0", children: [jsx("div", { className: "flex items-center gap-3", children: editor.isNew ? (jsxs("div", { className: "flex flex-col gap-0.5", children: [jsx(Label, { htmlFor: "policy-name", className: "text-xs text-muted-foreground", children: "Policy Name" }), jsx(Input, { id: "policy-name", type: "text", className: "w-[300px] text-base font-medium", value: editor.name, onChange: (e) => editor.setName(e.target.value), placeholder: "e.g., httpproxy-policy" })] })) : (jsxs("div", { className: "flex flex-col gap-0.5", children: [jsx("h2", { className: "m-0 text-xl font-semibold text-foreground leading-tight", children: editor.spec.resource.kind || 'Untitled Policy' }), jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [editor.spec.resource.apiGroup && (jsxs(Fragment, { children: [jsxs("span", { children: ["API Group: ", editor.spec.resource.apiGroup] }), jsx("span", { className: "text-muted-foreground/50", children: "\u2022" })] })), jsxs("span", { children: ["Resource: ", editor.name] }), jsxs(Tooltip, { delayDuration: 500, children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { onClick: handleCopyResourceName, className: "inline-flex items-center justify-center p-0.5 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition-opacity cursor-pointer", "aria-label": "Copy resource name", children: isCopied ? (jsx(Check, { className: "h-3 w-3 text-green-600 dark:text-green-400" })) : (jsx(Copy, { className: "h-3 w-3 text-gray-500 dark:text-gray-400" })) }) }), jsx(TooltipContent, { children: jsx("p", { className: "text-xs", children: "Click to copy" }) })] })] })] })) }), jsxs("div", { className: "flex gap-2", children: [onCancel && (jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", onClick: onCancel, disabled: editor.isSaving, className: "h-7 text-xs", children: "Cancel" })), jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", onClick: () => handleSave(true), disabled: !canSave || editor.isSaving, title: "Validate without saving", className: "h-7 text-xs", children: "Validate" }), jsx(Button, { htmlType: "button", size: "small", onClick: () => handleSave(false), disabled: !canSave || editor.isSaving || !editor.isDirty, className: "bg-primary text-primary-foreground border-primary hover:bg-primary/90 hover:border-primary/90 h-7 text-xs", children: editor.isSaving ? (jsxs(Fragment, { children: [jsx("span", { className: "w-3.5 h-3.5 border-2 border-border border-t-[#BF9595] rounded-full animate-spin" }), "Saving..."] })) : ('Save Policy') })] })] }), jsx(ApiErrorAlert, { error: editor.error, className: "mx-4 mt-3", errorFormatter: errorFormatter }), isUnhealthy && policyStatus && (jsxs(Alert, { variant: policyStatus.status === 'error' ? 'destructive' : 'warning', className: "mx-4 mt-3", children: [policyStatus.status === 'error' ? (jsx(CircleAlert, { className: "h-4 w-4" })) : (jsx(TriangleAlert, { className: "h-4 w-4" })), jsxs(AlertDescription, { children: [jsx("strong", { children: policyStatus.status === 'error' ? 'Policy Error: ' : 'Policy Pending: ' }), policyStatus.message] })] })), editor.isLoading && (jsxs("div", { className: "flex items-center justify-center gap-2 py-8 text-muted-foreground", children: [jsx("span", { className: "w-5 h-5 border-[3px] border-border border-t-[#BF9595] rounded-full animate-spin" }), "Loading policy..."] })), !editor.isLoading && (jsx(CardContent, { className: "p-4", children: jsxs("div", { className: "flex flex-col gap-4", children: [jsx(PolicyResourceForm, { resource: editor.spec.resource, onChange: editor.setResource, client: client, isEditMode: !editor.isNew }), jsx(PolicyRuleList, { auditRules: editor.spec.auditRules || [], eventRules: editor.spec.eventRules || [], policyResource: editor.spec.resource, apiClient: client, onAuditRulesChange: editor.setAuditRules, onEventRulesChange: editor.setEventRules }), !editor.isNew && (jsx("div", { className: "mt-6 pt-4 border-t border-border", children: jsx("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-4", children: jsx("div", { className: "flex items-start gap-3", children: jsxs("div", { className: "flex-1", children: [jsxs("h3", { className: "text-base font-semibold text-foreground mb-1.5 flex items-center gap-2", children: [jsx(TriangleAlert, { className: "h-5 w-5 text-destructive" }), "Danger Zone"] }), jsxs("p", { className: "text-sm text-muted-foreground mb-3", children: ["Deleting this policy will stop translating audit logs and events for", ' ', jsx("strong", { className: "text-foreground", children: editor.spec.resource.kind }), ' ', "resources. Existing activities will be preserved, but no new activities will be generated."] }), jsxs(Button, { type: "danger", theme: "solid", size: "small", onClick: () => setShowDeleteDialog(true), className: "h-8 text-xs", children: [jsx(Trash2, { className: "h-3.5 w-3.5 mr-1.5" }), "Delete Policy"] })] }) }) }) }))] }) })), jsx(Dialog, { open: showDeleteDialog, onOpenChange: setShowDeleteDialog, children: jsxs(DialogContent, { className: "max-w-[500px]", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: "Delete Policy" }), jsxs(DialogDescription, { children: ["Are you sure you want to delete", ' ', jsx("strong", { className: "text-foreground", children: editor.name }), "?"] })] }), jsxs(Alert, { variant: "destructive", className: "mt-2", children: [jsx(CircleAlert, { className: "h-4 w-4" }), jsxs(AlertDescription, { children: [jsx("strong", { children: "This action cannot be undone." }), jsx("br", {}), "Activities already generated by this policy will remain in the system, but no new activities will be created for ", editor.spec.resource.kind, " resources."] })] }), jsxs(DialogFooter, { children: [jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: () => setShowDeleteDialog(false), disabled: isDeleting, className: "h-8 text-xs", children: "Cancel" }), jsx(Button, { type: "danger", theme: "solid", size: "small", onClick: handleDelete, disabled: isDeleting, className: "h-8 text-xs", children: isDeleting ? (jsxs(Fragment, { children: [jsx("span", { className: "w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin mr-2" }), "Deleting..."] })) : (jsxs(Fragment, { children: [jsx(Trash2, { className: "h-3.5 w-3.5 mr-1.5" }), "Delete Policy"] })) })] })] }) })] }) }));
14410
+ return (jsx(TooltipProvider, { delayDuration: 0, children: jsxs(Card, { className: `rounded-xl ${className}`, children: [jsxs(CardHeader, { className: "flex flex-row justify-between items-center p-4 border-b border-border space-y-0", children: [jsx("div", { className: "flex items-center gap-3", children: editor.isNew ? (jsxs("div", { className: "flex flex-col gap-0.5", children: [jsx(Label, { htmlFor: "policy-name", className: "text-xs text-muted-foreground", children: "Policy Name" }), jsx(Input, { id: "policy-name", type: "text", className: "w-[300px] text-base font-medium", value: editor.name, onChange: (e) => editor.setName(e.target.value), placeholder: "e.g., httpproxy-policy" })] })) : (jsxs("div", { className: "flex flex-col gap-0.5", children: [jsx("h2", { className: "m-0 text-xl font-semibold text-foreground leading-tight", children: editor.spec.resource.kind || 'Untitled Policy' }), jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [editor.spec.resource.apiGroup && (jsxs(Fragment, { children: [jsxs("span", { children: ["API Group: ", editor.spec.resource.apiGroup] }), jsx("span", { className: "text-muted-foreground/50", children: "\u2022" })] })), jsxs("span", { children: ["Resource: ", editor.name] }), jsxs(Tooltip, { delayDuration: 500, children: [jsx(TooltipTrigger, { asChild: true, children: jsx("button", { onClick: handleCopyResourceName, className: "inline-flex items-center justify-center p-0.5 rounded hover:bg-muted transition-opacity cursor-pointer", "aria-label": "Copy resource name", children: isCopied ? (jsx(Check, { className: "h-3 w-3 text-[var(--success-500)]" })) : (jsx(Copy, { className: "h-3 w-3 text-muted-foreground" })) }) }), jsx(TooltipContent, { children: jsx("p", { className: "text-xs", children: "Click to copy" }) })] })] })] })) }), jsxs("div", { className: "flex gap-2", children: [onCancel && (jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", onClick: onCancel, disabled: editor.isSaving, className: "h-7 text-xs", children: "Cancel" })), jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", onClick: () => handleSave(true), disabled: !canSave || editor.isSaving, title: "Validate without saving", className: "h-7 text-xs", children: "Validate" }), jsx(Button, { htmlType: "button", size: "small", onClick: () => handleSave(false), disabled: !canSave || editor.isSaving || !editor.isDirty, className: "bg-primary text-primary-foreground border-primary hover:bg-primary/90 hover:border-primary/90 h-7 text-xs", children: editor.isSaving ? (jsxs(Fragment, { children: [jsx("span", { className: "w-3.5 h-3.5 border-2 border-border border-t-[#BF9595] rounded-full animate-spin" }), "Saving..."] })) : ('Save Policy') })] })] }), jsx(ApiErrorAlert, { error: editor.error, className: "mx-4 mt-3", errorFormatter: errorFormatter }), isUnhealthy && policyStatus && (jsxs(Alert, { variant: policyStatus.status === 'error' ? 'destructive' : 'warning', className: "mx-4 mt-3", children: [policyStatus.status === 'error' ? (jsx(CircleAlert, { className: "h-4 w-4" })) : (jsx(TriangleAlert, { className: "h-4 w-4" })), jsxs(AlertDescription, { children: [jsx("strong", { children: policyStatus.status === 'error' ? 'Policy Error: ' : 'Policy Pending: ' }), policyStatus.message] })] })), editor.isLoading && (jsxs("div", { className: "flex items-center justify-center gap-2 py-8 text-muted-foreground", children: [jsx("span", { className: "w-5 h-5 border-[3px] border-border border-t-[#BF9595] rounded-full animate-spin" }), "Loading policy..."] })), !editor.isLoading && (jsx(CardContent, { className: "p-4", children: jsxs("div", { className: "flex flex-col gap-4", children: [jsx(PolicyResourceForm, { resource: editor.spec.resource, onChange: editor.setResource, client: client, isEditMode: !editor.isNew }), jsx(PolicyRuleList, { auditRules: editor.spec.auditRules || [], eventRules: editor.spec.eventRules || [], policyResource: editor.spec.resource, apiClient: client, onAuditRulesChange: editor.setAuditRules, onEventRulesChange: editor.setEventRules }), !editor.isNew && (jsx("div", { className: "mt-6 pt-4 border-t border-border", children: jsx("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-4", children: jsx("div", { className: "flex items-start gap-3", children: jsxs("div", { className: "flex-1", children: [jsxs("h3", { className: "text-base font-semibold text-foreground mb-1.5 flex items-center gap-2", children: [jsx(TriangleAlert, { className: "h-5 w-5 text-destructive" }), "Danger Zone"] }), jsxs("p", { className: "text-sm text-muted-foreground mb-3", children: ["Deleting this policy will stop translating audit logs and events for", ' ', jsx("strong", { className: "text-foreground", children: editor.spec.resource.kind }), ' ', "resources. Existing activities will be preserved, but no new activities will be generated."] }), jsxs(Button, { type: "danger", theme: "solid", size: "small", onClick: () => setShowDeleteDialog(true), className: "h-8 text-xs", children: [jsx(Trash2, { className: "h-3.5 w-3.5 mr-1.5" }), "Delete Policy"] })] }) }) }) }))] }) })), jsx(Dialog, { open: showDeleteDialog, onOpenChange: setShowDeleteDialog, children: jsxs(DialogContent, { className: "max-w-[500px]", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: "Delete Policy" }), jsxs(DialogDescription, { children: ["Are you sure you want to delete", ' ', jsx("strong", { className: "text-foreground", children: editor.name }), "?"] })] }), jsxs(Alert, { variant: "destructive", className: "mt-2", children: [jsx(CircleAlert, { className: "h-4 w-4" }), jsxs(AlertDescription, { children: [jsx("strong", { children: "This action cannot be undone." }), jsx("br", {}), "Activities already generated by this policy will remain in the system, but no new activities will be created for ", editor.spec.resource.kind, " resources."] })] }), jsxs(DialogFooter, { children: [jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: () => setShowDeleteDialog(false), disabled: isDeleting, className: "h-8 text-xs", children: "Cancel" }), jsx(Button, { type: "danger", theme: "solid", size: "small", onClick: handleDelete, disabled: isDeleting, className: "h-8 text-xs", children: isDeleting ? (jsxs(Fragment, { children: [jsx("span", { className: "w-3.5 h-3.5 border-2 border-current border-t-transparent rounded-full animate-spin mr-2" }), "Deleting..."] })) : (jsxs(Fragment, { children: [jsx(Trash2, { className: "h-3.5 w-3.5 mr-1.5" }), "Delete Policy"] })) })] })] }) })] }) }));
14034
14411
  }
14035
14412
 
14036
14413
  /**
@@ -14111,8 +14488,8 @@ function PolicyRuleEditor({ rule, index, ruleType, isHighlighted = false, onChan
14111
14488
  onChange({ ...rule, summary: example });
14112
14489
  };
14113
14490
  return (jsxs(Card, { className: `mb-3 transition-all duration-200 ${isHighlighted
14114
- ? 'border-emerald-600 bg-emerald-50'
14115
- : 'bg-muted'} ${className}`, children: [jsxs(CardHeader, { className: "flex flex-row justify-between items-center p-3 pb-0", children: [jsxs("span", { className: `text-xs font-semibold uppercase tracking-wide ${isHighlighted ? 'text-emerald-600' : 'text-muted-foreground'}`, children: [ruleType === 'audit' ? 'Audit' : 'Event', " Rule #", index + 1] }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "icon", className: "w-6 h-6 text-xl leading-none text-muted-foreground hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-950/50 dark:hover:text-red-400", onClick: onDelete, title: "Delete rule", children: "\u00D7" })] }), jsxs(CardContent, { className: "p-3", children: [jsxs("div", { className: "mb-3", children: [jsxs("div", { className: "flex justify-between items-center mb-1", children: [jsx(Label, { htmlFor: `rule-${index}-match`, children: "Match Expression (CEL)" }), jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", className: "px-2 py-0.5 h-auto text-xs", onClick: () => setShowMatchHelp(!showMatchHelp), children: showMatchHelp ? 'Hide Help' : 'Show Help' })] }), jsx(Textarea, { id: `rule-${index}-match`, className: "font-mono text-sm resize-y min-h-[60px]", value: rule.match, onChange: handleMatchChange, placeholder: `e.g., ${celHelp.examples[0]}`, rows: 2, spellCheck: false }), showMatchHelp && (jsxs("div", { className: "mt-2 p-3 bg-background border border-border rounded-md text-xs", children: [jsxs("div", { className: "mb-3 last:mb-0", children: [jsx("strong", { className: "block mb-1.5 text-foreground", children: "Available Variables:" }), jsx("ul", { className: "m-0 pl-5 list-disc", children: celHelp.variables.map((v) => (jsxs("li", { className: "mb-0.5", children: [jsx("code", { className: "bg-muted px-1.5 py-0.5 rounded text-xs", children: v.name }), " - ", v.description] }, v.name))) })] }), jsxs("div", { className: "mb-3 last:mb-0", children: [jsx("strong", { className: "block mb-1.5 text-foreground", children: "Examples:" }), jsx("ul", { className: "m-0 p-0 list-none", children: celHelp.examples.map((ex, i) => (jsxs("li", { className: "flex items-center gap-2 mb-1 px-2 py-1 bg-muted rounded", children: [jsx("code", { className: "flex-1 text-xs break-all", children: ex }), jsx(Button, { htmlType: "button", size: "small", className: "px-2 py-0.5 h-auto bg-primary text-primary-foreground border-none text-xs font-medium uppercase hover:bg-primary/90", onClick: () => insertMatchExample(ex), children: "Use" })] }, i))) })] })] }))] }), jsxs("div", { className: "mb-3 last:mb-0", children: [jsxs("div", { className: "flex justify-between items-center mb-1", children: [jsx(Label, { htmlFor: `rule-${index}-summary`, children: "Summary Template (CEL)" }), jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", className: "px-2 py-0.5 h-auto text-xs", onClick: () => setShowSummaryHelp(!showSummaryHelp), children: showSummaryHelp ? 'Hide Help' : 'Show Help' })] }), jsx(Textarea, { id: `rule-${index}-summary`, className: "font-mono text-sm resize-y min-h-[60px]", value: rule.summary, onChange: handleSummaryChange, placeholder: `e.g., ${SUMMARY_HELP.examples[0]}`, rows: 2, spellCheck: false }), showSummaryHelp && (jsxs("div", { className: "mt-2 p-3 bg-background border border-border rounded-md text-xs", children: [jsxs("div", { className: "mb-3 last:mb-0", children: [jsx("strong", { className: "block mb-1.5 text-foreground", children: "Available Variables:" }), jsx("ul", { className: "m-0 pl-5 list-disc", children: SUMMARY_HELP.variables.map((v) => (jsxs("li", { className: "mb-0.5", children: [jsx("code", { className: "bg-muted px-1.5 py-0.5 rounded text-xs", children: '{{ ' + v.name + ' }}' }), " - ", v.description] }, v.name))) })] }), jsxs("div", { className: "mb-3 last:mb-0", children: [jsx("strong", { className: "block mb-1.5 text-foreground", children: "Examples:" }), jsx("ul", { className: "m-0 p-0 list-none", children: SUMMARY_HELP.examples.map((ex, i) => (jsxs("li", { className: "flex items-center gap-2 mb-1 px-2 py-1 bg-muted rounded", children: [jsx("code", { className: "flex-1 text-xs break-all", children: ex }), jsx(Button, { htmlType: "button", size: "small", className: "px-2 py-0.5 h-auto bg-primary text-primary-foreground border-none text-xs font-medium uppercase hover:bg-primary/90", onClick: () => insertSummaryExample(ex), children: "Use" })] }, i))) })] })] }))] })] })] }));
14491
+ ? 'border-[var(--success-500)] bg-[var(--success-100)]'
14492
+ : 'bg-muted'} ${className}`, children: [jsxs(CardHeader, { className: "flex flex-row justify-between items-center p-3 pb-0", children: [jsxs("span", { className: `text-xs font-semibold uppercase tracking-wide ${isHighlighted ? 'text-[var(--success-500)]' : 'text-muted-foreground'}`, children: [ruleType === 'audit' ? 'Audit' : 'Event', " Rule #", index + 1] }), jsx(Button, { htmlType: "button", type: "quaternary", theme: "borderless", size: "icon", className: "w-6 h-6 text-xl leading-none text-muted-foreground hover:bg-destructive/10 hover:text-destructive", onClick: onDelete, title: "Delete rule", children: "\u00D7" })] }), jsxs(CardContent, { className: "p-3", children: [jsxs("div", { className: "mb-3", children: [jsxs("div", { className: "flex justify-between items-center mb-1", children: [jsx(Label, { htmlFor: `rule-${index}-match`, children: "Match Expression (CEL)" }), jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", className: "px-2 py-0.5 h-auto text-xs", onClick: () => setShowMatchHelp(!showMatchHelp), children: showMatchHelp ? 'Hide Help' : 'Show Help' })] }), jsx(Textarea, { id: `rule-${index}-match`, className: "font-mono text-sm resize-y min-h-[60px]", value: rule.match, onChange: handleMatchChange, placeholder: `e.g., ${celHelp.examples[0]}`, rows: 2, spellCheck: false }), showMatchHelp && (jsxs("div", { className: "mt-2 p-3 bg-background border border-border rounded-md text-xs", children: [jsxs("div", { className: "mb-3 last:mb-0", children: [jsx("strong", { className: "block mb-1.5 text-foreground", children: "Available Variables:" }), jsx("ul", { className: "m-0 pl-5 list-disc", children: celHelp.variables.map((v) => (jsxs("li", { className: "mb-0.5", children: [jsx("code", { className: "bg-muted px-1.5 py-0.5 rounded text-xs", children: v.name }), " - ", v.description] }, v.name))) })] }), jsxs("div", { className: "mb-3 last:mb-0", children: [jsx("strong", { className: "block mb-1.5 text-foreground", children: "Examples:" }), jsx("ul", { className: "m-0 p-0 list-none", children: celHelp.examples.map((ex, i) => (jsxs("li", { className: "flex items-center gap-2 mb-1 px-2 py-1 bg-muted rounded", children: [jsx("code", { className: "flex-1 text-xs break-all", children: ex }), jsx(Button, { htmlType: "button", size: "small", className: "px-2 py-0.5 h-auto bg-primary text-primary-foreground border-none text-xs font-medium uppercase hover:bg-primary/90", onClick: () => insertMatchExample(ex), children: "Use" })] }, i))) })] })] }))] }), jsxs("div", { className: "mb-3 last:mb-0", children: [jsxs("div", { className: "flex justify-between items-center mb-1", children: [jsx(Label, { htmlFor: `rule-${index}-summary`, children: "Summary Template (CEL)" }), jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", size: "small", className: "px-2 py-0.5 h-auto text-xs", onClick: () => setShowSummaryHelp(!showSummaryHelp), children: showSummaryHelp ? 'Hide Help' : 'Show Help' })] }), jsx(Textarea, { id: `rule-${index}-summary`, className: "font-mono text-sm resize-y min-h-[60px]", value: rule.summary, onChange: handleSummaryChange, placeholder: `e.g., ${SUMMARY_HELP.examples[0]}`, rows: 2, spellCheck: false }), showSummaryHelp && (jsxs("div", { className: "mt-2 p-3 bg-background border border-border rounded-md text-xs", children: [jsxs("div", { className: "mb-3 last:mb-0", children: [jsx("strong", { className: "block mb-1.5 text-foreground", children: "Available Variables:" }), jsx("ul", { className: "m-0 pl-5 list-disc", children: SUMMARY_HELP.variables.map((v) => (jsxs("li", { className: "mb-0.5", children: [jsx("code", { className: "bg-muted px-1.5 py-0.5 rounded text-xs", children: '{{ ' + v.name + ' }}' }), " - ", v.description] }, v.name))) })] }), jsxs("div", { className: "mb-3 last:mb-0", children: [jsx("strong", { className: "block mb-1.5 text-foreground", children: "Examples:" }), jsx("ul", { className: "m-0 p-0 list-none", children: SUMMARY_HELP.examples.map((ex, i) => (jsxs("li", { className: "flex items-center gap-2 mb-1 px-2 py-1 bg-muted rounded", children: [jsx("code", { className: "flex-1 text-xs break-all", children: ex }), jsx(Button, { htmlType: "button", size: "small", className: "px-2 py-0.5 h-auto bg-primary text-primary-foreground border-none text-xs font-medium uppercase hover:bg-primary/90", onClick: () => insertSummaryExample(ex), children: "Use" })] }, i))) })] })] }))] })] })] }));
14116
14493
  }
14117
14494
 
14118
14495
  /**
@@ -14770,7 +15147,7 @@ function ReindexJobList({ client, onViewJob, onCreateJob, watch = true, classNam
14770
15147
  const bTime = b.metadata?.creationTimestamp || '';
14771
15148
  return bTime.localeCompare(aTime);
14772
15149
  });
14773
- return (jsxs(Card, { className: `rounded-xl ${className}`, children: [jsxs(CardHeader, { className: "pb-1.5", children: [jsxs("div", { className: "flex justify-between items-center", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("p", { className: "text-sm text-muted-foreground m-0", children: "Re-process historical events through updated policies" }), isWatching && (jsxs(Badge, { variant: "success", className: "text-xs", children: [jsx("span", { className: "w-1.5 h-1.5 bg-green-500 rounded-full mr-1.5 animate-pulse" }), "Live"] }))] }), jsxs("div", { className: "flex gap-1.5", children: [jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: refresh, disabled: isLoading, title: "Refresh job list", className: "h-7 px-2", children: isLoading ? (jsx("span", { className: "w-3.5 h-3.5 border-2 border-border border-t-primary rounded-full animate-spin" })) : ('↻') }), onCreateJob && (jsx(Button, { size: "small", onClick: onCreateJob, className: "bg-primary text-primary-foreground hover:bg-primary/90 h-7 text-xs", children: "+ Create Job" }))] })] }), jsx(Separator, { className: "mt-1.5" })] }), jsxs(CardContent, { className: "pt-3", children: [jsx(ApiErrorAlert, { error: error, onRetry: refresh, className: "mb-3", errorFormatter: errorFormatter }), isLoading && jobs.length === 0 && (jsx("div", { className: "border border-input rounded-lg overflow-hidden", children: jsxs("table", { className: "w-full border-collapse text-sm", children: [jsx("thead", { children: jsxs("tr", { children: [jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap", children: "Job Name" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Status" }), jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap", children: "Message" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Duration" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Actions" })] }) }), jsxs("tbody", { children: [jsx(ReindexJobSkeletonRow, {}), jsx(ReindexJobSkeletonRow, {}), jsx(ReindexJobSkeletonRow, {})] })] }) })), !isLoading && !error && jobs.length === 0 && (jsxs("div", { className: "text-center py-8 px-6 text-muted-foreground", children: [jsx("div", { className: "text-4xl mb-3", children: "\uD83D\uDD04" }), jsx("h3", { className: "m-0 mb-1.5 text-foreground", children: "No reindex jobs found" }), jsx("p", { className: "m-0 mb-4 max-w-[400px] mx-auto", children: "Create a reindex job to re-process historical audit logs and events through current ActivityPolicy rules." }), onCreateJob && (jsx(Button, { size: "small", onClick: onCreateJob, className: "bg-primary text-primary-foreground hover:bg-primary/90 h-7 text-xs", children: "Create your first reindex job" }))] })), sortedJobs.length > 0 && (jsx("div", { className: "border border-input rounded-lg overflow-hidden", children: jsxs("table", { className: "w-full border-collapse text-sm", children: [jsx("thead", { children: jsxs("tr", { children: [jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap", children: "Job Name" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Status" }), jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap", children: "Message" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Duration" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Actions" })] }) }), jsx("tbody", { children: sortedJobs.map((job) => {
15150
+ return (jsxs(Card, { className: `rounded-xl ${className}`, children: [jsxs(CardHeader, { className: "pb-1.5", children: [jsxs("div", { className: "flex justify-between items-center", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("p", { className: "text-sm text-muted-foreground m-0", children: "Re-process historical events through updated policies" }), isWatching && (jsxs(Badge, { variant: "success", className: "text-xs", children: [jsx("span", { className: "w-1.5 h-1.5 bg-[var(--success-500)] rounded-full mr-1.5 animate-pulse" }), "Live"] }))] }), jsxs("div", { className: "flex gap-1.5", children: [jsx(Button, { type: "tertiary", theme: "outline", size: "small", onClick: refresh, disabled: isLoading, title: "Refresh job list", className: "h-7 px-2", children: isLoading ? (jsx("span", { className: "w-3.5 h-3.5 border-2 border-border border-t-primary rounded-full animate-spin" })) : ('↻') }), onCreateJob && (jsx(Button, { size: "small", onClick: onCreateJob, className: "bg-primary text-primary-foreground hover:bg-primary/90 h-7 text-xs", children: "+ Create Job" }))] })] }), jsx(Separator, { className: "mt-1.5" })] }), jsxs(CardContent, { className: "pt-3", children: [jsx(ApiErrorAlert, { error: error, onRetry: refresh, className: "mb-3", errorFormatter: errorFormatter }), isLoading && jobs.length === 0 && (jsx("div", { className: "border border-input rounded-lg overflow-hidden", children: jsxs("table", { className: "w-full border-collapse text-sm", children: [jsx("thead", { children: jsxs("tr", { children: [jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap", children: "Job Name" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Status" }), jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap", children: "Message" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Duration" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Actions" })] }) }), jsxs("tbody", { children: [jsx(ReindexJobSkeletonRow, {}), jsx(ReindexJobSkeletonRow, {}), jsx(ReindexJobSkeletonRow, {})] })] }) })), !isLoading && !error && jobs.length === 0 && (jsxs("div", { className: "text-center py-8 px-6 text-muted-foreground", children: [jsx("div", { className: "text-4xl mb-3", children: "\uD83D\uDD04" }), jsx("h3", { className: "m-0 mb-1.5 text-foreground", children: "No reindex jobs found" }), jsx("p", { className: "m-0 mb-4 max-w-[400px] mx-auto", children: "Create a reindex job to re-process historical audit logs and events through current ActivityPolicy rules." }), onCreateJob && (jsx(Button, { size: "small", onClick: onCreateJob, className: "bg-primary text-primary-foreground hover:bg-primary/90 h-7 text-xs", children: "Create your first reindex job" }))] })), sortedJobs.length > 0 && (jsx("div", { className: "border border-input rounded-lg overflow-hidden", children: jsxs("table", { className: "w-full border-collapse text-sm", children: [jsx("thead", { children: jsxs("tr", { children: [jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap", children: "Job Name" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Status" }), jsx("th", { className: "text-left px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap", children: "Message" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Duration" }), jsx("th", { className: "text-center px-3 py-2 bg-muted text-muted-foreground font-medium border-b border-input text-xs whitespace-nowrap w-24", children: "Actions" })] }) }), jsx("tbody", { children: sortedJobs.map((job) => {
14774
15151
  const duration = getReindexJobDuration(job);
14775
15152
  return (jsxs("tr", { className: "hover:bg-muted transition-colors", children: [jsx("td", { className: "px-3 py-2 border-b border-border last:border-b-0", children: jsx("span", { className: "text-xs font-medium font-mono", children: job.metadata?.name }) }), jsx("td", { className: "px-3 py-2 border-b border-border last:border-b-0 text-center", children: jsxs(Badge, { variant: getPhaseBadgeVariant(job.status?.phase), className: "inline-flex items-center gap-1", children: [getPhaseIcon$1(job.status?.phase), job.status?.phase || 'Unknown'] }) }), jsx("td", { className: "px-3 py-2 border-b border-border last:border-b-0", children: job.status?.phase === 'Failed' ? (jsx("span", { className: "text-xs text-destructive", children: job.status?.message || 'Job failed' })) : (jsx("span", { className: "text-xs text-muted-foreground", children: job.status?.message || 'Waiting to start' })) }), jsx("td", { className: "px-3 py-2 border-b border-border last:border-b-0 text-center", children: jsx("span", { className: "text-xs text-muted-foreground", children: duration || '-' }) }), jsx("td", { className: "px-3 py-2 border-b border-border last:border-b-0 text-center", children: onViewJob && (jsx(Button, { type: "quaternary", theme: "borderless", size: "small", onClick: () => onViewJob(job.metadata?.name || ''), className: "h-7 px-2", title: "View job details", children: jsx(Eye, { className: "h-3.5 w-3.5" }) })) })] }, job.metadata?.name));
14776
15153
  }) })] }) }))] })] }));
@@ -14782,11 +15159,11 @@ function ReindexJobList({ client, onViewJob, onCreateJob, watch = true, classNam
14782
15159
  function getPhaseIcon(phase) {
14783
15160
  switch (phase) {
14784
15161
  case 'Succeeded':
14785
- return jsx(CircleCheck, { className: "h-8 w-8 text-green-500" });
15162
+ return jsx(CircleCheck, { className: "h-8 w-8 text-[var(--success-500)]" });
14786
15163
  case 'Failed':
14787
- return jsx(CircleX, { className: "h-8 w-8 text-red-500" });
15164
+ return jsx(CircleX, { className: "h-8 w-8 text-destructive" });
14788
15165
  case 'Running':
14789
- return jsx(LoaderCircle, { className: "h-8 w-8 text-blue-500 animate-spin" });
15166
+ return jsx(LoaderCircle, { className: "h-8 w-8 text-[var(--info-500)] animate-spin" });
14790
15167
  case 'Pending':
14791
15168
  default:
14792
15169
  return jsx(Clock, { className: "h-8 w-8 text-muted-foreground" });
@@ -14867,7 +15244,7 @@ function ReindexJobDetailView({ client, jobName, onDelete, watch = true, classNa
14867
15244
  const duration = job ? getReindexJobDuration(job) : null;
14868
15245
  const statusMessage = job ? getReindexJobStatusMessage(job) : '';
14869
15246
  const isTerminal = job ? isReindexJobTerminal(job) : false;
14870
- return (jsxs(Card, { className: `rounded-xl max-w-md ${className}`, children: [jsxs(CardHeader, { className: "flex flex-row justify-between items-start p-6 border-b border-border space-y-0", children: [jsxs("div", { className: "flex items-center gap-4", children: [isLoading ? (jsx(Skeleton, { className: "h-8 w-8 rounded-full" })) : (getPhaseIcon(job?.status?.phase)), jsx("div", { children: isLoading ? (jsxs(Fragment, { children: [jsx(Skeleton, { className: "h-5 w-32 mb-2" }), jsx(Skeleton, { className: "h-3 w-24" })] })) : (jsxs(Fragment, { children: [jsx("h2", { className: "text-lg font-semibold text-foreground m-0", children: statusMessage }), jsxs("p", { className: "text-xs text-muted-foreground m-0", children: [duration && jsx(Fragment, { children: duration }), isWatching && !isTerminal && (jsx(Badge, { variant: "outline", className: "ml-2 text-xs text-green-600 border-green-300", children: "Live" }))] })] })) })] }), isTerminal && onDelete && (jsx(Button, { type: "quaternary", theme: "borderless", size: "small", onClick: handleDelete, disabled: isDeleting, className: "h-8 w-8 p-0", children: isDeleting ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsx(Trash2, { className: "h-4 w-4" })) }))] }), jsx(ApiErrorAlert, { error: error, onRetry: loadJob, className: "mx-6 mt-4", errorFormatter: errorFormatter }), !isLoading && job && (jsxs(CardContent, { className: "p-6 space-y-4", children: [job.status?.phase === 'Failed' && job.status?.message && (jsx("div", { className: "p-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700", children: job.status.message })), jsxs("div", { children: [jsx("div", { className: "text-xs text-muted-foreground mb-1", children: "Time Range" }), jsxs("div", { className: "text-sm font-mono", children: [job.spec.timeRange.startTime, job.spec.timeRange.endTime && jsxs(Fragment, { children: [" \u2192 ", job.spec.timeRange.endTime] })] })] }), job.spec.policySelector?.names && (jsxs("div", { children: [jsx("div", { className: "text-xs text-muted-foreground mb-1", children: "Policy" }), jsx("div", { className: "flex flex-wrap gap-1", children: job.spec.policySelector.names.map((name) => (jsx(Badge, { variant: "secondary", className: "text-xs font-mono", children: name }, name))) })] }))] }))] }));
15247
+ return (jsxs(Card, { className: `rounded-xl max-w-md ${className}`, children: [jsxs(CardHeader, { className: "flex flex-row justify-between items-start p-6 border-b border-border space-y-0", children: [jsxs("div", { className: "flex items-center gap-4", children: [isLoading ? (jsx(Skeleton, { className: "h-8 w-8 rounded-full" })) : (getPhaseIcon(job?.status?.phase)), jsx("div", { children: isLoading ? (jsxs(Fragment, { children: [jsx(Skeleton, { className: "h-5 w-32 mb-2" }), jsx(Skeleton, { className: "h-3 w-24" })] })) : (jsxs(Fragment, { children: [jsx("h2", { className: "text-lg font-semibold text-foreground m-0", children: statusMessage }), jsxs("p", { className: "text-xs text-muted-foreground m-0", children: [duration && jsx(Fragment, { children: duration }), isWatching && !isTerminal && (jsx(Badge, { variant: "outline", className: "ml-2 text-xs text-[var(--success-500)] border-[var(--success-300)]", children: "Live" }))] })] })) })] }), isTerminal && onDelete && (jsx(Button, { type: "quaternary", theme: "borderless", size: "small", onClick: handleDelete, disabled: isDeleting, className: "h-8 w-8 p-0", children: isDeleting ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsx(Trash2, { className: "h-4 w-4" })) }))] }), jsx(ApiErrorAlert, { error: error, onRetry: loadJob, className: "mx-6 mt-4", errorFormatter: errorFormatter }), !isLoading && job && (jsxs(CardContent, { className: "p-6 space-y-4", children: [job.status?.phase === 'Failed' && job.status?.message && (jsx("div", { className: "p-3 bg-destructive/10 border border-destructive/30 rounded-lg text-sm text-destructive", children: job.status.message })), jsxs("div", { children: [jsx("div", { className: "text-xs text-muted-foreground mb-1", children: "Time Range" }), jsxs("div", { className: "text-sm font-mono", children: [job.spec.timeRange.startTime, job.spec.timeRange.endTime && jsxs(Fragment, { children: [" \u2192 ", job.spec.timeRange.endTime] })] })] }), job.spec.policySelector?.names && (jsxs("div", { children: [jsx("div", { className: "text-xs text-muted-foreground mb-1", children: "Policy" }), jsx("div", { className: "flex flex-wrap gap-1", children: job.spec.policySelector.names.map((name) => (jsx(Badge, { variant: "secondary", className: "text-xs font-mono", children: name }, name))) })] }))] }))] }));
14871
15248
  }
14872
15249
 
14873
15250
  const TIME_RANGE_OPTIONS$1 = [
@@ -14970,76 +15347,11 @@ function ReindexJobDialog({ client, policyName, open, onOpenChange, onSuccess, e
14970
15347
  return (jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: jsxs(DialogContent, { className: "sm:max-w-sm", children: [jsx(DialogHeader, { children: jsxs("div", { className: "flex items-center gap-3", children: [jsx("div", { className: "p-2 bg-primary/10 rounded-lg", children: jsx(RefreshCw, { className: "h-5 w-5 text-primary" }) }), jsxs("div", { children: [jsx(DialogTitle, { children: "Reindex Policy" }), jsxs(DialogDescription, { className: "text-xs", children: ["Re-process events for \"", policyName, "\""] })] })] }) }), jsxs("div", { className: "space-y-4 py-2", children: [jsx(ApiErrorAlert, { error: error, errorFormatter: errorFormatter }), jsxs("div", { children: [jsx("label", { className: "block text-sm font-medium text-foreground mb-2", children: "Time range" }), jsxs(Select, { value: selectedRange, onValueChange: setSelectedRange, children: [jsx(SelectTrigger, { className: "w-full", children: jsx(SelectValue, { placeholder: "Select time range" }) }), jsx(SelectContent, { children: TIME_RANGE_OPTIONS.map((option) => (jsx(SelectItem, { value: option.value, children: option.label }, option.value))) })] })] }), jsxs("p", { className: "text-sm text-muted-foreground", children: ["Events from ", selectedOption?.label.toLowerCase(), " will be re-processed."] })] }), jsxs("div", { className: "flex justify-end gap-2", children: [jsx(Button, { htmlType: "button", type: "tertiary", theme: "outline", onClick: () => onOpenChange(false), disabled: isSubmitting, children: "Cancel" }), jsx(Button, { htmlType: "button", onClick: handleSubmit, disabled: isSubmitting, children: isSubmitting ? (jsxs(Fragment, { children: [jsx(LoaderCircle, { className: "h-4 w-4 mr-2 animate-spin" }), "Starting..."] })) : ('Start Reindex') })] })] }) }));
14971
15348
  }
14972
15349
 
14973
- /**
14974
- * Multi-select combobox component with type-ahead search and fuzzy matching.
14975
- * Built on cmdk for search functionality and Radix Popover for positioning.
14976
- */
14977
- function MultiCombobox({ options, values, onValuesChange, placeholder = 'Select...', searchPlaceholder = 'Search...', emptyMessage = 'No results found.', disabled = false, loading = false, className, maxDisplayed = 2, }) {
14978
- const [open, setOpen] = React.useState(false);
14979
- const [search, setSearch] = React.useState('');
14980
- // Find selected options for display
14981
- const selectedOptions = options.filter((opt) => values.includes(opt.value));
14982
- const displayValue = React.useMemo(() => {
14983
- if (loading)
14984
- return 'Loading...';
14985
- if (selectedOptions.length === 0)
14986
- return placeholder;
14987
- if (selectedOptions.length <= maxDisplayed) {
14988
- return selectedOptions.map((opt) => opt.label).join(', ');
14989
- }
14990
- return `${selectedOptions.slice(0, maxDisplayed).map((opt) => opt.label).join(', ')} +${selectedOptions.length - maxDisplayed} more`;
14991
- }, [loading, selectedOptions, placeholder, maxDisplayed]);
14992
- const handleSelect = React.useCallback((selectedValue) => {
14993
- if (values.includes(selectedValue)) {
14994
- // Remove from selection
14995
- onValuesChange(values.filter((v) => v !== selectedValue));
14996
- }
14997
- else {
14998
- // Add to selection
14999
- onValuesChange([...values, selectedValue]);
15000
- }
15001
- }, [onValuesChange, values]);
15002
- const handleClear = React.useCallback((e) => {
15003
- e.stopPropagation();
15004
- onValuesChange([]);
15005
- }, [onValuesChange]);
15006
- const handleRemove = React.useCallback((e, valueToRemove) => {
15007
- e.stopPropagation();
15008
- onValuesChange(values.filter((v) => v !== valueToRemove));
15009
- }, [onValuesChange, values]);
15010
- // Custom fuzzy filter function
15011
- const filterOptions = React.useCallback((optionValue, searchQuery) => {
15012
- if (!searchQuery)
15013
- return 1;
15014
- const option = options.find((o) => o.value === optionValue);
15015
- const label = option?.label || optionValue;
15016
- const lowerLabel = label.toLowerCase();
15017
- const lowerSearch = searchQuery.toLowerCase();
15018
- // Exact match
15019
- if (lowerLabel === lowerSearch)
15020
- return 1;
15021
- // Starts with
15022
- if (lowerLabel.startsWith(lowerSearch))
15023
- return 0.9;
15024
- // Contains
15025
- if (lowerLabel.includes(lowerSearch))
15026
- return 0.8;
15027
- // Fuzzy match - check if all search chars appear in order
15028
- let searchIdx = 0;
15029
- for (let i = 0; i < lowerLabel.length && searchIdx < lowerSearch.length; i++) {
15030
- if (lowerLabel[i] === lowerSearch[searchIdx]) {
15031
- searchIdx++;
15032
- }
15033
- }
15034
- if (searchIdx === lowerSearch.length)
15035
- return 0.6;
15036
- return 0;
15037
- }, [options]);
15038
- return (jsxs(Popover.Root, { open: open, onOpenChange: setOpen, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs("button", { type: "button", role: "combobox", "aria-expanded": open, "aria-haspopup": "listbox", disabled: disabled || loading, className: cn('flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background', 'placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', 'disabled:cursor-not-allowed disabled:opacity-50', 'whitespace-nowrap', className), children: [jsx("span", { className: "truncate text-left flex-1", children: displayValue }), jsxs("div", { className: "flex items-center gap-1 ml-2", children: [values.length > 0 && !disabled && (jsx("span", { role: "button", tabIndex: 0, onClick: handleClear, onKeyDown: (e) => {
15039
- if (e.key === 'Enter' || e.key === ' ') {
15040
- handleClear(e);
15041
- }
15042
- }, className: "rounded-sm opacity-50 hover:opacity-100 cursor-pointer", children: jsx(X, { className: "h-3 w-3" }) })), jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50" })] })] }) }), jsx(Popover.Portal, { children: jsx(Popover.Content, { className: cn('z-50 min-w-[var(--radix-popover-trigger-width)] overflow-hidden rounded-md border shadow-md', 'bg-white dark:bg-slate-900 text-foreground', 'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2'), sideOffset: 4, align: "start", children: jsxs(Command, { filter: filterOptions, className: "w-full", children: [jsx("div", { className: "flex items-center border-b px-3", children: jsx(CommandInput, { placeholder: searchPlaceholder, value: search, onValueChange: setSearch, className: "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50" }) }), values.length > 0 && (jsx("div", { className: "flex flex-wrap gap-1 p-2 border-b", children: selectedOptions.map((option) => (jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-secondary text-secondary-foreground text-xs", children: [option.label, jsx("button", { type: "button", onClick: (e) => handleRemove(e, option.value), className: "rounded-sm hover:bg-secondary-foreground/20", children: jsx(X, { className: "h-3 w-3" }) })] }, option.value))) })), jsxs(CommandList, { className: "max-h-[300px] overflow-y-auto p-1", children: [jsx(CommandEmpty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }), jsx(CommandGroup, { children: options.map((option) => (jsxs(CommandItem, { value: option.value, onSelect: () => handleSelect(option.value), className: cn('relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', 'data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground', 'hover:bg-accent hover:text-accent-foreground'), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', values.includes(option.value) ? 'opacity-100' : 'opacity-0') }), jsx("span", { className: "flex-1 truncate", children: option.label }), option.count !== undefined && (jsxs("span", { className: "ml-2 text-xs text-muted-foreground", children: ["(", option.count, ")"] }))] }, option.value))) })] })] }) }) })] }));
15350
+ function MultiCombobox({ options, values, onValuesChange, placeholder = 'Select…',
15351
+ // searchPlaceholder is accepted for API compatibility but MultiSelect has no counterpart.
15352
+ emptyMessage = 'No results', disabled, loading, className, maxDisplayed = 3, }) {
15353
+ const items = React.useMemo(() => options.map((o) => ({ value: o.value, label: o.label })), [options]);
15354
+ return (jsx(MultiSelect, { className: className, options: items, value: values, onValueChange: onValuesChange, placeholder: placeholder, emptyContent: emptyMessage, isLoading: loading, disabled: disabled, maxCount: maxDisplayed }));
15043
15355
  }
15044
15356
 
15045
15357
  /**
@@ -15749,7 +16061,7 @@ class ActivityApiClient {
15749
16061
  const reader = response.body.getReader();
15750
16062
  const decoder = new TextDecoder();
15751
16063
  let buffer = '';
15752
- while (true) {
16064
+ for (;;) {
15753
16065
  const { done, value } = await reader.read();
15754
16066
  if (done) {
15755
16067
  options?.onClose?.();
@@ -16149,7 +16461,7 @@ class ActivityApiClient {
16149
16461
  const reader = response.body.getReader();
16150
16462
  const decoder = new TextDecoder();
16151
16463
  let buffer = '';
16152
- while (true) {
16464
+ for (;;) {
16153
16465
  const { done, value } = await reader.read();
16154
16466
  if (done) {
16155
16467
  options?.onClose?.();
@@ -16265,7 +16577,7 @@ class ActivityApiClient {
16265
16577
  const reader = response.body.getReader();
16266
16578
  const decoder = new TextDecoder();
16267
16579
  let buffer = '';
16268
- while (true) {
16580
+ for (;;) {
16269
16581
  const { done, value } = await reader.read();
16270
16582
  if (done) {
16271
16583
  options?.onClose?.();
@@ -16501,5 +16813,5 @@ const POLICY_FILTER_FIELDS = [
16501
16813
  },
16502
16814
  ];
16503
16815
 
16504
- export { ACTIVITY_FILTER_FIELDS, AUDIT_TEMPLATES, ActionMultiSelect, ActionToggle, ActivityApiClient, ActivityExpandedDetails, ActivityFeed, ActivityFeedFilters, ActivityFeedItem, ActivityFeedItemSkeleton, ActivityFeedSummary, ActivityLayout, ApiError, ApiErrorAlert, AuditEventViewer, AuditLogExpandedDetails, AuditLogFeedItem, AuditLogFilters, AuditLogQueryComponent, Badge, CelEditor, ChangeSourceToggle, Combobox, DateTimeRangePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, EVENT_FACET_FIELDS, EVENT_FILTER_FIELDS, EVENT_TEMPLATES, EventExpandedDetails, EventFeedItem, EventTypeToggle, EventsFeed, FILTER_FIELDS, FilterBuilder, FilterBuilderWithAutocomplete, MultiCombobox, NetworkError, POLICY_FILTER_FIELDS, PolicyActivityView, PolicyActivityViewSkeleton, PolicyDetailView, PolicyEditView, PolicyEditor, PolicyList, PolicyPreviewPanel, PolicyPreviewResult, PolicyResourceForm, PolicyRuleEditor, PolicyRuleEditorDialog, PolicyRuleList, PolicyRuleListItem, ReindexJobCreate, ReindexJobDetailView, ReindexJobDialog, ReindexJobList, ResourceHistoryView, RulePreviewPanel, SampleInputTemplates, SimpleQueryBuilder, TenantBadge, TimeRangeDropdown, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserSelect, badgeVariants, buildAuditLogCEL, cn, copyToClipboard, defaultErrorFormatter, defaultResourceLinkResolver, extractEvent, extractFieldPaths, extractFieldPathsFromMany, generateShareableUrl, getReindexJobDuration, getReindexJobStatusMessage, isEventRecord, isReindexJobRunning, isReindexJobTerminal, parseActivityFilters, parseApiError, parseEventFilters, parseTimeRange, serializeActivityFilters, serializeEventFilters, useActivityFeed, useAuditLogFacets, useAuditLogQuery, useEventFacets, useEventsFeed, useFacets, usePolicyEditor, usePolicyList, usePolicyPreview, useReindexJobs };
16816
+ export { ACTIVITY_FILTER_FIELDS, AUDIT_TEMPLATES, ActionMultiSelect, ActionToggle, ActivityApiClient, ActivityDigestSummary, ActivityExpandedDetails, ActivityFeed, ActivityFeedFilters, ActivityFeedGroupItem, ActivityFeedItem, ActivityFeedItemSkeleton, ActivityFeedSummary, ActivityLayout, ApiError, ApiErrorAlert, AuditEventViewer, AuditLogExpandedDetails, AuditLogFeedItem, AuditLogFilters, AuditLogQueryComponent, Badge, CelEditor, ChangeSourceToggle, Combobox, DateTimeRangePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, EVENT_FACET_FIELDS, EVENT_FILTER_FIELDS, EVENT_TEMPLATES, EventExpandedDetails, EventFeedItem, EventTypeToggle, EventsFeed, FILTER_FIELDS, FilterBuilder, FilterBuilderWithAutocomplete, MultiCombobox, NetworkError, POLICY_FILTER_FIELDS, PolicyActivityView, PolicyActivityViewSkeleton, PolicyDetailView, PolicyEditView, PolicyEditor, PolicyList, PolicyPreviewPanel, PolicyPreviewResult, PolicyResourceForm, PolicyRuleEditor, PolicyRuleEditorDialog, PolicyRuleList, PolicyRuleListItem, ReindexJobCreate, ReindexJobDetailView, ReindexJobDialog, ReindexJobList, ResourceHistoryView, RulePreviewPanel, SampleInputTemplates, SimpleQueryBuilder, TenantBadge, TimeRangeDropdown, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserSelect, badgeVariants, buildAuditLogCEL, cn, copyToClipboard, defaultErrorFormatter, defaultResourceLinkResolver, extractEvent, extractFieldPaths, extractFieldPathsFromMany, generateShareableUrl, getReindexJobDuration, getReindexJobStatusMessage, groupActivities, groupActivitiesByDay, isEventRecord, isReindexJobRunning, isReindexJobTerminal, parseActivityFilters, parseApiError, parseEventFilters, parseTimeRange, serializeActivityFilters, serializeEventFilters, useActivityFeed, useAuditLogFacets, useAuditLogQuery, useEventFacets, useEventsFeed, useFacets, usePolicyEditor, usePolicyList, usePolicyPreview, useReindexJobs };
16505
16817
  //# sourceMappingURL=index.esm.js.map