@datum-cloud/activity-ui 0.5.1 → 0.6.0-dev.5b51180
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/components/ActionMultiSelect.d.ts.map +1 -1
- package/dist/components/ActivityDigestSummary.d.ts +15 -0
- package/dist/components/ActivityDigestSummary.d.ts.map +1 -0
- package/dist/components/ActivityFeed.d.ts +11 -2
- package/dist/components/ActivityFeed.d.ts.map +1 -1
- package/dist/components/ActivityFeedFilters.d.ts +9 -1
- package/dist/components/ActivityFeedFilters.d.ts.map +1 -1
- package/dist/components/ActivityFeedGroupItem.d.ts +23 -0
- package/dist/components/ActivityFeedGroupItem.d.ts.map +1 -0
- package/dist/components/ActivityFeedItem.d.ts +8 -1
- package/dist/components/ActivityFeedItem.d.ts.map +1 -1
- package/dist/components/AuditLogFeedItem.d.ts.map +1 -1
- package/dist/components/CelEditor.d.ts +1 -1
- package/dist/components/CelEditor.d.ts.map +1 -1
- package/dist/components/ChangeSourceToggle.d.ts.map +1 -1
- package/dist/components/DateTimeRangePicker.d.ts.map +1 -1
- package/dist/components/EventFeedItem.d.ts.map +1 -1
- package/dist/components/EventTypeToggle.d.ts.map +1 -1
- package/dist/components/FilterBuilder.d.ts.map +1 -1
- package/dist/components/FilterBuilderWithAutocomplete.d.ts.map +1 -1
- package/dist/components/PolicyDetailView.d.ts.map +1 -1
- package/dist/components/PolicyEditView.d.ts.map +1 -1
- package/dist/components/PolicyEditor.d.ts.map +1 -1
- package/dist/components/UserSelect.d.ts.map +1 -1
- package/dist/components/VerbToggle.d.ts.map +1 -1
- package/dist/components/ui/add-filter-dropdown.d.ts.map +1 -1
- package/dist/components/ui/combobox.d.ts +0 -4
- package/dist/components/ui/combobox.d.ts.map +1 -1
- package/dist/components/ui/filter-chip.d.ts.map +1 -1
- package/dist/components/ui/multi-combobox.d.ts +2 -5
- package/dist/components/ui/multi-combobox.d.ts.map +1 -1
- package/dist/components/ui/segmented-toggle.d.ts +26 -0
- package/dist/components/ui/segmented-toggle.d.ts.map +1 -0
- package/dist/components/ui/time-range-dropdown.d.ts +13 -4
- package/dist/components/ui/time-range-dropdown.d.ts.map +1 -1
- package/dist/hooks/useAuditLogQuery.d.ts +1 -1
- package/dist/hooks/useAuditLogQuery.d.ts.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +1524 -1271
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1527 -1271
- package/dist/index.js.map +1 -1
- package/dist/lib/groupActivities.d.ts +40 -0
- package/dist/lib/groupActivities.d.ts.map +1 -0
- package/dist/lib/verb.d.ts +22 -0
- package/dist/lib/verb.d.ts.map +1 -0
- package/package.json +7 -12
- package/dist/components/ui/alert.d.ts +0 -9
- package/dist/components/ui/alert.d.ts.map +0 -1
- package/dist/components/ui/button.d.ts +0 -12
- package/dist/components/ui/button.d.ts.map +0 -1
- package/dist/components/ui/card.d.ts +0 -9
- package/dist/components/ui/card.d.ts.map +0 -1
- package/dist/components/ui/checkbox.d.ts +0 -5
- package/dist/components/ui/checkbox.d.ts.map +0 -1
- package/dist/components/ui/input.d.ts +0 -6
- package/dist/components/ui/input.d.ts.map +0 -1
- package/dist/components/ui/label.d.ts +0 -8
- package/dist/components/ui/label.d.ts.map +0 -1
- package/dist/components/ui/select.d.ts +0 -14
- package/dist/components/ui/select.d.ts.map +0 -1
- package/dist/components/ui/separator.d.ts +0 -5
- package/dist/components/ui/separator.d.ts.map +0 -1
- package/dist/components/ui/sheet.d.ts +0 -23
- package/dist/components/ui/sheet.d.ts.map +0 -1
- package/dist/components/ui/skeleton.d.ts +0 -3
- package/dist/components/ui/skeleton.d.ts.map +0 -1
- package/dist/components/ui/tabs.d.ts +0 -8
- package/dist/components/ui/tabs.d.ts.map +0 -1
- package/dist/components/ui/textarea.d.ts +0 -6
- 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 {
|
|
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: "
|
|
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 = /(
|
|
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-
|
|
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,559 +4113,323 @@ function subMinutes(date, amount, options) {
|
|
|
4007
4113
|
return addMinutes(date, -15, options);
|
|
4008
4114
|
}
|
|
4009
4115
|
|
|
4010
|
-
/**
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
}).
|
|
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
|
-
*
|
|
4092
|
-
*
|
|
4093
|
-
*
|
|
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
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
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
|
-
|
|
4123
|
-
|
|
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
|
-
*
|
|
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
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
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
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
const
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
const
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
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
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
*/
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
const
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
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"
|
|
4366
|
-
}
|
|
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"
|
|
4397
|
-
}
|
|
4398
|
-
],
|
|
4399
|
-
["path", { d: "m15 5 4 4", key: "1mk7zo" }]
|
|
4400
|
-
];
|
|
4401
|
-
const Pencil = createLucideIcon("pencil", __iconNode$9);
|
|
4402
|
-
|
|
4403
|
-
/**
|
|
4404
|
-
* @license lucide-react v0.577.0 - ISC
|
|
4405
|
-
*
|
|
4406
|
-
* This source code is licensed under the ISC license.
|
|
4407
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4408
|
-
*/
|
|
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
|
-
};
|
|
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
|
+
};
|
|
4563
4433
|
|
|
4564
4434
|
// Factory function ensures consistent object shapes
|
|
4565
4435
|
const createClassValidatorObject = (classGroupId, validator) => ({
|
|
@@ -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
|
|
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/
|
|
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-
|
|
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-
|
|
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/
|
|
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-
|
|
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-
|
|
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
|
-
'',
|
|
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,6 +7280,13 @@ const getDefaultConfig = () => {
|
|
|
7364
7280
|
* @see https://tailwindcss.com/docs/translate
|
|
7365
7281
|
*/
|
|
7366
7282
|
'translate-none': ['translate-none'],
|
|
7283
|
+
/**
|
|
7284
|
+
* Zoom
|
|
7285
|
+
* @see https://tailwindcss.com/docs/zoom
|
|
7286
|
+
*/
|
|
7287
|
+
zoom: [{
|
|
7288
|
+
zoom: [isInteger, isArbitraryVariable, isArbitraryValue]
|
|
7289
|
+
}],
|
|
7367
7290
|
// ---------------------
|
|
7368
7291
|
// --- Interactivity ---
|
|
7369
7292
|
// ---------------------
|
|
@@ -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,485 +7692,684 @@ const getDefaultConfig = () => {
|
|
|
7740
7692
|
conflictingClassGroupModifiers: {
|
|
7741
7693
|
'font-size': ['leading']
|
|
7742
7694
|
},
|
|
7743
|
-
|
|
7744
|
-
|
|
7695
|
+
postfixLookupClassGroups: ['container-type'],
|
|
7696
|
+
orderSensitiveModifiers: ['*', '**', 'after', 'backdrop', 'before', 'details-content', 'file', 'first-letter', 'first-line', 'marker', 'placeholder', 'selection']
|
|
7697
|
+
};
|
|
7698
|
+
};
|
|
7699
|
+
const twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);
|
|
7700
|
+
|
|
7701
|
+
function cn(...inputs) {
|
|
7702
|
+
return twMerge(clsx(inputs));
|
|
7703
|
+
}
|
|
7704
|
+
|
|
7705
|
+
/**
|
|
7706
|
+
* Get icon for tenant type
|
|
7707
|
+
*/
|
|
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
|
+
}
|
|
7738
|
+
}
|
|
7739
|
+
/**
|
|
7740
|
+
* TenantBadge displays tenant information in a compact badge format
|
|
7741
|
+
* Renders as a clickable link if tenantLinkResolver is provided and returns a URL
|
|
7742
|
+
*/
|
|
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;
|
|
7752
|
+
}
|
|
7753
|
+
|
|
7754
|
+
/**
|
|
7755
|
+
* Extract tenant information from audit event annotations if present
|
|
7756
|
+
* Expected annotations: tenant.type and tenant.name
|
|
7757
|
+
*/
|
|
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
|
+
}
|
|
7777
|
+
}
|
|
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);
|
|
7790
|
+
}
|
|
7791
|
+
else {
|
|
7792
|
+
newExpanded.add(auditId);
|
|
7793
|
+
}
|
|
7794
|
+
setExpandedEvents(newExpanded);
|
|
7795
|
+
};
|
|
7796
|
+
const handleEventClick = (event) => {
|
|
7797
|
+
setSelectedEvent(event);
|
|
7798
|
+
if (onEventSelect) {
|
|
7799
|
+
onEventSelect(event);
|
|
7800
|
+
}
|
|
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");
|
|
7807
|
+
}
|
|
7808
|
+
catch {
|
|
7809
|
+
return timestamp;
|
|
7810
|
+
}
|
|
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";
|
|
7827
|
+
}
|
|
7828
|
+
};
|
|
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
|
+
}) }) }));
|
|
7844
|
+
}
|
|
7845
|
+
|
|
7846
|
+
/**
|
|
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;
|
|
7745
7987
|
};
|
|
7746
|
-
const twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);
|
|
7747
7988
|
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
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);
|
|
7751
8007
|
|
|
7752
8008
|
/**
|
|
7753
|
-
*
|
|
7754
|
-
*
|
|
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.
|
|
7755
8013
|
*/
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
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);
|
|
7811
8100
|
|
|
7812
8101
|
/**
|
|
7813
|
-
*
|
|
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.
|
|
7814
8106
|
*/
|
|
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" })] })] })) }) })] }));
|
|
7848
|
-
}
|
|
7849
8107
|
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
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
|
-
},
|
|
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" }]
|
|
7899
8112
|
];
|
|
7900
|
-
|
|
7901
|
-
|
|
8113
|
+
const CircleCheck = createLucideIcon("circle-check", __iconNode$g);
|
|
8114
|
+
|
|
7902
8115
|
/**
|
|
7903
|
-
*
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
}
|
|
7914
|
-
|
|
7915
|
-
|
|
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);
|
|
7936
|
-
}
|
|
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);
|
|
7945
|
-
}
|
|
7946
|
-
}, []);
|
|
7947
|
-
const generateCEL = useCallback(() => {
|
|
7948
|
-
const parts = [];
|
|
7949
|
-
if (filters.verb) {
|
|
7950
|
-
parts.push(`verb == "${filters.verb}"`);
|
|
7951
|
-
}
|
|
7952
|
-
if (filters.resourceType) {
|
|
7953
|
-
parts.push(`objectRef.resource == "${filters.resourceType}"`);
|
|
7954
|
-
}
|
|
7955
|
-
if (filters.namespace) {
|
|
7956
|
-
parts.push(`objectRef.namespace == "${filters.namespace}"`);
|
|
7957
|
-
}
|
|
7958
|
-
if (filters.resourceName) {
|
|
7959
|
-
parts.push(`objectRef.name.contains("${filters.resourceName}")`);
|
|
7960
|
-
}
|
|
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';
|
|
8051
|
-
}
|
|
8052
|
-
const preset = TIME_PRESETS$3.find(p => p.key === selectedPreset);
|
|
8053
|
-
return preset?.label || 'Select time range';
|
|
8054
|
-
};
|
|
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 })] }))] }))] }));
|
|
8080
|
-
}
|
|
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);
|
|
8081
8129
|
|
|
8082
8130
|
/**
|
|
8083
|
-
*
|
|
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.
|
|
8084
8135
|
*/
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
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.
|
|
8149
|
+
*/
|
|
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
|
+
|
|
8158
|
+
/**
|
|
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.
|
|
8163
|
+
*/
|
|
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"
|
|
8097
8172
|
}
|
|
8098
|
-
|
|
8173
|
+
],
|
|
8174
|
+
["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
|
|
8175
|
+
];
|
|
8176
|
+
const Eye = createLucideIcon("eye", __iconNode$c);
|
|
8177
|
+
|
|
8099
8178
|
/**
|
|
8100
|
-
*
|
|
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.
|
|
8101
8183
|
*/
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
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"
|
|
8114
8203
|
}
|
|
8115
|
-
|
|
8204
|
+
],
|
|
8205
|
+
["path", { d: "m15 5 4 4", key: "1mk7zo" }]
|
|
8206
|
+
];
|
|
8207
|
+
const Pencil = createLucideIcon("pencil", __iconNode$a);
|
|
8208
|
+
|
|
8116
8209
|
/**
|
|
8117
|
-
*
|
|
8118
|
-
*
|
|
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.
|
|
8119
8214
|
*/
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
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"
|
|
8127
8303
|
}
|
|
8128
|
-
|
|
8129
|
-
|
|
8304
|
+
]
|
|
8305
|
+
];
|
|
8306
|
+
const SquarePen = createLucideIcon("square-pen", __iconNode$4);
|
|
8130
8307
|
|
|
8131
8308
|
/**
|
|
8132
|
-
*
|
|
8133
|
-
*
|
|
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.
|
|
8134
8313
|
*/
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8153
|
-
|
|
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"
|
|
8154
8339
|
}
|
|
8155
|
-
|
|
8156
|
-
}
|
|
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
|
+
|
|
8157
8360
|
/**
|
|
8158
|
-
*
|
|
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.
|
|
8159
8365
|
*/
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
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" }) }));
|
|
8208
|
-
}
|
|
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
|
-
}
|
|
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(
|
|
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
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
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
|
|
8393
|
-
|
|
8394
|
-
|
|
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
|
|
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: "
|
|
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-
|
|
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-
|
|
8989
|
-
: 'text-
|
|
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
|
|
9150
|
+
* Get the badge variant for a verb
|
|
8999
9151
|
*/
|
|
9000
|
-
function
|
|
9001
|
-
|
|
9002
|
-
const normalized = verb?.toLowerCase();
|
|
9003
|
-
switch (normalized) {
|
|
9152
|
+
function getVerbBadgeVariant(verb) {
|
|
9153
|
+
switch (verb?.toLowerCase()) {
|
|
9004
9154
|
case 'create':
|
|
9005
|
-
return
|
|
9155
|
+
return 'success';
|
|
9006
9156
|
case 'update':
|
|
9007
9157
|
case 'patch':
|
|
9008
|
-
return
|
|
9158
|
+
return 'warning';
|
|
9009
9159
|
case 'delete':
|
|
9010
|
-
return
|
|
9160
|
+
return 'destructive';
|
|
9011
9161
|
default:
|
|
9012
|
-
return
|
|
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-
|
|
9173
|
+
return { icon: '✓', className: 'text-[var(--success-500)]' };
|
|
9024
9174
|
}
|
|
9025
|
-
return { icon: '✗', className: 'text-
|
|
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-
|
|
9067
|
-
verb === 'delete' ? 'bg-
|
|
9068
|
-
verb === 'update' || verb === 'patch' ? 'bg-
|
|
9069
|
-
'bg-
|
|
9070
|
-
const iconColor = verb === 'create' ? 'text-
|
|
9071
|
-
verb === 'delete' ? 'text-
|
|
9072
|
-
verb === 'update' || verb === 'patch' ? 'text-
|
|
9073
|
-
'text-
|
|
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-
|
|
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, {
|
|
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,
|
|
9245
|
+
function useAuditLogQuery({ client, }) {
|
|
9096
9246
|
const [query, setQuery] = useState(null);
|
|
9097
9247
|
const [events, setEvents] = useState([]);
|
|
9098
9248
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -10259,40 +10409,10 @@ function ActivityExpandedDetails({ activity, compact = false, }) {
|
|
|
10259
10409
|
const timestamp = metadata?.creationTimestamp;
|
|
10260
10410
|
const actorDisplay = actor.displayName || actor.name;
|
|
10261
10411
|
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-
|
|
10412
|
+
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
10413
|
return (jsx(TooltipProvider, { children: compact ? (jsx(DetailPanelShell, { children: body })) : (jsx("div", { className: "mt-4 pt-4 border-t border-border", children: body })) }));
|
|
10264
10414
|
}
|
|
10265
10415
|
|
|
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
10416
|
/**
|
|
10297
10417
|
* Extract verb from activity summary (e.g., "alice created HTTPProxy" -> "created")
|
|
10298
10418
|
*/
|
|
@@ -10301,24 +10421,24 @@ function extractVerb(summary) {
|
|
|
10301
10421
|
if (words.length >= 2) {
|
|
10302
10422
|
return words[1].toLowerCase();
|
|
10303
10423
|
}
|
|
10304
|
-
return
|
|
10424
|
+
return 'unknown';
|
|
10305
10425
|
}
|
|
10306
10426
|
/**
|
|
10307
|
-
* Normalize verb to a canonical form for coloring
|
|
10427
|
+
* Normalize a verb to a canonical form used for coloring/icons/grouping.
|
|
10308
10428
|
*/
|
|
10309
10429
|
function normalizeVerb(verb) {
|
|
10310
10430
|
const normalized = verb.toLowerCase();
|
|
10311
|
-
if (normalized.includes(
|
|
10312
|
-
return
|
|
10313
|
-
if (normalized.includes(
|
|
10314
|
-
return
|
|
10315
|
-
if (normalized.includes(
|
|
10316
|
-
normalized.includes(
|
|
10317
|
-
normalized.includes(
|
|
10318
|
-
normalized.includes(
|
|
10319
|
-
normalized.includes(
|
|
10320
|
-
return
|
|
10321
|
-
return
|
|
10431
|
+
if (normalized.includes('create') || normalized.includes('add'))
|
|
10432
|
+
return 'create';
|
|
10433
|
+
if (normalized.includes('delete') || normalized.includes('remove'))
|
|
10434
|
+
return 'delete';
|
|
10435
|
+
if (normalized.includes('update') ||
|
|
10436
|
+
normalized.includes('patch') ||
|
|
10437
|
+
normalized.includes('modify') ||
|
|
10438
|
+
normalized.includes('change') ||
|
|
10439
|
+
normalized.includes('edit'))
|
|
10440
|
+
return 'update';
|
|
10441
|
+
return 'other';
|
|
10322
10442
|
}
|
|
10323
10443
|
/**
|
|
10324
10444
|
* Get icon container + icon color classes based on verb
|
|
@@ -10326,25 +10446,25 @@ function normalizeVerb(verb) {
|
|
|
10326
10446
|
function getActionIconClasses(verb) {
|
|
10327
10447
|
const normalizedVerb = normalizeVerb(verb);
|
|
10328
10448
|
switch (normalizedVerb) {
|
|
10329
|
-
case
|
|
10449
|
+
case 'create':
|
|
10330
10450
|
return {
|
|
10331
|
-
container:
|
|
10332
|
-
icon:
|
|
10451
|
+
container: 'bg-blue-50 dark:bg-blue-950',
|
|
10452
|
+
icon: 'text-blue-500 dark:text-blue-400',
|
|
10333
10453
|
};
|
|
10334
|
-
case
|
|
10454
|
+
case 'update':
|
|
10335
10455
|
return {
|
|
10336
|
-
container:
|
|
10337
|
-
icon:
|
|
10456
|
+
container: 'bg-green-50 dark:bg-green-950',
|
|
10457
|
+
icon: 'text-green-600 dark:text-green-400',
|
|
10338
10458
|
};
|
|
10339
|
-
case
|
|
10459
|
+
case 'delete':
|
|
10340
10460
|
return {
|
|
10341
|
-
container:
|
|
10342
|
-
icon:
|
|
10461
|
+
container: 'bg-red-50 dark:bg-red-950',
|
|
10462
|
+
icon: 'text-red-500 dark:text-red-400',
|
|
10343
10463
|
};
|
|
10344
10464
|
default:
|
|
10345
10465
|
return {
|
|
10346
|
-
container:
|
|
10347
|
-
icon:
|
|
10466
|
+
container: 'bg-slate-100 dark:bg-slate-800',
|
|
10467
|
+
icon: 'text-slate-500 dark:text-slate-400',
|
|
10348
10468
|
};
|
|
10349
10469
|
}
|
|
10350
10470
|
}
|
|
@@ -10354,20 +10474,51 @@ function getActionIconClasses(verb) {
|
|
|
10354
10474
|
function getTimelineIcon(verb) {
|
|
10355
10475
|
const normalizedVerb = normalizeVerb(verb);
|
|
10356
10476
|
switch (normalizedVerb) {
|
|
10357
|
-
case
|
|
10477
|
+
case 'create':
|
|
10358
10478
|
return Plus;
|
|
10359
|
-
case
|
|
10479
|
+
case 'update':
|
|
10360
10480
|
return Pencil;
|
|
10361
|
-
case
|
|
10481
|
+
case 'delete':
|
|
10362
10482
|
return Trash2;
|
|
10363
10483
|
default:
|
|
10364
10484
|
return Activity;
|
|
10365
10485
|
}
|
|
10366
10486
|
}
|
|
10487
|
+
|
|
10488
|
+
// Number of columns rendered for the feed variant. Used as the colSpan on
|
|
10489
|
+
// the expanded-detail row so it stretches across the full width.
|
|
10490
|
+
const ACTIVITY_FEED_COLUMN_COUNT = 5;
|
|
10491
|
+
/**
|
|
10492
|
+
* Get avatar initials from actor name
|
|
10493
|
+
*/
|
|
10494
|
+
function getActorInitials(name) {
|
|
10495
|
+
const parts = name.split(/[@\s.]+/).filter(Boolean);
|
|
10496
|
+
if (parts.length === 0)
|
|
10497
|
+
return "?";
|
|
10498
|
+
if (parts.length === 1)
|
|
10499
|
+
return parts[0].charAt(0).toUpperCase();
|
|
10500
|
+
return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
|
|
10501
|
+
}
|
|
10502
|
+
/**
|
|
10503
|
+
* Get Tailwind classes for actor avatar based on actor type
|
|
10504
|
+
*/
|
|
10505
|
+
function getActorAvatarClasses(actorType, compact) {
|
|
10506
|
+
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");
|
|
10507
|
+
switch (actorType) {
|
|
10508
|
+
case "user":
|
|
10509
|
+
return cn(baseClasses, "bg-primary text-primary-foreground");
|
|
10510
|
+
case "controller":
|
|
10511
|
+
return cn(baseClasses, "bg-secondary text-secondary-foreground");
|
|
10512
|
+
case "machine account":
|
|
10513
|
+
return cn(baseClasses, "bg-muted text-muted-foreground");
|
|
10514
|
+
default:
|
|
10515
|
+
return cn(baseClasses, "bg-muted text-muted-foreground");
|
|
10516
|
+
}
|
|
10517
|
+
}
|
|
10367
10518
|
/**
|
|
10368
10519
|
* ActivityFeedItem renders a single activity in the feed or timeline
|
|
10369
10520
|
*/
|
|
10370
|
-
function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isSelected = false, className = "", compact = false, isNew = false, variant = "feed", isLast = false, defaultExpanded = false, }) {
|
|
10521
|
+
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
10522
|
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
|
10372
10523
|
const { spec, metadata } = activity;
|
|
10373
10524
|
const { actor, summary, links, tenant } = spec;
|
|
@@ -10388,7 +10539,7 @@ function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, ten
|
|
|
10388
10539
|
if (isTimeline) {
|
|
10389
10540
|
const { container: iconBg, icon: iconColor } = getActionIconClasses(verb);
|
|
10390
10541
|
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
|
|
10542
|
+
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
10543
|
}
|
|
10393
10544
|
// Feed variant — flat-column table row. Columns: Actor | Summary |
|
|
10394
10545
|
// Tenant | When | expand toggle. Renders an additional TableRow with a
|
|
@@ -10402,7 +10553,7 @@ function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, ten
|
|
|
10402
10553
|
// surfaced via a hover tooltip on the avatar so the column stays narrow.
|
|
10403
10554
|
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
10555
|
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-
|
|
10556
|
+
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
10557
|
// Row-level click toggles expansion. Inner interactive elements
|
|
10407
10558
|
// (links, avatar, expand button) call e.stopPropagation() so
|
|
10408
10559
|
// they don't double-fire. If a parent passed onActivityClick,
|
|
@@ -10573,6 +10724,18 @@ function useFacets(client, timeRange, filters = {}) {
|
|
|
10573
10724
|
};
|
|
10574
10725
|
}
|
|
10575
10726
|
|
|
10727
|
+
/**
|
|
10728
|
+
* SegmentedToggle - a single-select control built on datum-ui's ButtonGroup.
|
|
10729
|
+
* The active segment is a solid primary Button; inactive segments sit on the
|
|
10730
|
+
* neutral (muted) button surface so the control reads in both themes.
|
|
10731
|
+
*/
|
|
10732
|
+
function SegmentedToggle({ options, value, onChange, ariaLabel, className, disabled = false, }) {
|
|
10733
|
+
return (jsx(ButtonGroup, { "aria-label": ariaLabel, className: className, children: options.map((option) => {
|
|
10734
|
+
const active = value === option.value;
|
|
10735
|
+
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));
|
|
10736
|
+
}) }));
|
|
10737
|
+
}
|
|
10738
|
+
|
|
10576
10739
|
/**
|
|
10577
10740
|
* Options for the change source toggle
|
|
10578
10741
|
*/
|
|
@@ -10596,17 +10759,8 @@ const OPTIONS$1 = [
|
|
|
10596
10759
|
/**
|
|
10597
10760
|
* ChangeSourceToggle provides a segmented control for filtering by change source
|
|
10598
10761
|
*/
|
|
10599
|
-
function ChangeSourceToggle({ value, onChange, className
|
|
10600
|
-
return (jsx(
|
|
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
|
-
}) }));
|
|
10762
|
+
function ChangeSourceToggle({ value, onChange, className, disabled = false, }) {
|
|
10763
|
+
return (jsx(SegmentedToggle, { options: OPTIONS$1, value: value, onChange: onChange, ariaLabel: "Filter by change source", className: className, disabled: disabled }));
|
|
10610
10764
|
}
|
|
10611
10765
|
|
|
10612
10766
|
/**
|
|
@@ -10678,7 +10832,7 @@ const getSelectedPreset$1 = (timeRange) => {
|
|
|
10678
10832
|
/**
|
|
10679
10833
|
* ActivityFeedFilters provides filter controls for the activity feed
|
|
10680
10834
|
*/
|
|
10681
|
-
function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTimeRangeChange, disabled = false, hiddenFilters = [], className = "", extraFilters, }) {
|
|
10835
|
+
function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTimeRangeChange, disabled = false, hiddenFilters = [], className = "", extraFilters, layout = "inline", }) {
|
|
10682
10836
|
const { resourceKinds, actorNames, apiGroups, resourceNamespaces, error: facetsError, } = useFacets(client, timeRange, filters);
|
|
10683
10837
|
// Log facets error for debugging
|
|
10684
10838
|
if (facetsError) {
|
|
@@ -10898,12 +11052,186 @@ function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTi
|
|
|
10898
11052
|
clearTimeout(searchDebounceRef.current);
|
|
10899
11053
|
onFiltersChangeRef.current({ ...filtersRef.current, search: undefined });
|
|
10900
11054
|
}, []);
|
|
10901
|
-
|
|
11055
|
+
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" }) }))] }));
|
|
11056
|
+
const changeSourceControl = !hiddenFilters.includes("changeSource") && (jsx(ChangeSourceToggle, { value: filters.changeSource || "all", onChange: handleChangeSourceChange, disabled: disabled }));
|
|
11057
|
+
const filterChips = activeFilterIds.map((filterId) => {
|
|
11058
|
+
const config = FILTER_CONFIGS$1[filterId];
|
|
11059
|
+
return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
|
|
11060
|
+
? getFilterOptions(filterId)
|
|
11061
|
+
: 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));
|
|
11062
|
+
});
|
|
11063
|
+
const addFilterControl = (jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }));
|
|
11064
|
+
const timeRangeControl = (jsx(TimeRangeDropdown, { presets: TIME_PRESETS$1, selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() }));
|
|
11065
|
+
if (layout === "compact") {
|
|
11066
|
+
const activeFilterCount = activeFilterIds.length +
|
|
11067
|
+
(!hiddenFilters.includes("changeSource") &&
|
|
11068
|
+
filters.changeSource &&
|
|
11069
|
+
filters.changeSource !== "all"
|
|
11070
|
+
? 1
|
|
11071
|
+
: 0);
|
|
11072
|
+
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] }) })] })] }) }));
|
|
11073
|
+
}
|
|
11074
|
+
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
11075
|
const config = FILTER_CONFIGS$1[filterId];
|
|
10903
11076
|
return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
|
|
10904
11077
|
? getFilterOptions(filterId)
|
|
10905
11078
|
: 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]" }),
|
|
11079
|
+
}), jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }), extraFilters, jsx("div", { className: "flex-1 min-w-[20px]" }), timeRangeControl] }) }));
|
|
11080
|
+
}
|
|
11081
|
+
|
|
11082
|
+
const KIND_PLURALS = {
|
|
11083
|
+
DNSRecordSet: "DNS records",
|
|
11084
|
+
};
|
|
11085
|
+
function pluralizeKind(kind, count) {
|
|
11086
|
+
if (count === 1)
|
|
11087
|
+
return kind;
|
|
11088
|
+
return KIND_PLURALS[kind] ?? `${kind}s`;
|
|
11089
|
+
}
|
|
11090
|
+
const VERB_LABEL = {
|
|
11091
|
+
create: "created",
|
|
11092
|
+
update: "updated",
|
|
11093
|
+
delete: "deleted",
|
|
11094
|
+
other: "changed",
|
|
11095
|
+
};
|
|
11096
|
+
/**
|
|
11097
|
+
* ActivityFeedGroupItem renders a run of consecutive same verb/kind/actor
|
|
11098
|
+
* activities (see `groupActivities`) as a single collapsed row in the
|
|
11099
|
+
* "digest" timeline. A group of size 1 renders exactly like a normal
|
|
11100
|
+
* `ActivityFeedItem` — no visual change for ungrouped activities. Larger
|
|
11101
|
+
* groups render a synthesized summary ("{actor} created 12 DNS records")
|
|
11102
|
+
* that expands to reveal the individual activities underneath.
|
|
11103
|
+
*/
|
|
11104
|
+
function ActivityFeedGroupItem({ group, resourceLinkResolver, tenantLinkResolver, tenantRenderer, onActorClick, onActivityClick, isLast = false, }) {
|
|
11105
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
11106
|
+
if (group.count === 1) {
|
|
11107
|
+
return (jsx(ActivityFeedItem, { activity: group.activities[0], resourceLinkResolver: resourceLinkResolver, tenantLinkResolver: tenantLinkResolver, tenantRenderer: tenantRenderer, onActorClick: onActorClick, onActivityClick: onActivityClick, variant: "timeline", density: "compact", isLast: isLast }));
|
|
11108
|
+
}
|
|
11109
|
+
const { container: iconBg, icon: iconColor } = getActionIconClasses(group.verb);
|
|
11110
|
+
const Icon = getTimelineIcon(group.verb);
|
|
11111
|
+
const actorVisible = group.actorDisplayName || group.actorName;
|
|
11112
|
+
const summary = `${actorVisible} ${VERB_LABEL[group.verb]} ${group.count} ${pluralizeKind(group.kind, group.count)}`;
|
|
11113
|
+
const toggleExpand = (e) => {
|
|
11114
|
+
e.stopPropagation();
|
|
11115
|
+
setIsExpanded((prev) => !prev);
|
|
11116
|
+
};
|
|
11117
|
+
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))) }))] }));
|
|
11118
|
+
}
|
|
11119
|
+
|
|
11120
|
+
/**
|
|
11121
|
+
* Thin stat strip summarizing the activities currently loaded in the feed —
|
|
11122
|
+
* e.g. "18 changes · 3 people · 1 system". Computed client-side over the
|
|
11123
|
+
* loaded/paginated activities; it is a quick-glance headline, not a
|
|
11124
|
+
* full-range aggregate (that would require a backend facet query).
|
|
11125
|
+
*/
|
|
11126
|
+
function ActivityDigestSummary({ activities, className = "" }) {
|
|
11127
|
+
const stats = useMemo(() => {
|
|
11128
|
+
const humanActors = new Set();
|
|
11129
|
+
const systemActors = new Set();
|
|
11130
|
+
for (const activity of activities) {
|
|
11131
|
+
const { actor, changeSource } = activity.spec;
|
|
11132
|
+
if (changeSource === "human") {
|
|
11133
|
+
humanActors.add(actor.name);
|
|
11134
|
+
}
|
|
11135
|
+
else {
|
|
11136
|
+
systemActors.add(actor.name);
|
|
11137
|
+
}
|
|
11138
|
+
}
|
|
11139
|
+
return {
|
|
11140
|
+
total: activities.length,
|
|
11141
|
+
people: humanActors.size,
|
|
11142
|
+
systems: systemActors.size,
|
|
11143
|
+
};
|
|
11144
|
+
}, [activities]);
|
|
11145
|
+
if (stats.total === 0)
|
|
11146
|
+
return null;
|
|
11147
|
+
const parts = [`${stats.total} ${stats.total === 1 ? "change" : "changes"}`];
|
|
11148
|
+
if (stats.people > 0)
|
|
11149
|
+
parts.push(`${stats.people} ${stats.people === 1 ? "person" : "people"}`);
|
|
11150
|
+
if (stats.systems > 0)
|
|
11151
|
+
parts.push(`${stats.systems} ${stats.systems === 1 ? "system" : "systems"}`);
|
|
11152
|
+
return (jsx("div", { className: `px-1 pt-4 pb-2 text-xs text-muted-foreground ${className}`, children: parts.join(" · ") }));
|
|
11153
|
+
}
|
|
11154
|
+
|
|
11155
|
+
function tenantKey(tenant) {
|
|
11156
|
+
if (!tenant)
|
|
11157
|
+
return '';
|
|
11158
|
+
return `${tenant.type}:${tenant.name}`;
|
|
11159
|
+
}
|
|
11160
|
+
function groupSignature(activity) {
|
|
11161
|
+
const { spec } = activity;
|
|
11162
|
+
const verb = normalizeVerb(extractVerb(spec.summary));
|
|
11163
|
+
return [verb, spec.resource.kind, spec.actor.name, tenantKey(spec.tenant)].join('|');
|
|
11164
|
+
}
|
|
11165
|
+
/**
|
|
11166
|
+
* Group consecutive activities that share verb + resource kind + actor +
|
|
11167
|
+
* tenant. Activities are assumed to already be sorted newest-first (as
|
|
11168
|
+
* returned by `useActivityFeed`); only adjacent items are merged, so a
|
|
11169
|
+
* burst of matching activities collapses into one group without needing
|
|
11170
|
+
* any time-window heuristics.
|
|
11171
|
+
*/
|
|
11172
|
+
function groupActivities(activities) {
|
|
11173
|
+
const groups = [];
|
|
11174
|
+
for (const activity of activities) {
|
|
11175
|
+
const signature = groupSignature(activity);
|
|
11176
|
+
const last = groups[groups.length - 1];
|
|
11177
|
+
if (last && last.key === signature) {
|
|
11178
|
+
last.activities.push(activity);
|
|
11179
|
+
last.count += 1;
|
|
11180
|
+
continue;
|
|
11181
|
+
}
|
|
11182
|
+
const { spec } = activity;
|
|
11183
|
+
groups.push({
|
|
11184
|
+
key: signature,
|
|
11185
|
+
verb: normalizeVerb(extractVerb(spec.summary)),
|
|
11186
|
+
kind: spec.resource.kind,
|
|
11187
|
+
actorName: spec.actor.name,
|
|
11188
|
+
actorDisplayName: spec.actor.displayName,
|
|
11189
|
+
tenant: spec.tenant,
|
|
11190
|
+
activities: [activity],
|
|
11191
|
+
count: 1,
|
|
11192
|
+
latestTimestamp: activity.metadata?.creationTimestamp,
|
|
11193
|
+
});
|
|
11194
|
+
}
|
|
11195
|
+
return groups;
|
|
11196
|
+
}
|
|
11197
|
+
function dayLabel(timestamp) {
|
|
11198
|
+
if (!timestamp)
|
|
11199
|
+
return 'Unknown date';
|
|
11200
|
+
const date = new Date(timestamp);
|
|
11201
|
+
if (Number.isNaN(date.getTime()))
|
|
11202
|
+
return 'Unknown date';
|
|
11203
|
+
if (isToday(date))
|
|
11204
|
+
return 'Today';
|
|
11205
|
+
if (isYesterday(date))
|
|
11206
|
+
return 'Yesterday';
|
|
11207
|
+
return format(date, 'MMMM d, yyyy');
|
|
11208
|
+
}
|
|
11209
|
+
/**
|
|
11210
|
+
* Bucket activities into calendar-day sections (Today / Yesterday / a
|
|
11211
|
+
* formatted date), then group consecutive matching activities within each
|
|
11212
|
+
* day. Bucketing by day first (rather than grouping across the whole list)
|
|
11213
|
+
* keeps a group from silently spanning a day boundary.
|
|
11214
|
+
*/
|
|
11215
|
+
function groupActivitiesByDay(activities) {
|
|
11216
|
+
const sections = [];
|
|
11217
|
+
let currentLabel = null;
|
|
11218
|
+
let currentBucket = [];
|
|
11219
|
+
const flush = () => {
|
|
11220
|
+
if (currentLabel !== null && currentBucket.length > 0) {
|
|
11221
|
+
sections.push({ label: currentLabel, groups: groupActivities(currentBucket) });
|
|
11222
|
+
}
|
|
11223
|
+
};
|
|
11224
|
+
for (const activity of activities) {
|
|
11225
|
+
const label = dayLabel(activity.metadata?.creationTimestamp);
|
|
11226
|
+
if (label !== currentLabel) {
|
|
11227
|
+
flush();
|
|
11228
|
+
currentLabel = label;
|
|
11229
|
+
currentBucket = [];
|
|
11230
|
+
}
|
|
11231
|
+
currentBucket.push(activity);
|
|
11232
|
+
}
|
|
11233
|
+
flush();
|
|
11234
|
+
return sections;
|
|
10907
11235
|
}
|
|
10908
11236
|
|
|
10909
11237
|
/**
|
|
@@ -10973,26 +11301,28 @@ function ActivityFeed({ client, initialFilters = { changeSource: "human" }, init
|
|
|
10973
11301
|
useEffect(() => {
|
|
10974
11302
|
isLoadingRef.current = isLoading;
|
|
10975
11303
|
}, [isLoading]);
|
|
10976
|
-
// Infinite scroll using Intersection Observer
|
|
11304
|
+
// Infinite scroll using Intersection Observer.
|
|
11305
|
+
// The list container is the root only when it actually scrolls. Host apps
|
|
11306
|
+
// such as cloud-portal scroll at the page level, so the container never
|
|
11307
|
+
// overflows; in that case observe against the viewport instead.
|
|
10977
11308
|
useEffect(() => {
|
|
10978
11309
|
if (!infiniteScroll || !hasMore || !loadMoreTriggerRef.current)
|
|
10979
11310
|
return;
|
|
11311
|
+
const container = scrollContainerRef.current;
|
|
11312
|
+
const containerScrolls = !!container && container.scrollHeight > container.clientHeight + 1;
|
|
10980
11313
|
const observer = new IntersectionObserver((entries) => {
|
|
10981
11314
|
const entry = entries[0];
|
|
10982
11315
|
if (entry.isIntersecting && !isLoadingRef.current) {
|
|
10983
|
-
// Call through the ref to always use the latest function
|
|
10984
11316
|
loadMoreRef.current();
|
|
10985
11317
|
}
|
|
10986
11318
|
}, {
|
|
10987
|
-
root:
|
|
11319
|
+
root: containerScrolls ? container : null,
|
|
10988
11320
|
rootMargin: `${loadMoreThreshold}px`,
|
|
10989
11321
|
threshold: 0,
|
|
10990
11322
|
});
|
|
10991
11323
|
observer.observe(loadMoreTriggerRef.current);
|
|
10992
|
-
return () =>
|
|
10993
|
-
|
|
10994
|
-
};
|
|
10995
|
-
}, [infiniteScroll, loadMoreThreshold, hasMore]);
|
|
11324
|
+
return () => observer.disconnect();
|
|
11325
|
+
}, [infiniteScroll, loadMoreThreshold, hasMore, activities.length]);
|
|
10996
11326
|
// Handle filter changes - refresh is automatic via the hook
|
|
10997
11327
|
const handleFiltersChange = useCallback((newFilters) => {
|
|
10998
11328
|
setFilters(newFilters);
|
|
@@ -11032,10 +11362,10 @@ function ActivityFeed({ client, initialFilters = { changeSource: "human" }, init
|
|
|
11032
11362
|
// Parent containers must have proper height constraints (h-screen/h-full + overflow-hidden)
|
|
11033
11363
|
const effectiveMaxHeight = maxHeight === "none" ? undefined : maxHeight;
|
|
11034
11364
|
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-
|
|
11365
|
+
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
11366
|
activities.length === 0 &&
|
|
11037
11367
|
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 &&
|
|
11368
|
+
(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
11369
|
activities.length === 0 &&
|
|
11040
11370
|
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
11371
|
}
|
|
@@ -11686,7 +12016,7 @@ function useEventsFeed({ client, initialFilters = {}, initialTimeRange = { start
|
|
|
11686
12016
|
function getRegarding$1(event) {
|
|
11687
12017
|
return event.regarding || event.involvedObject || {};
|
|
11688
12018
|
}
|
|
11689
|
-
function getReportingController
|
|
12019
|
+
function getReportingController(event) {
|
|
11690
12020
|
return event.reportingController || event.reportingComponent || event.source?.component;
|
|
11691
12021
|
}
|
|
11692
12022
|
function getReportingInstance(event) {
|
|
@@ -11699,7 +12029,7 @@ function getReportingInstance(event) {
|
|
|
11699
12029
|
*/
|
|
11700
12030
|
function EventExpandedDetails({ event, compact = true }) {
|
|
11701
12031
|
const regarding = getRegarding$1(event);
|
|
11702
|
-
const reportingController = getReportingController
|
|
12032
|
+
const reportingController = getReportingController(event);
|
|
11703
12033
|
const reportingInstance = getReportingInstance(event);
|
|
11704
12034
|
const { eventTime, action, metadata, related } = event;
|
|
11705
12035
|
const firstTimestamp = event.firstTimestamp || event.deprecatedFirstTimestamp;
|
|
@@ -11726,12 +12056,6 @@ function getRegarding(event) {
|
|
|
11726
12056
|
function getNote(event) {
|
|
11727
12057
|
return event.note || event.message;
|
|
11728
12058
|
}
|
|
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
12059
|
/**
|
|
11736
12060
|
* Get the event count (handling both new and deprecated field names)
|
|
11737
12061
|
*/
|
|
@@ -11767,7 +12091,6 @@ function EventFeedItem({ event, onEventClick, onResourceClick, isSelected = fals
|
|
|
11767
12091
|
const note = getNote(event);
|
|
11768
12092
|
const count = getCount(event);
|
|
11769
12093
|
const timestamp = getTimestamp(event);
|
|
11770
|
-
getReportingController(event);
|
|
11771
12094
|
const { type, reason } = event;
|
|
11772
12095
|
const handleClick = () => {
|
|
11773
12096
|
onEventClick?.(event);
|
|
@@ -11810,11 +12133,11 @@ function EventFeedItem({ event, onEventClick, onResourceClick, isSelected = fals
|
|
|
11810
12133
|
if (variant === 'timeline') {
|
|
11811
12134
|
const TypeIcon = isWarning ? TriangleAlert : Bell;
|
|
11812
12135
|
const iconBg = isWarning
|
|
11813
|
-
? 'bg-
|
|
11814
|
-
: 'bg-
|
|
12136
|
+
? 'bg-destructive/10'
|
|
12137
|
+
: 'bg-[var(--info-100)]';
|
|
11815
12138
|
const iconColor = isWarning
|
|
11816
|
-
? 'text-
|
|
11817
|
-
: 'text-
|
|
12139
|
+
? 'text-destructive'
|
|
12140
|
+
: 'text-[var(--info-500)]';
|
|
11818
12141
|
const objectLabel = regarding.namespace
|
|
11819
12142
|
? `${regarding.kind || 'Unknown'} · ${regarding.namespace}/${regarding.name || ''}`
|
|
11820
12143
|
: `${regarding.kind || 'Unknown'} · ${regarding.name || ''}`;
|
|
@@ -11827,18 +12150,16 @@ function EventFeedItem({ event, onEventClick, onResourceClick, isSelected = fals
|
|
|
11827
12150
|
cursor: 'help',
|
|
11828
12151
|
}, 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
12152
|
}
|
|
11830
|
-
return (jsxs(Fragment, { children: [jsxs(TableRow, { "data-state": isSelected ? 'selected' : undefined, className: cn('cursor-pointer', isNew && 'bg-
|
|
12153
|
+
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
12154
|
toggleExpand(e);
|
|
11832
12155
|
handleClick();
|
|
11833
|
-
}, "aria-expanded": isExpanded, children: [jsx(TableCell, { className: "py-2 align-middle whitespace-nowrap", children: jsx(
|
|
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: {
|
|
12156
|
+
}, "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
12157
|
overflow: 'hidden',
|
|
11837
12158
|
textOverflow: 'ellipsis',
|
|
11838
12159
|
whiteSpace: 'nowrap',
|
|
11839
12160
|
}, 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
12161
|
? `${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-
|
|
12162
|
+
: `${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
12163
|
}
|
|
11843
12164
|
|
|
11844
12165
|
/**
|
|
@@ -11951,11 +12272,8 @@ const OPTIONS = [
|
|
|
11951
12272
|
/**
|
|
11952
12273
|
* EventTypeToggle provides a segmented control for filtering by event type
|
|
11953
12274
|
*/
|
|
11954
|
-
function EventTypeToggle({ value, onChange, className
|
|
11955
|
-
return (jsx(
|
|
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
|
-
}) }));
|
|
12275
|
+
function EventTypeToggle({ value, onChange, className, disabled = false, }) {
|
|
12276
|
+
return (jsx(SegmentedToggle, { options: OPTIONS, value: value, onChange: onChange, ariaLabel: "Filter by event type", className: className, disabled: disabled }));
|
|
11959
12277
|
}
|
|
11960
12278
|
|
|
11961
12279
|
/**
|
|
@@ -12199,7 +12517,7 @@ function EventsFeedFilters({ client, filters, timeRange, onFiltersChange, onTime
|
|
|
12199
12517
|
search: value || undefined,
|
|
12200
12518
|
});
|
|
12201
12519
|
}, [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-
|
|
12520
|
+
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
12521
|
const config = FILTER_CONFIGS[filterId];
|
|
12204
12522
|
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
12523
|
}), 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 +12585,7 @@ function EventsFeed({ client, initialFilters = {}, initialTimeRange = { start: "
|
|
|
12267
12585
|
: `flex-1 min-h-0 flex flex-col p-3 gap-0 ${className}`;
|
|
12268
12586
|
// Build list classes - use flex-1 min-h-0 for flex-based scrolling
|
|
12269
12587
|
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-
|
|
12588
|
+
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
12589
|
? 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
12590
|
: 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
12591
|
? 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 +12765,14 @@ function PolicyList({ client, onViewPolicy, onEditPolicy, onCreatePolicy, groupB
|
|
|
12447
12765
|
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
12766
|
const rules = countRules(policy);
|
|
12449
12767
|
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-
|
|
12451
|
-
? "text-
|
|
12768
|
+
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"
|
|
12769
|
+
? "text-destructive"
|
|
12452
12770
|
: policyStatus.status === "pending"
|
|
12453
|
-
? "text-
|
|
12454
|
-
: "text-
|
|
12455
|
-
? "bg-
|
|
12771
|
+
? "text-[var(--badge-warning)]"
|
|
12772
|
+
: "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
|
|
12773
|
+
? "bg-muted text-muted-foreground"
|
|
12456
12774
|
: "", 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-
|
|
12775
|
+
? "bg-muted text-muted-foreground"
|
|
12458
12776
|
: "", children: rules.event }) })] }, policy.metadata?.name));
|
|
12459
12777
|
}) })] }) }))] }, group.apiGroup))) }))] })] }));
|
|
12460
12778
|
}
|
|
@@ -12902,7 +13220,7 @@ function PolicyRuleListItem({ rule, ruleType, index, onEdit, onDelete, onMoveUp,
|
|
|
12902
13220
|
flex items-center gap-2 p-2 rounded-md border transition-all duration-200
|
|
12903
13221
|
border-border bg-background hover:bg-muted/50
|
|
12904
13222
|
${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-
|
|
13223
|
+
`, 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
13224
|
}
|
|
12907
13225
|
|
|
12908
13226
|
/**
|
|
@@ -13023,7 +13341,7 @@ const registeredLanguages = new Set();
|
|
|
13023
13341
|
* Lazy-loaded Monaco editor for CEL expressions and templates
|
|
13024
13342
|
* Provides autocomplete based on available field paths
|
|
13025
13343
|
*/
|
|
13026
|
-
function CelEditor({ value, onChange, language = 'cel', availableFields = [],
|
|
13344
|
+
function CelEditor({ value, onChange, language = 'cel', availableFields = [], height = '100px', className, error = false, readOnly = false, 'data-testid': testId, }) {
|
|
13027
13345
|
const [Editor, setEditor] = useState(null);
|
|
13028
13346
|
const [loadError, setLoadError] = useState(null);
|
|
13029
13347
|
const [isLoading, setIsLoading] = useState(true);
|
|
@@ -13182,7 +13500,7 @@ function CelEditor({ value, onChange, language = 'cel', availableFields = [], pl
|
|
|
13182
13500
|
*/
|
|
13183
13501
|
function requiresBracketNotation(key) {
|
|
13184
13502
|
// Keys with dots, slashes, or other special characters need bracket notation
|
|
13185
|
-
return /[
|
|
13503
|
+
return /[./\-:]/.test(key) || /^\d/.test(key);
|
|
13186
13504
|
}
|
|
13187
13505
|
/**
|
|
13188
13506
|
* Build the CEL path for a key, using bracket notation when needed
|
|
@@ -13389,7 +13707,7 @@ function PolicyRuleEditorDialog({ open, onOpenChange, rule, ruleType, existingNa
|
|
|
13389
13707
|
const [previewError, setPreviewError] = useState(null);
|
|
13390
13708
|
// Sample data for autocomplete (fetched once when dialog opens)
|
|
13391
13709
|
const [sampleData, setSampleData] = useState([]);
|
|
13392
|
-
const [
|
|
13710
|
+
const [, setSampleDataLoading] = useState(false);
|
|
13393
13711
|
// Extract field paths from sample data OR preview data for autocomplete
|
|
13394
13712
|
const availableFields = useMemo(() => {
|
|
13395
13713
|
// First try to use preview data if available
|
|
@@ -13838,10 +14156,10 @@ function PolicyEditor({ client, policyName, onSaveSuccess, onCancel, onResourceC
|
|
|
13838
14156
|
};
|
|
13839
14157
|
const policyStatus = getPolicyStatus();
|
|
13840
14158
|
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-
|
|
14159
|
+
? 'bg-[var(--success-500)]'
|
|
13842
14160
|
: policyStatus.status === 'error'
|
|
13843
|
-
? 'bg-
|
|
13844
|
-
: 'bg-
|
|
14161
|
+
? 'bg-destructive'
|
|
14162
|
+
: '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
14163
|
}
|
|
13846
14164
|
|
|
13847
14165
|
/**
|
|
@@ -13925,12 +14243,12 @@ function PolicyDetailView({ client, policyName, onEdit, onResourceClick, classNa
|
|
|
13925
14243
|
const policyStatus = getPolicyStatus();
|
|
13926
14244
|
const isUnhealthy = policyStatus && policyStatus.status !== 'ready';
|
|
13927
14245
|
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-
|
|
14246
|
+
? 'bg-[var(--success-500)]'
|
|
13929
14247
|
: policyStatus?.status === 'error'
|
|
13930
|
-
? 'bg-
|
|
14248
|
+
? 'bg-destructive'
|
|
13931
14249
|
: policyStatus?.status === 'pending'
|
|
13932
|
-
? 'bg-
|
|
13933
|
-
: 'bg-
|
|
14250
|
+
? 'bg-[var(--badge-warning)]'
|
|
14251
|
+
: '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
14252
|
}
|
|
13935
14253
|
|
|
13936
14254
|
/**
|
|
@@ -14030,7 +14348,7 @@ function PolicyEditView({ client, policyName, onSaveSuccess, onCancel, className
|
|
|
14030
14348
|
};
|
|
14031
14349
|
const policyStatus = getPolicyStatus();
|
|
14032
14350
|
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-
|
|
14351
|
+
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
14352
|
}
|
|
14035
14353
|
|
|
14036
14354
|
/**
|
|
@@ -14111,8 +14429,8 @@ function PolicyRuleEditor({ rule, index, ruleType, isHighlighted = false, onChan
|
|
|
14111
14429
|
onChange({ ...rule, summary: example });
|
|
14112
14430
|
};
|
|
14113
14431
|
return (jsxs(Card, { className: `mb-3 transition-all duration-200 ${isHighlighted
|
|
14114
|
-
? 'border-
|
|
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-
|
|
14432
|
+
? 'border-[var(--success-500)] bg-[var(--success-100)]'
|
|
14433
|
+
: '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
14434
|
}
|
|
14117
14435
|
|
|
14118
14436
|
/**
|
|
@@ -14770,7 +15088,7 @@ function ReindexJobList({ client, onViewJob, onCreateJob, watch = true, classNam
|
|
|
14770
15088
|
const bTime = b.metadata?.creationTimestamp || '';
|
|
14771
15089
|
return bTime.localeCompare(aTime);
|
|
14772
15090
|
});
|
|
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-
|
|
15091
|
+
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
15092
|
const duration = getReindexJobDuration(job);
|
|
14775
15093
|
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
15094
|
}) })] }) }))] })] }));
|
|
@@ -14782,11 +15100,11 @@ function ReindexJobList({ client, onViewJob, onCreateJob, watch = true, classNam
|
|
|
14782
15100
|
function getPhaseIcon(phase) {
|
|
14783
15101
|
switch (phase) {
|
|
14784
15102
|
case 'Succeeded':
|
|
14785
|
-
return jsx(CircleCheck, { className: "h-8 w-8 text-
|
|
15103
|
+
return jsx(CircleCheck, { className: "h-8 w-8 text-[var(--success-500)]" });
|
|
14786
15104
|
case 'Failed':
|
|
14787
|
-
return jsx(CircleX, { className: "h-8 w-8 text-
|
|
15105
|
+
return jsx(CircleX, { className: "h-8 w-8 text-destructive" });
|
|
14788
15106
|
case 'Running':
|
|
14789
|
-
return jsx(LoaderCircle, { className: "h-8 w-8 text-
|
|
15107
|
+
return jsx(LoaderCircle, { className: "h-8 w-8 text-[var(--info-500)] animate-spin" });
|
|
14790
15108
|
case 'Pending':
|
|
14791
15109
|
default:
|
|
14792
15110
|
return jsx(Clock, { className: "h-8 w-8 text-muted-foreground" });
|
|
@@ -14867,7 +15185,7 @@ function ReindexJobDetailView({ client, jobName, onDelete, watch = true, classNa
|
|
|
14867
15185
|
const duration = job ? getReindexJobDuration(job) : null;
|
|
14868
15186
|
const statusMessage = job ? getReindexJobStatusMessage(job) : '';
|
|
14869
15187
|
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-
|
|
15188
|
+
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
15189
|
}
|
|
14872
15190
|
|
|
14873
15191
|
const TIME_RANGE_OPTIONS$1 = [
|
|
@@ -14970,76 +15288,11 @@ function ReindexJobDialog({ client, policyName, open, onOpenChange, onSuccess, e
|
|
|
14970
15288
|
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
15289
|
}
|
|
14972
15290
|
|
|
14973
|
-
|
|
14974
|
-
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
|
|
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))) })] })] }) }) })] }));
|
|
15291
|
+
function MultiCombobox({ options, values, onValuesChange, placeholder = 'Select…',
|
|
15292
|
+
// searchPlaceholder is accepted for API compatibility but MultiSelect has no counterpart.
|
|
15293
|
+
emptyMessage = 'No results', disabled, loading, className, maxDisplayed = 3, }) {
|
|
15294
|
+
const items = React.useMemo(() => options.map((o) => ({ value: o.value, label: o.label })), [options]);
|
|
15295
|
+
return (jsx(MultiSelect, { className: className, options: items, value: values, onValueChange: onValuesChange, placeholder: placeholder, emptyContent: emptyMessage, isLoading: loading, disabled: disabled, maxCount: maxDisplayed }));
|
|
15043
15296
|
}
|
|
15044
15297
|
|
|
15045
15298
|
/**
|
|
@@ -15749,7 +16002,7 @@ class ActivityApiClient {
|
|
|
15749
16002
|
const reader = response.body.getReader();
|
|
15750
16003
|
const decoder = new TextDecoder();
|
|
15751
16004
|
let buffer = '';
|
|
15752
|
-
|
|
16005
|
+
for (;;) {
|
|
15753
16006
|
const { done, value } = await reader.read();
|
|
15754
16007
|
if (done) {
|
|
15755
16008
|
options?.onClose?.();
|
|
@@ -16149,7 +16402,7 @@ class ActivityApiClient {
|
|
|
16149
16402
|
const reader = response.body.getReader();
|
|
16150
16403
|
const decoder = new TextDecoder();
|
|
16151
16404
|
let buffer = '';
|
|
16152
|
-
|
|
16405
|
+
for (;;) {
|
|
16153
16406
|
const { done, value } = await reader.read();
|
|
16154
16407
|
if (done) {
|
|
16155
16408
|
options?.onClose?.();
|
|
@@ -16265,7 +16518,7 @@ class ActivityApiClient {
|
|
|
16265
16518
|
const reader = response.body.getReader();
|
|
16266
16519
|
const decoder = new TextDecoder();
|
|
16267
16520
|
let buffer = '';
|
|
16268
|
-
|
|
16521
|
+
for (;;) {
|
|
16269
16522
|
const { done, value } = await reader.read();
|
|
16270
16523
|
if (done) {
|
|
16271
16524
|
options?.onClose?.();
|
|
@@ -16501,5 +16754,5 @@ const POLICY_FILTER_FIELDS = [
|
|
|
16501
16754
|
},
|
|
16502
16755
|
];
|
|
16503
16756
|
|
|
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 };
|
|
16757
|
+
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
16758
|
//# sourceMappingURL=index.esm.js.map
|