@datum-cloud/activity-ui 0.5.0 → 0.5.1-dev.a55ad87
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 +1 -1
- package/dist/components/ActionMultiSelect.d.ts.map +1 -1
- 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/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 +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +1361 -1406
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1361 -1406
- package/dist/index.js.map +1 -1
- package/package.json +8 -13
- 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
|
/**
|
|
@@ -4007,763 +4011,527 @@ function subMinutes(date, amount, options) {
|
|
|
4007
4011
|
return addMinutes(date, -15, options);
|
|
4008
4012
|
}
|
|
4009
4013
|
|
|
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
|
-
};
|
|
4014
|
+
/** Sentinel for the "All" item; the public value stays '' for "no filter". */
|
|
4015
|
+
const ALL_VALUE = '__all__';
|
|
4016
|
+
function Combobox({ options, value, onValueChange, placeholder = 'Select…', searchPlaceholder = 'Search…', emptyMessage = 'No results', disabled, loading, className, clearable = false, showAllOption = true, allOptionLabel = 'All', }) {
|
|
4017
|
+
const items = React.useMemo(() => {
|
|
4018
|
+
const base = options.map((o) => ({ value: o.value, label: o.label, count: o.count }));
|
|
4019
|
+
return showAllOption ? [{ value: ALL_VALUE, label: allOptionLabel }, ...base] : base;
|
|
4020
|
+
}, [options, showAllOption, allOptionLabel]);
|
|
4021
|
+
// An empty value selects the "All" item so the trigger reads its label.
|
|
4022
|
+
const selectedValue = value === '' && showAllOption ? ALL_VALUE : value;
|
|
4023
|
+
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 }))] })) }));
|
|
4024
|
+
}
|
|
4089
4025
|
|
|
4026
|
+
const RELATIVE_KEY = /^now-(\d+)([mhd])$/;
|
|
4027
|
+
/** Resolve a relative key such as `now-7d` to a `{ from, to }` window ending now. */
|
|
4028
|
+
function relativeKeyToRange(key, now = new Date()) {
|
|
4029
|
+
const match = RELATIVE_KEY.exec(key);
|
|
4030
|
+
if (!match)
|
|
4031
|
+
return null;
|
|
4032
|
+
const amount = Number(match[1]);
|
|
4033
|
+
const unitMs = { m: 60000, h: 3600000, d: 86400000 }[match[2]];
|
|
4034
|
+
return { from: new Date(now.getTime() - amount * unitMs), to: now };
|
|
4035
|
+
}
|
|
4036
|
+
/** `YYYY-MM-DDTHH:mm` in local time, the format the filter components store. */
|
|
4037
|
+
function toDatetimeLocal(iso) {
|
|
4038
|
+
const d = new Date(iso);
|
|
4039
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
4040
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
4041
|
+
}
|
|
4042
|
+
function toPickerPresets(presets) {
|
|
4043
|
+
return presets
|
|
4044
|
+
.filter((p) => RELATIVE_KEY.test(p.key))
|
|
4045
|
+
.map((p) => ({
|
|
4046
|
+
key: p.key,
|
|
4047
|
+
label: p.label,
|
|
4048
|
+
getRange: () => relativeKeyToRange(p.key),
|
|
4049
|
+
}));
|
|
4050
|
+
}
|
|
4090
4051
|
/**
|
|
4091
|
-
*
|
|
4092
|
-
*
|
|
4093
|
-
*
|
|
4094
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4052
|
+
* TimeRangeDropdown - relative presets plus an absolute range, on datum-ui's
|
|
4053
|
+
* DateTimeRangePicker. The emitted value carries `preset` when a preset was
|
|
4054
|
+
* clicked, which keeps `start=now-7d` style URL state intact.
|
|
4095
4055
|
*/
|
|
4056
|
+
function TimeRangeDropdown({ presets, selectedPreset, onPresetSelect, onCustomRangeApply, customStart, customEnd, disabled = false, className, displayLabel, }) {
|
|
4057
|
+
const pickerPresets = React.useMemo(() => toPickerPresets(presets), [presets]);
|
|
4058
|
+
const value = React.useMemo(() => {
|
|
4059
|
+
if (selectedPreset !== 'custom') {
|
|
4060
|
+
const range = relativeKeyToRange(selectedPreset);
|
|
4061
|
+
return range
|
|
4062
|
+
? { from: range.from.toISOString(), to: range.to.toISOString(), preset: selectedPreset }
|
|
4063
|
+
: null;
|
|
4064
|
+
}
|
|
4065
|
+
if (customStart && customEnd) {
|
|
4066
|
+
return { from: new Date(customStart).toISOString(), to: new Date(customEnd).toISOString() };
|
|
4067
|
+
}
|
|
4068
|
+
return null;
|
|
4069
|
+
}, [selectedPreset, customStart, customEnd]);
|
|
4070
|
+
const handleChange = (next) => {
|
|
4071
|
+
if (!next)
|
|
4072
|
+
return;
|
|
4073
|
+
const preset = next.preset;
|
|
4074
|
+
if (preset) {
|
|
4075
|
+
onPresetSelect(preset);
|
|
4076
|
+
return;
|
|
4077
|
+
}
|
|
4078
|
+
onCustomRangeApply(toDatetimeLocal(next.from), toDatetimeLocal(next.to));
|
|
4079
|
+
};
|
|
4080
|
+
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 }));
|
|
4081
|
+
}
|
|
4096
4082
|
|
|
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",
|
|
4083
|
+
const TIME_PRESETS$3 = [
|
|
4110
4084
|
{
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
className: mergeClasses("lucide", className),
|
|
4118
|
-
...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
|
|
4119
|
-
...rest
|
|
4085
|
+
key: 'last15min',
|
|
4086
|
+
label: 'Last 15 min',
|
|
4087
|
+
getValue: () => ({
|
|
4088
|
+
start: formatISO(subMinutes(new Date())),
|
|
4089
|
+
end: formatISO(new Date()),
|
|
4090
|
+
}),
|
|
4120
4091
|
},
|
|
4121
|
-
[
|
|
4122
|
-
...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
|
|
4123
|
-
...Array.isArray(children) ? children : [children]
|
|
4124
|
-
]
|
|
4125
|
-
)
|
|
4126
|
-
);
|
|
4127
|
-
|
|
4128
|
-
/**
|
|
4129
|
-
* @license lucide-react v0.577.0 - ISC
|
|
4130
|
-
*
|
|
4131
|
-
* This source code is licensed under the ISC license.
|
|
4132
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4133
|
-
*/
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
const createLucideIcon = (iconName, iconNode) => {
|
|
4137
|
-
const Component = forwardRef(
|
|
4138
|
-
({ className, ...props }, ref) => createElement(Icon, {
|
|
4139
|
-
ref,
|
|
4140
|
-
iconNode,
|
|
4141
|
-
className: mergeClasses(
|
|
4142
|
-
`lucide-${toKebabCase(toPascalCase(iconName))}`,
|
|
4143
|
-
`lucide-${iconName}`,
|
|
4144
|
-
className
|
|
4145
|
-
),
|
|
4146
|
-
...props
|
|
4147
|
-
})
|
|
4148
|
-
);
|
|
4149
|
-
Component.displayName = toPascalCase(iconName);
|
|
4150
|
-
return Component;
|
|
4151
|
-
};
|
|
4152
|
-
|
|
4153
|
-
/**
|
|
4154
|
-
* @license lucide-react v0.577.0 - ISC
|
|
4155
|
-
*
|
|
4156
|
-
* This source code is licensed under the ISC license.
|
|
4157
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4158
|
-
*/
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
const __iconNode$p = [
|
|
4162
|
-
[
|
|
4163
|
-
"path",
|
|
4164
4092
|
{
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
/**
|
|
4173
|
-
* @license lucide-react v0.577.0 - ISC
|
|
4174
|
-
*
|
|
4175
|
-
* This source code is licensed under the ISC license.
|
|
4176
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4177
|
-
*/
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
const __iconNode$o = [
|
|
4181
|
-
["path", { d: "M10.268 21a2 2 0 0 0 3.464 0", key: "vwvbt9" }],
|
|
4182
|
-
[
|
|
4183
|
-
"path",
|
|
4093
|
+
key: 'last1hour',
|
|
4094
|
+
label: 'Last hour',
|
|
4095
|
+
getValue: () => ({
|
|
4096
|
+
start: formatISO(subHours(new Date(), 1)),
|
|
4097
|
+
end: formatISO(new Date()),
|
|
4098
|
+
}),
|
|
4099
|
+
},
|
|
4184
4100
|
{
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4101
|
+
key: 'last6hours',
|
|
4102
|
+
label: 'Last 6 hours',
|
|
4103
|
+
getValue: () => ({
|
|
4104
|
+
start: formatISO(subHours(new Date(), 6)),
|
|
4105
|
+
end: formatISO(new Date()),
|
|
4106
|
+
}),
|
|
4107
|
+
},
|
|
4108
|
+
{
|
|
4109
|
+
key: 'last24hours',
|
|
4110
|
+
label: 'Last 24 hours',
|
|
4111
|
+
getValue: () => ({
|
|
4112
|
+
start: formatISO(subHours(new Date(), 24)),
|
|
4113
|
+
end: formatISO(new Date()),
|
|
4114
|
+
}),
|
|
4115
|
+
},
|
|
4116
|
+
{
|
|
4117
|
+
key: 'last7days',
|
|
4118
|
+
label: 'Last 7 days',
|
|
4119
|
+
getValue: () => ({
|
|
4120
|
+
start: formatISO(subDays(new Date(), 7)),
|
|
4121
|
+
end: formatISO(new Date()),
|
|
4122
|
+
}),
|
|
4123
|
+
},
|
|
4124
|
+
{
|
|
4125
|
+
key: 'last30days',
|
|
4126
|
+
label: 'Last 30 days',
|
|
4127
|
+
getValue: () => ({
|
|
4128
|
+
start: formatISO(subDays(new Date(), 30)),
|
|
4129
|
+
end: formatISO(new Date()),
|
|
4130
|
+
}),
|
|
4131
|
+
},
|
|
4205
4132
|
];
|
|
4206
|
-
|
|
4207
|
-
|
|
4133
|
+
/** Debounce delay for text inputs (ms) */
|
|
4134
|
+
const DEBOUNCE_DELAY = 300;
|
|
4208
4135
|
/**
|
|
4209
|
-
*
|
|
4210
|
-
*
|
|
4211
|
-
* This source code is licensed under the ISC license.
|
|
4212
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4136
|
+
* Helper function to convert ISO string to datetime-local format
|
|
4213
4137
|
*/
|
|
4138
|
+
const formatDatetimeLocal$4 = (isoString) => {
|
|
4139
|
+
const date = new Date(isoString);
|
|
4140
|
+
const year = date.getFullYear();
|
|
4141
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
4142
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
4143
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
4144
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
4145
|
+
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
|
4146
|
+
};
|
|
4147
|
+
function SimpleQueryBuilder({ client, onFilterChange, initialLimit = 100, className = '', disabled = false, }) {
|
|
4148
|
+
const [isAdvancedExpanded, setIsAdvancedExpanded] = useState(false);
|
|
4149
|
+
const [filters, setFilters] = useState({
|
|
4150
|
+
verb: '',
|
|
4151
|
+
resourceType: '',
|
|
4152
|
+
namespace: '',
|
|
4153
|
+
resourceName: '',
|
|
4154
|
+
username: '',
|
|
4155
|
+
});
|
|
4156
|
+
const [limit, setLimit] = useState(initialLimit);
|
|
4157
|
+
const [advancedFilter, setAdvancedFilter] = useState('');
|
|
4158
|
+
const [useAdvancedFilter, setUseAdvancedFilter] = useState(false);
|
|
4159
|
+
// Time range state
|
|
4160
|
+
const [selectedPreset, setSelectedPreset] = useState('last24hours');
|
|
4161
|
+
const [timeRange, setTimeRange] = useState(null);
|
|
4162
|
+
const [customStart, setCustomStart] = useState(() => formatDatetimeLocal$4(formatISO(subDays(new Date(), 1))));
|
|
4163
|
+
const [customEnd, setCustomEnd] = useState(() => formatDatetimeLocal$4(formatISO(new Date())));
|
|
4164
|
+
// Fetch facets for dynamic filter options
|
|
4165
|
+
const { verbs, resources, namespaces, usernames, isLoading: facetsLoading, error: facetsError } = useAuditLogFacets(client, timeRange);
|
|
4166
|
+
// Log facets error for debugging
|
|
4167
|
+
if (facetsError) {
|
|
4168
|
+
console.error('Failed to load audit log facets:', facetsError);
|
|
4169
|
+
}
|
|
4170
|
+
// Refs for debouncing
|
|
4171
|
+
const debounceTimerRef = useRef(null);
|
|
4172
|
+
const isInitialMount = useRef(true);
|
|
4173
|
+
// Initialize with default time range and trigger initial query
|
|
4174
|
+
useEffect(() => {
|
|
4175
|
+
const defaultRange = TIME_PRESETS$3.find(p => p.key === 'last24hours')?.getValue();
|
|
4176
|
+
if (defaultRange) {
|
|
4177
|
+
setTimeRange(defaultRange);
|
|
4178
|
+
}
|
|
4179
|
+
}, []);
|
|
4180
|
+
const generateCEL = useCallback(() => {
|
|
4181
|
+
const parts = [];
|
|
4182
|
+
if (filters.verb) {
|
|
4183
|
+
parts.push(`verb == "${filters.verb}"`);
|
|
4184
|
+
}
|
|
4185
|
+
if (filters.resourceType) {
|
|
4186
|
+
parts.push(`objectRef.resource == "${filters.resourceType}"`);
|
|
4187
|
+
}
|
|
4188
|
+
if (filters.namespace) {
|
|
4189
|
+
parts.push(`objectRef.namespace == "${filters.namespace}"`);
|
|
4190
|
+
}
|
|
4191
|
+
if (filters.resourceName) {
|
|
4192
|
+
parts.push(`objectRef.name.contains("${filters.resourceName}")`);
|
|
4193
|
+
}
|
|
4194
|
+
if (filters.username) {
|
|
4195
|
+
parts.push(`user.username.contains("${filters.username}")`);
|
|
4196
|
+
}
|
|
4197
|
+
return parts.join(' && ');
|
|
4198
|
+
}, [filters]);
|
|
4199
|
+
// Execute query when filters change (with debouncing for text inputs)
|
|
4200
|
+
const executeQuery = useCallback(() => {
|
|
4201
|
+
if (!timeRange || disabled)
|
|
4202
|
+
return;
|
|
4203
|
+
const filter = useAdvancedFilter ? advancedFilter : generateCEL();
|
|
4204
|
+
onFilterChange({
|
|
4205
|
+
filter,
|
|
4206
|
+
limit,
|
|
4207
|
+
startTime: timeRange.start,
|
|
4208
|
+
endTime: timeRange.end
|
|
4209
|
+
});
|
|
4210
|
+
}, [useAdvancedFilter, advancedFilter, generateCEL, onFilterChange, limit, timeRange, disabled]);
|
|
4211
|
+
// Auto-execute query when dropdown filters or time range change (immediate)
|
|
4212
|
+
useEffect(() => {
|
|
4213
|
+
// Skip the initial mount - we want to wait for timeRange to be set
|
|
4214
|
+
if (isInitialMount.current) {
|
|
4215
|
+
isInitialMount.current = false;
|
|
4216
|
+
return;
|
|
4217
|
+
}
|
|
4218
|
+
// Clear any pending debounced query
|
|
4219
|
+
if (debounceTimerRef.current) {
|
|
4220
|
+
clearTimeout(debounceTimerRef.current);
|
|
4221
|
+
}
|
|
4222
|
+
executeQuery();
|
|
4223
|
+
}, [filters.verb, filters.resourceType, timeRange, limit, useAdvancedFilter]);
|
|
4224
|
+
// Auto-execute query when text filters change (debounced)
|
|
4225
|
+
useEffect(() => {
|
|
4226
|
+
if (isInitialMount.current || !timeRange)
|
|
4227
|
+
return;
|
|
4228
|
+
// Clear previous timer
|
|
4229
|
+
if (debounceTimerRef.current) {
|
|
4230
|
+
clearTimeout(debounceTimerRef.current);
|
|
4231
|
+
}
|
|
4232
|
+
// Set new debounced timer
|
|
4233
|
+
debounceTimerRef.current = setTimeout(() => {
|
|
4234
|
+
executeQuery();
|
|
4235
|
+
}, DEBOUNCE_DELAY);
|
|
4236
|
+
return () => {
|
|
4237
|
+
if (debounceTimerRef.current) {
|
|
4238
|
+
clearTimeout(debounceTimerRef.current);
|
|
4239
|
+
}
|
|
4240
|
+
};
|
|
4241
|
+
}, [filters.namespace, filters.resourceName, filters.username, advancedFilter]);
|
|
4242
|
+
const handleFilterChange = useCallback((field, value) => {
|
|
4243
|
+
setFilters(prev => ({ ...prev, [field]: value }));
|
|
4244
|
+
}, []);
|
|
4245
|
+
const handleTimePresetSelect = useCallback((presetKey) => {
|
|
4246
|
+
const preset = TIME_PRESETS$3.find(p => p.key === presetKey);
|
|
4247
|
+
if (preset) {
|
|
4248
|
+
setSelectedPreset(presetKey);
|
|
4249
|
+
setTimeRange(preset.getValue());
|
|
4250
|
+
}
|
|
4251
|
+
}, []);
|
|
4252
|
+
const handleCustomRangeApply = useCallback((start, end) => {
|
|
4253
|
+
setSelectedPreset('custom');
|
|
4254
|
+
setCustomStart(start);
|
|
4255
|
+
setCustomEnd(end);
|
|
4256
|
+
setTimeRange({
|
|
4257
|
+
start: new Date(start).toISOString(),
|
|
4258
|
+
end: new Date(end).toISOString(),
|
|
4259
|
+
});
|
|
4260
|
+
}, []);
|
|
4261
|
+
const handleLimitChange = useCallback((newLimit) => {
|
|
4262
|
+
const validLimit = Math.min(Math.max(1, newLimit), 1000);
|
|
4263
|
+
setLimit(validLimit);
|
|
4264
|
+
}, []);
|
|
4265
|
+
const handleAdvancedToggle = useCallback(() => {
|
|
4266
|
+
if (!isAdvancedExpanded) {
|
|
4267
|
+
// When expanding, populate advanced filter with generated CEL
|
|
4268
|
+
setAdvancedFilter(generateCEL());
|
|
4269
|
+
}
|
|
4270
|
+
setIsAdvancedExpanded(!isAdvancedExpanded);
|
|
4271
|
+
}, [isAdvancedExpanded, generateCEL]);
|
|
4272
|
+
const handleUseAdvancedChange = useCallback((checked) => {
|
|
4273
|
+
setUseAdvancedFilter(checked === true);
|
|
4274
|
+
}, []);
|
|
4275
|
+
// Get display label for time range
|
|
4276
|
+
const getTimeRangeLabel = () => {
|
|
4277
|
+
if (selectedPreset === 'custom') {
|
|
4278
|
+
if (timeRange?.start && timeRange?.end) {
|
|
4279
|
+
const start = new Date(timeRange.start);
|
|
4280
|
+
const end = new Date(timeRange.end);
|
|
4281
|
+
return `${start.toLocaleDateString()} - ${end.toLocaleDateString()}`;
|
|
4282
|
+
}
|
|
4283
|
+
return 'Custom';
|
|
4284
|
+
}
|
|
4285
|
+
const preset = TIME_PRESETS$3.find(p => p.key === selectedPreset);
|
|
4286
|
+
return preset?.label || 'Select time range';
|
|
4287
|
+
};
|
|
4288
|
+
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
|
|
4289
|
+
.filter(facet => facet.value)
|
|
4290
|
+
.map(facet => ({
|
|
4291
|
+
value: facet.value,
|
|
4292
|
+
label: facet.value,
|
|
4293
|
+
count: facet.count,
|
|
4294
|
+
})), 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
|
|
4295
|
+
.filter(facet => facet.value)
|
|
4296
|
+
.map(facet => ({
|
|
4297
|
+
value: facet.value,
|
|
4298
|
+
label: facet.value,
|
|
4299
|
+
count: facet.count,
|
|
4300
|
+
})), 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
|
|
4301
|
+
.filter(facet => facet.value)
|
|
4302
|
+
.map(facet => ({
|
|
4303
|
+
value: facet.value,
|
|
4304
|
+
label: facet.value,
|
|
4305
|
+
count: facet.count,
|
|
4306
|
+
})), 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
|
|
4307
|
+
.filter(facet => facet.value)
|
|
4308
|
+
.map(facet => ({
|
|
4309
|
+
value: facet.value,
|
|
4310
|
+
label: facet.value,
|
|
4311
|
+
count: facet.count,
|
|
4312
|
+
})), 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 })] }))] }))] }));
|
|
4313
|
+
}
|
|
4214
4314
|
|
|
4215
|
-
|
|
4216
|
-
const __iconNode$m = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
|
|
4217
|
-
const Check = createLucideIcon("check", __iconNode$m);
|
|
4218
|
-
|
|
4219
|
-
/**
|
|
4220
|
-
* @license lucide-react v0.577.0 - ISC
|
|
4221
|
-
*
|
|
4222
|
-
* This source code is licensed under the ISC license.
|
|
4223
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4224
|
-
*/
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
const __iconNode$l = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
|
|
4228
|
-
const ChevronDown = createLucideIcon("chevron-down", __iconNode$l);
|
|
4229
|
-
|
|
4230
|
-
/**
|
|
4231
|
-
* @license lucide-react v0.577.0 - ISC
|
|
4232
|
-
*
|
|
4233
|
-
* This source code is licensed under the ISC license.
|
|
4234
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4235
|
-
*/
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
const __iconNode$k = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
|
|
4239
|
-
const ChevronRight = createLucideIcon("chevron-right", __iconNode$k);
|
|
4240
|
-
|
|
4241
|
-
/**
|
|
4242
|
-
* @license lucide-react v0.577.0 - ISC
|
|
4243
|
-
*
|
|
4244
|
-
* This source code is licensed under the ISC license.
|
|
4245
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4246
|
-
*/
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
const __iconNode$j = [["path", { d: "m18 15-6-6-6 6", key: "153udz" }]];
|
|
4250
|
-
const ChevronUp = createLucideIcon("chevron-up", __iconNode$j);
|
|
4251
|
-
|
|
4252
|
-
/**
|
|
4253
|
-
* @license lucide-react v0.577.0 - ISC
|
|
4254
|
-
*
|
|
4255
|
-
* This source code is licensed under the ISC license.
|
|
4256
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4257
|
-
*/
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
const __iconNode$i = [
|
|
4261
|
-
["path", { d: "m7 15 5 5 5-5", key: "1hf1tw" }],
|
|
4262
|
-
["path", { d: "m7 9 5-5 5 5", key: "sgt6xg" }]
|
|
4263
|
-
];
|
|
4264
|
-
const ChevronsUpDown = createLucideIcon("chevrons-up-down", __iconNode$i);
|
|
4265
|
-
|
|
4266
|
-
/**
|
|
4267
|
-
* @license lucide-react v0.577.0 - ISC
|
|
4268
|
-
*
|
|
4269
|
-
* This source code is licensed under the ISC license.
|
|
4270
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4271
|
-
*/
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
const __iconNode$h = [
|
|
4275
|
-
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
4276
|
-
["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
|
|
4277
|
-
["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
|
|
4278
|
-
];
|
|
4279
|
-
const CircleAlert = createLucideIcon("circle-alert", __iconNode$h);
|
|
4280
|
-
|
|
4281
|
-
/**
|
|
4282
|
-
* @license lucide-react v0.577.0 - ISC
|
|
4283
|
-
*
|
|
4284
|
-
* This source code is licensed under the ISC license.
|
|
4285
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4286
|
-
*/
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
const __iconNode$g = [
|
|
4290
|
-
["path", { d: "M21.801 10A10 10 0 1 1 17 3.335", key: "yps3ct" }],
|
|
4291
|
-
["path", { d: "m9 11 3 3L22 4", key: "1pflzl" }]
|
|
4292
|
-
];
|
|
4293
|
-
const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$g);
|
|
4315
|
+
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}
|
|
4294
4316
|
|
|
4295
4317
|
/**
|
|
4296
|
-
*
|
|
4297
|
-
*
|
|
4298
|
-
* This source code is licensed under the ISC license.
|
|
4299
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4318
|
+
* Concatenates two arrays faster than the array spread operator.
|
|
4300
4319
|
*/
|
|
4320
|
+
const concatArrays = (array1, array2) => {
|
|
4321
|
+
// Pre-allocate for better V8 optimization
|
|
4322
|
+
const combinedArray = new Array(array1.length + array2.length);
|
|
4323
|
+
for (let i = 0; i < array1.length; i++) {
|
|
4324
|
+
combinedArray[i] = array1[i];
|
|
4325
|
+
}
|
|
4326
|
+
for (let i = 0; i < array2.length; i++) {
|
|
4327
|
+
combinedArray[array1.length + i] = array2[i];
|
|
4328
|
+
}
|
|
4329
|
+
return combinedArray;
|
|
4330
|
+
};
|
|
4301
4331
|
|
|
4302
|
-
|
|
4303
|
-
const
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
const
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
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"
|
|
4332
|
+
// Factory function ensures consistent object shapes
|
|
4333
|
+
const createClassValidatorObject = (classGroupId, validator) => ({
|
|
4334
|
+
classGroupId,
|
|
4335
|
+
validator
|
|
4336
|
+
});
|
|
4337
|
+
// Factory ensures consistent ClassPartObject shape
|
|
4338
|
+
const createClassPartObject = (nextPart = new Map(), validators = null, classGroupId) => ({
|
|
4339
|
+
nextPart,
|
|
4340
|
+
validators,
|
|
4341
|
+
classGroupId
|
|
4342
|
+
});
|
|
4343
|
+
const CLASS_PART_SEPARATOR = '-';
|
|
4344
|
+
const EMPTY_CONFLICTS = [];
|
|
4345
|
+
// I use two dots here because one dot is used as prefix for class groups in plugins
|
|
4346
|
+
const ARBITRARY_PROPERTY_PREFIX = 'arbitrary..';
|
|
4347
|
+
const createClassGroupUtils = config => {
|
|
4348
|
+
const classMap = createClassMap(config);
|
|
4349
|
+
const {
|
|
4350
|
+
conflictingClassGroups,
|
|
4351
|
+
conflictingClassGroupModifiers
|
|
4352
|
+
} = config;
|
|
4353
|
+
const getClassGroupId = className => {
|
|
4354
|
+
if (className.startsWith('[') && className.endsWith(']')) {
|
|
4355
|
+
return getGroupIdForArbitraryProperty(className);
|
|
4366
4356
|
}
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
];
|
|
4370
|
-
|
|
4371
|
-
|
|
4357
|
+
const classParts = className.split(CLASS_PART_SEPARATOR);
|
|
4358
|
+
// Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and skip it.
|
|
4359
|
+
const startIndex = classParts[0] === '' && classParts.length > 1 ? 1 : 0;
|
|
4360
|
+
return getGroupRecursive(classParts, startIndex, classMap);
|
|
4361
|
+
};
|
|
4362
|
+
const getConflictingClassGroupIds = (classGroupId, hasPostfixModifier) => {
|
|
4363
|
+
if (hasPostfixModifier) {
|
|
4364
|
+
const modifierConflicts = conflictingClassGroupModifiers[classGroupId];
|
|
4365
|
+
const baseConflicts = conflictingClassGroups[classGroupId];
|
|
4366
|
+
if (modifierConflicts) {
|
|
4367
|
+
if (baseConflicts) {
|
|
4368
|
+
// Merge base conflicts with modifier conflicts
|
|
4369
|
+
return concatArrays(baseConflicts, modifierConflicts);
|
|
4370
|
+
}
|
|
4371
|
+
// Only modifier conflicts
|
|
4372
|
+
return modifierConflicts;
|
|
4373
|
+
}
|
|
4374
|
+
// Fall back to without postfix if no modifier conflicts
|
|
4375
|
+
return baseConflicts || EMPTY_CONFLICTS;
|
|
4376
|
+
}
|
|
4377
|
+
return conflictingClassGroups[classGroupId] || EMPTY_CONFLICTS;
|
|
4378
|
+
};
|
|
4379
|
+
return {
|
|
4380
|
+
getClassGroupId,
|
|
4381
|
+
getConflictingClassGroupIds
|
|
4382
|
+
};
|
|
4383
|
+
};
|
|
4384
|
+
const getGroupRecursive = (classParts, startIndex, classPartObject) => {
|
|
4385
|
+
const classPathsLength = classParts.length - startIndex;
|
|
4386
|
+
if (classPathsLength === 0) {
|
|
4387
|
+
return classPartObject.classGroupId;
|
|
4388
|
+
}
|
|
4389
|
+
const currentClassPart = classParts[startIndex];
|
|
4390
|
+
const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);
|
|
4391
|
+
if (nextClassPartObject) {
|
|
4392
|
+
const result = getGroupRecursive(classParts, startIndex + 1, nextClassPartObject);
|
|
4393
|
+
if (result) return result;
|
|
4394
|
+
}
|
|
4395
|
+
const validators = classPartObject.validators;
|
|
4396
|
+
if (validators === null) {
|
|
4397
|
+
return undefined;
|
|
4398
|
+
}
|
|
4399
|
+
// Build classRest string efficiently by joining from startIndex onwards
|
|
4400
|
+
const classRest = startIndex === 0 ? classParts.join(CLASS_PART_SEPARATOR) : classParts.slice(startIndex).join(CLASS_PART_SEPARATOR);
|
|
4401
|
+
const validatorsLength = validators.length;
|
|
4402
|
+
for (let i = 0; i < validatorsLength; i++) {
|
|
4403
|
+
const validatorObj = validators[i];
|
|
4404
|
+
if (validatorObj.validator(classRest)) {
|
|
4405
|
+
return validatorObj.classGroupId;
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
return undefined;
|
|
4409
|
+
};
|
|
4372
4410
|
/**
|
|
4373
|
-
*
|
|
4411
|
+
* Get the class group ID for an arbitrary property.
|
|
4374
4412
|
*
|
|
4375
|
-
*
|
|
4376
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4413
|
+
* @param className - The class name to get the group ID for. Is expected to be string starting with `[` and ending with `]`.
|
|
4377
4414
|
*/
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
const
|
|
4381
|
-
const
|
|
4382
|
-
|
|
4415
|
+
const getGroupIdForArbitraryProperty = className => className.slice(1, -1).indexOf(':') === -1 ? undefined : (() => {
|
|
4416
|
+
const content = className.slice(1, -1);
|
|
4417
|
+
const colonIndex = content.indexOf(':');
|
|
4418
|
+
const property = content.slice(0, colonIndex);
|
|
4419
|
+
return property ? ARBITRARY_PROPERTY_PREFIX + property : undefined;
|
|
4420
|
+
})();
|
|
4383
4421
|
/**
|
|
4384
|
-
*
|
|
4385
|
-
*
|
|
4386
|
-
* This source code is licensed under the ISC license.
|
|
4387
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
4422
|
+
* Exported for testing only
|
|
4388
4423
|
*/
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4424
|
+
const createClassMap = config => {
|
|
4425
|
+
const {
|
|
4426
|
+
theme,
|
|
4427
|
+
classGroups
|
|
4428
|
+
} = config;
|
|
4429
|
+
return processClassGroups(classGroups, theme);
|
|
4430
|
+
};
|
|
4431
|
+
// Split into separate functions to maintain monomorphic call sites
|
|
4432
|
+
const processClassGroups = (classGroups, theme) => {
|
|
4433
|
+
const classMap = createClassPartObject();
|
|
4434
|
+
for (const classGroupId in classGroups) {
|
|
4435
|
+
const group = classGroups[classGroupId];
|
|
4436
|
+
processClassesRecursively(group, classMap, classGroupId, theme);
|
|
4437
|
+
}
|
|
4438
|
+
return classMap;
|
|
4439
|
+
};
|
|
4440
|
+
const processClassesRecursively = (classGroup, classPartObject, classGroupId, theme) => {
|
|
4441
|
+
const len = classGroup.length;
|
|
4442
|
+
for (let i = 0; i < len; i++) {
|
|
4443
|
+
const classDefinition = classGroup[i];
|
|
4444
|
+
processClassDefinition(classDefinition, classPartObject, classGroupId, theme);
|
|
4445
|
+
}
|
|
4446
|
+
};
|
|
4447
|
+
// Split into separate functions for each type to maintain monomorphic call sites
|
|
4448
|
+
const processClassDefinition = (classDefinition, classPartObject, classGroupId, theme) => {
|
|
4449
|
+
if (typeof classDefinition === 'string') {
|
|
4450
|
+
processStringDefinition(classDefinition, classPartObject, classGroupId);
|
|
4451
|
+
return;
|
|
4452
|
+
}
|
|
4453
|
+
if (typeof classDefinition === 'function') {
|
|
4454
|
+
processFunctionDefinition(classDefinition, classPartObject, classGroupId, theme);
|
|
4455
|
+
return;
|
|
4456
|
+
}
|
|
4457
|
+
processObjectDefinition(classDefinition, classPartObject, classGroupId, theme);
|
|
4458
|
+
};
|
|
4459
|
+
const processStringDefinition = (classDefinition, classPartObject, classGroupId) => {
|
|
4460
|
+
const classPartObjectToEdit = classDefinition === '' ? classPartObject : getPart(classPartObject, classDefinition);
|
|
4461
|
+
classPartObjectToEdit.classGroupId = classGroupId;
|
|
4462
|
+
};
|
|
4463
|
+
const processFunctionDefinition = (classDefinition, classPartObject, classGroupId, theme) => {
|
|
4464
|
+
if (isThemeGetter(classDefinition)) {
|
|
4465
|
+
processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme);
|
|
4466
|
+
return;
|
|
4467
|
+
}
|
|
4468
|
+
if (classPartObject.validators === null) {
|
|
4469
|
+
classPartObject.validators = [];
|
|
4470
|
+
}
|
|
4471
|
+
classPartObject.validators.push(createClassValidatorObject(classGroupId, classDefinition));
|
|
4472
|
+
};
|
|
4473
|
+
const processObjectDefinition = (classDefinition, classPartObject, classGroupId, theme) => {
|
|
4474
|
+
const entries = Object.entries(classDefinition);
|
|
4475
|
+
const len = entries.length;
|
|
4476
|
+
for (let i = 0; i < len; i++) {
|
|
4477
|
+
const [key, value] = entries[i];
|
|
4478
|
+
processClassesRecursively(value, getPart(classPartObject, key), classGroupId, theme);
|
|
4479
|
+
}
|
|
4480
|
+
};
|
|
4481
|
+
const getPart = (classPartObject, path) => {
|
|
4482
|
+
let current = classPartObject;
|
|
4483
|
+
const parts = path.split(CLASS_PART_SEPARATOR);
|
|
4484
|
+
const len = parts.length;
|
|
4485
|
+
for (let i = 0; i < len; i++) {
|
|
4486
|
+
const part = parts[i];
|
|
4487
|
+
let next = current.nextPart.get(part);
|
|
4488
|
+
if (!next) {
|
|
4489
|
+
next = createClassPartObject();
|
|
4490
|
+
current.nextPart.set(part, next);
|
|
4397
4491
|
}
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
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
|
-
*/
|
|
4492
|
+
current = next;
|
|
4493
|
+
}
|
|
4494
|
+
return current;
|
|
4495
|
+
};
|
|
4496
|
+
// Type guard maintains monomorphic check
|
|
4497
|
+
const isThemeGetter = func => 'isThemeGetter' in func && func.isThemeGetter === true;
|
|
4409
4498
|
|
|
4410
|
-
|
|
4411
|
-
const
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
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];
|
|
4499
|
+
// LRU cache implementation using plain objects for simplicity
|
|
4500
|
+
const createLruCache = maxCacheSize => {
|
|
4501
|
+
if (maxCacheSize < 1) {
|
|
4502
|
+
return {
|
|
4503
|
+
get: () => undefined,
|
|
4504
|
+
set: () => {}
|
|
4505
|
+
};
|
|
4560
4506
|
}
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
nextPart,
|
|
4572
|
-
validators,
|
|
4573
|
-
classGroupId
|
|
4574
|
-
});
|
|
4575
|
-
const CLASS_PART_SEPARATOR = '-';
|
|
4576
|
-
const EMPTY_CONFLICTS = [];
|
|
4577
|
-
// I use two dots here because one dot is used as prefix for class groups in plugins
|
|
4578
|
-
const ARBITRARY_PROPERTY_PREFIX = 'arbitrary..';
|
|
4579
|
-
const createClassGroupUtils = config => {
|
|
4580
|
-
const classMap = createClassMap(config);
|
|
4581
|
-
const {
|
|
4582
|
-
conflictingClassGroups,
|
|
4583
|
-
conflictingClassGroupModifiers
|
|
4584
|
-
} = config;
|
|
4585
|
-
const getClassGroupId = className => {
|
|
4586
|
-
if (className.startsWith('[') && className.endsWith(']')) {
|
|
4587
|
-
return getGroupIdForArbitraryProperty(className);
|
|
4507
|
+
let cacheSize = 0;
|
|
4508
|
+
let cache = Object.create(null);
|
|
4509
|
+
let previousCache = Object.create(null);
|
|
4510
|
+
const update = (key, value) => {
|
|
4511
|
+
cache[key] = value;
|
|
4512
|
+
cacheSize++;
|
|
4513
|
+
if (cacheSize > maxCacheSize) {
|
|
4514
|
+
cacheSize = 0;
|
|
4515
|
+
previousCache = cache;
|
|
4516
|
+
cache = Object.create(null);
|
|
4588
4517
|
}
|
|
4589
|
-
const classParts = className.split(CLASS_PART_SEPARATOR);
|
|
4590
|
-
// Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and skip it.
|
|
4591
|
-
const startIndex = classParts[0] === '' && classParts.length > 1 ? 1 : 0;
|
|
4592
|
-
return getGroupRecursive(classParts, startIndex, classMap);
|
|
4593
4518
|
};
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
};
|
|
4611
|
-
return {
|
|
4612
|
-
getClassGroupId,
|
|
4613
|
-
getConflictingClassGroupIds
|
|
4614
|
-
};
|
|
4615
|
-
};
|
|
4616
|
-
const getGroupRecursive = (classParts, startIndex, classPartObject) => {
|
|
4617
|
-
const classPathsLength = classParts.length - startIndex;
|
|
4618
|
-
if (classPathsLength === 0) {
|
|
4619
|
-
return classPartObject.classGroupId;
|
|
4620
|
-
}
|
|
4621
|
-
const currentClassPart = classParts[startIndex];
|
|
4622
|
-
const nextClassPartObject = classPartObject.nextPart.get(currentClassPart);
|
|
4623
|
-
if (nextClassPartObject) {
|
|
4624
|
-
const result = getGroupRecursive(classParts, startIndex + 1, nextClassPartObject);
|
|
4625
|
-
if (result) return result;
|
|
4626
|
-
}
|
|
4627
|
-
const validators = classPartObject.validators;
|
|
4628
|
-
if (validators === null) {
|
|
4629
|
-
return undefined;
|
|
4630
|
-
}
|
|
4631
|
-
// Build classRest string efficiently by joining from startIndex onwards
|
|
4632
|
-
const classRest = startIndex === 0 ? classParts.join(CLASS_PART_SEPARATOR) : classParts.slice(startIndex).join(CLASS_PART_SEPARATOR);
|
|
4633
|
-
const validatorsLength = validators.length;
|
|
4634
|
-
for (let i = 0; i < validatorsLength; i++) {
|
|
4635
|
-
const validatorObj = validators[i];
|
|
4636
|
-
if (validatorObj.validator(classRest)) {
|
|
4637
|
-
return validatorObj.classGroupId;
|
|
4638
|
-
}
|
|
4639
|
-
}
|
|
4640
|
-
return undefined;
|
|
4641
|
-
};
|
|
4642
|
-
/**
|
|
4643
|
-
* Get the class group ID for an arbitrary property.
|
|
4644
|
-
*
|
|
4645
|
-
* @param className - The class name to get the group ID for. Is expected to be string starting with `[` and ending with `]`.
|
|
4646
|
-
*/
|
|
4647
|
-
const getGroupIdForArbitraryProperty = className => className.slice(1, -1).indexOf(':') === -1 ? undefined : (() => {
|
|
4648
|
-
const content = className.slice(1, -1);
|
|
4649
|
-
const colonIndex = content.indexOf(':');
|
|
4650
|
-
const property = content.slice(0, colonIndex);
|
|
4651
|
-
return property ? ARBITRARY_PROPERTY_PREFIX + property : undefined;
|
|
4652
|
-
})();
|
|
4653
|
-
/**
|
|
4654
|
-
* Exported for testing only
|
|
4655
|
-
*/
|
|
4656
|
-
const createClassMap = config => {
|
|
4657
|
-
const {
|
|
4658
|
-
theme,
|
|
4659
|
-
classGroups
|
|
4660
|
-
} = config;
|
|
4661
|
-
return processClassGroups(classGroups, theme);
|
|
4662
|
-
};
|
|
4663
|
-
// Split into separate functions to maintain monomorphic call sites
|
|
4664
|
-
const processClassGroups = (classGroups, theme) => {
|
|
4665
|
-
const classMap = createClassPartObject();
|
|
4666
|
-
for (const classGroupId in classGroups) {
|
|
4667
|
-
const group = classGroups[classGroupId];
|
|
4668
|
-
processClassesRecursively(group, classMap, classGroupId, theme);
|
|
4669
|
-
}
|
|
4670
|
-
return classMap;
|
|
4671
|
-
};
|
|
4672
|
-
const processClassesRecursively = (classGroup, classPartObject, classGroupId, theme) => {
|
|
4673
|
-
const len = classGroup.length;
|
|
4674
|
-
for (let i = 0; i < len; i++) {
|
|
4675
|
-
const classDefinition = classGroup[i];
|
|
4676
|
-
processClassDefinition(classDefinition, classPartObject, classGroupId, theme);
|
|
4677
|
-
}
|
|
4678
|
-
};
|
|
4679
|
-
// Split into separate functions for each type to maintain monomorphic call sites
|
|
4680
|
-
const processClassDefinition = (classDefinition, classPartObject, classGroupId, theme) => {
|
|
4681
|
-
if (typeof classDefinition === 'string') {
|
|
4682
|
-
processStringDefinition(classDefinition, classPartObject, classGroupId);
|
|
4683
|
-
return;
|
|
4684
|
-
}
|
|
4685
|
-
if (typeof classDefinition === 'function') {
|
|
4686
|
-
processFunctionDefinition(classDefinition, classPartObject, classGroupId, theme);
|
|
4687
|
-
return;
|
|
4688
|
-
}
|
|
4689
|
-
processObjectDefinition(classDefinition, classPartObject, classGroupId, theme);
|
|
4690
|
-
};
|
|
4691
|
-
const processStringDefinition = (classDefinition, classPartObject, classGroupId) => {
|
|
4692
|
-
const classPartObjectToEdit = classDefinition === '' ? classPartObject : getPart(classPartObject, classDefinition);
|
|
4693
|
-
classPartObjectToEdit.classGroupId = classGroupId;
|
|
4694
|
-
};
|
|
4695
|
-
const processFunctionDefinition = (classDefinition, classPartObject, classGroupId, theme) => {
|
|
4696
|
-
if (isThemeGetter(classDefinition)) {
|
|
4697
|
-
processClassesRecursively(classDefinition(theme), classPartObject, classGroupId, theme);
|
|
4698
|
-
return;
|
|
4699
|
-
}
|
|
4700
|
-
if (classPartObject.validators === null) {
|
|
4701
|
-
classPartObject.validators = [];
|
|
4702
|
-
}
|
|
4703
|
-
classPartObject.validators.push(createClassValidatorObject(classGroupId, classDefinition));
|
|
4704
|
-
};
|
|
4705
|
-
const processObjectDefinition = (classDefinition, classPartObject, classGroupId, theme) => {
|
|
4706
|
-
const entries = Object.entries(classDefinition);
|
|
4707
|
-
const len = entries.length;
|
|
4708
|
-
for (let i = 0; i < len; i++) {
|
|
4709
|
-
const [key, value] = entries[i];
|
|
4710
|
-
processClassesRecursively(value, getPart(classPartObject, key), classGroupId, theme);
|
|
4711
|
-
}
|
|
4712
|
-
};
|
|
4713
|
-
const getPart = (classPartObject, path) => {
|
|
4714
|
-
let current = classPartObject;
|
|
4715
|
-
const parts = path.split(CLASS_PART_SEPARATOR);
|
|
4716
|
-
const len = parts.length;
|
|
4717
|
-
for (let i = 0; i < len; i++) {
|
|
4718
|
-
const part = parts[i];
|
|
4719
|
-
let next = current.nextPart.get(part);
|
|
4720
|
-
if (!next) {
|
|
4721
|
-
next = createClassPartObject();
|
|
4722
|
-
current.nextPart.set(part, next);
|
|
4723
|
-
}
|
|
4724
|
-
current = next;
|
|
4725
|
-
}
|
|
4726
|
-
return current;
|
|
4727
|
-
};
|
|
4728
|
-
// Type guard maintains monomorphic check
|
|
4729
|
-
const isThemeGetter = func => 'isThemeGetter' in func && func.isThemeGetter === true;
|
|
4730
|
-
|
|
4731
|
-
// LRU cache implementation using plain objects for simplicity
|
|
4732
|
-
const createLruCache = maxCacheSize => {
|
|
4733
|
-
if (maxCacheSize < 1) {
|
|
4734
|
-
return {
|
|
4735
|
-
get: () => undefined,
|
|
4736
|
-
set: () => {}
|
|
4737
|
-
};
|
|
4738
|
-
}
|
|
4739
|
-
let cacheSize = 0;
|
|
4740
|
-
let cache = Object.create(null);
|
|
4741
|
-
let previousCache = Object.create(null);
|
|
4742
|
-
const update = (key, value) => {
|
|
4743
|
-
cache[key] = value;
|
|
4744
|
-
cacheSize++;
|
|
4745
|
-
if (cacheSize > maxCacheSize) {
|
|
4746
|
-
cacheSize = 0;
|
|
4747
|
-
previousCache = cache;
|
|
4748
|
-
cache = Object.create(null);
|
|
4749
|
-
}
|
|
4750
|
-
};
|
|
4751
|
-
return {
|
|
4752
|
-
get(key) {
|
|
4753
|
-
let value = cache[key];
|
|
4754
|
-
if (value !== undefined) {
|
|
4755
|
-
return value;
|
|
4756
|
-
}
|
|
4757
|
-
if ((value = previousCache[key]) !== undefined) {
|
|
4758
|
-
update(key, value);
|
|
4759
|
-
return value;
|
|
4760
|
-
}
|
|
4761
|
-
},
|
|
4762
|
-
set(key, value) {
|
|
4763
|
-
if (key in cache) {
|
|
4764
|
-
cache[key] = value;
|
|
4765
|
-
} else {
|
|
4766
|
-
update(key, value);
|
|
4519
|
+
return {
|
|
4520
|
+
get(key) {
|
|
4521
|
+
let value = cache[key];
|
|
4522
|
+
if (value !== undefined) {
|
|
4523
|
+
return value;
|
|
4524
|
+
}
|
|
4525
|
+
if ((value = previousCache[key]) !== undefined) {
|
|
4526
|
+
update(key, value);
|
|
4527
|
+
return value;
|
|
4528
|
+
}
|
|
4529
|
+
},
|
|
4530
|
+
set(key, value) {
|
|
4531
|
+
if (key in cache) {
|
|
4532
|
+
cache[key] = value;
|
|
4533
|
+
} else {
|
|
4534
|
+
update(key, value);
|
|
4767
4535
|
}
|
|
4768
4536
|
}
|
|
4769
4537
|
};
|
|
@@ -4893,15 +4661,27 @@ const createConfigUtils = config => ({
|
|
|
4893
4661
|
cache: createLruCache(config.cacheSize),
|
|
4894
4662
|
parseClassName: createParseClassName(config),
|
|
4895
4663
|
sortModifiers: createSortModifiers(config),
|
|
4664
|
+
postfixLookupClassGroupIds: createPostfixLookupClassGroupIds(config),
|
|
4896
4665
|
...createClassGroupUtils(config)
|
|
4897
4666
|
});
|
|
4667
|
+
const createPostfixLookupClassGroupIds = config => {
|
|
4668
|
+
const lookup = Object.create(null);
|
|
4669
|
+
const classGroupIds = config.postfixLookupClassGroups;
|
|
4670
|
+
if (classGroupIds) {
|
|
4671
|
+
for (let i = 0; i < classGroupIds.length; i++) {
|
|
4672
|
+
lookup[classGroupIds[i]] = true;
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
return lookup;
|
|
4676
|
+
};
|
|
4898
4677
|
const SPLIT_CLASSES_REGEX = /\s+/;
|
|
4899
4678
|
const mergeClassList = (classList, configUtils) => {
|
|
4900
4679
|
const {
|
|
4901
4680
|
parseClassName,
|
|
4902
4681
|
getClassGroupId,
|
|
4903
4682
|
getConflictingClassGroupIds,
|
|
4904
|
-
sortModifiers
|
|
4683
|
+
sortModifiers,
|
|
4684
|
+
postfixLookupClassGroupIds
|
|
4905
4685
|
} = configUtils;
|
|
4906
4686
|
/**
|
|
4907
4687
|
* Set of classGroupIds in following format:
|
|
@@ -4927,7 +4707,18 @@ const mergeClassList = (classList, configUtils) => {
|
|
|
4927
4707
|
continue;
|
|
4928
4708
|
}
|
|
4929
4709
|
let hasPostfixModifier = !!maybePostfixModifierPosition;
|
|
4930
|
-
let classGroupId
|
|
4710
|
+
let classGroupId;
|
|
4711
|
+
if (hasPostfixModifier) {
|
|
4712
|
+
const baseClassNameWithoutPostfix = baseClassName.substring(0, maybePostfixModifierPosition);
|
|
4713
|
+
classGroupId = getClassGroupId(baseClassNameWithoutPostfix);
|
|
4714
|
+
const classGroupIdWithPostfix = classGroupId && postfixLookupClassGroupIds[classGroupId] ? getClassGroupId(baseClassName) : undefined;
|
|
4715
|
+
if (classGroupIdWithPostfix && classGroupIdWithPostfix !== classGroupId) {
|
|
4716
|
+
classGroupId = classGroupIdWithPostfix;
|
|
4717
|
+
hasPostfixModifier = false;
|
|
4718
|
+
}
|
|
4719
|
+
} else {
|
|
4720
|
+
classGroupId = getClassGroupId(baseClassName);
|
|
4721
|
+
}
|
|
4931
4722
|
if (!classGroupId) {
|
|
4932
4723
|
if (!hasPostfixModifier) {
|
|
4933
4724
|
// Not a Tailwind class
|
|
@@ -5032,6 +4823,7 @@ const fallbackThemeArr = [];
|
|
|
5032
4823
|
const fromTheme = key => {
|
|
5033
4824
|
const themeGetter = theme => theme[key] || fallbackThemeArr;
|
|
5034
4825
|
themeGetter.isThemeGetter = true;
|
|
4826
|
+
themeGetter.themeKey = key;
|
|
5035
4827
|
return themeGetter;
|
|
5036
4828
|
};
|
|
5037
4829
|
const arbitraryValueRegex = /^\[(?:(\w[\w-]*):)?(.+)\]$/i;
|
|
@@ -5039,7 +4831,7 @@ const arbitraryVariableRegex = /^\((?:(\w[\w-]*):)?(.+)\)$/i;
|
|
|
5039
4831
|
const fractionRegex = /^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/;
|
|
5040
4832
|
const tshirtUnitRegex = /^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/;
|
|
5041
4833
|
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)\(.+\)$/;
|
|
4834
|
+
const colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix|color|light-dark)\(.+\)$/;
|
|
5043
4835
|
// Shadow always begins with x and y offset separated by underscore optionally prepended by inset
|
|
5044
4836
|
const shadowRegex = /^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/;
|
|
5045
4837
|
const imageRegex = /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/;
|
|
@@ -5058,6 +4850,7 @@ const isNever = () => false;
|
|
|
5058
4850
|
const isShadow = value => shadowRegex.test(value);
|
|
5059
4851
|
const isImage = value => imageRegex.test(value);
|
|
5060
4852
|
const isAnyNonArbitrary = value => !isArbitraryValue(value) && !isArbitraryVariable(value);
|
|
4853
|
+
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
4854
|
const isArbitrarySize = value => getIsArbitraryValue(value, isLabelSize, isNever);
|
|
5062
4855
|
const isArbitraryValue = value => arbitraryValueRegex.test(value);
|
|
5063
4856
|
const isArbitraryLength = value => getIsArbitraryValue(value, isLabelLength, isLengthOnly);
|
|
@@ -5162,7 +4955,7 @@ const getDefaultConfig = () => {
|
|
|
5162
4955
|
const scaleAlignSecondaryAxis = () => ['start', 'end', 'center', 'stretch', 'center-safe', 'end-safe'];
|
|
5163
4956
|
const scaleMargin = () => ['auto', ...scaleUnambiguousSpacing()];
|
|
5164
4957
|
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()];
|
|
4958
|
+
const scaleSizingInline = () => [themeContainer, isFraction, 'screen', 'full', 'dvw', 'lvw', 'svw', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];
|
|
5166
4959
|
const scaleSizingBlock = () => [isFraction, 'screen', 'full', 'lh', 'dvh', 'lvh', 'svh', 'min', 'max', 'fit', ...scaleUnambiguousSpacing()];
|
|
5167
4960
|
const scaleColor = () => [themeColor, isArbitraryVariable, isArbitraryValue];
|
|
5168
4961
|
const scaleBgPosition = () => [...scalePosition(), isArbitraryVariablePosition, isArbitraryPosition, {
|
|
@@ -5229,12 +5022,24 @@ const getDefaultConfig = () => {
|
|
|
5229
5022
|
* @deprecated since Tailwind CSS v4.0.0
|
|
5230
5023
|
*/
|
|
5231
5024
|
container: ['container'],
|
|
5025
|
+
/**
|
|
5026
|
+
* Container Type
|
|
5027
|
+
* @see https://tailwindcss.com/docs/responsive-design#container-queries
|
|
5028
|
+
*/
|
|
5029
|
+
'container-type': [{
|
|
5030
|
+
'@container': ['', 'normal', 'size', isArbitraryVariable, isArbitraryValue]
|
|
5031
|
+
}],
|
|
5032
|
+
/**
|
|
5033
|
+
* Container Name
|
|
5034
|
+
* @see https://tailwindcss.com/docs/responsive-design#named-containers
|
|
5035
|
+
*/
|
|
5036
|
+
'container-named': [isNamedContainerQuery],
|
|
5232
5037
|
/**
|
|
5233
5038
|
* Columns
|
|
5234
5039
|
* @see https://tailwindcss.com/docs/columns
|
|
5235
5040
|
*/
|
|
5236
5041
|
columns: [{
|
|
5237
|
-
columns: [isNumber, isArbitraryValue, isArbitraryVariable, themeContainer]
|
|
5042
|
+
columns: [isNumber, 'auto', isArbitraryValue, isArbitraryVariable, themeContainer]
|
|
5238
5043
|
}],
|
|
5239
5044
|
/**
|
|
5240
5045
|
* Break After
|
|
@@ -5870,42 +5675,42 @@ const getDefaultConfig = () => {
|
|
|
5870
5675
|
}],
|
|
5871
5676
|
/**
|
|
5872
5677
|
* Inline Size
|
|
5873
|
-
* @see https://tailwindcss.com/docs/
|
|
5678
|
+
* @see https://tailwindcss.com/docs/inline-size
|
|
5874
5679
|
*/
|
|
5875
5680
|
'inline-size': [{
|
|
5876
5681
|
inline: ['auto', ...scaleSizingInline()]
|
|
5877
5682
|
}],
|
|
5878
5683
|
/**
|
|
5879
5684
|
* Min-Inline Size
|
|
5880
|
-
* @see https://tailwindcss.com/docs/min-
|
|
5685
|
+
* @see https://tailwindcss.com/docs/min-inline-size
|
|
5881
5686
|
*/
|
|
5882
5687
|
'min-inline-size': [{
|
|
5883
5688
|
'min-inline': ['auto', ...scaleSizingInline()]
|
|
5884
5689
|
}],
|
|
5885
5690
|
/**
|
|
5886
5691
|
* Max-Inline Size
|
|
5887
|
-
* @see https://tailwindcss.com/docs/max-
|
|
5692
|
+
* @see https://tailwindcss.com/docs/max-inline-size
|
|
5888
5693
|
*/
|
|
5889
5694
|
'max-inline-size': [{
|
|
5890
5695
|
'max-inline': ['none', ...scaleSizingInline()]
|
|
5891
5696
|
}],
|
|
5892
5697
|
/**
|
|
5893
5698
|
* Block Size
|
|
5894
|
-
* @see https://tailwindcss.com/docs/
|
|
5699
|
+
* @see https://tailwindcss.com/docs/block-size
|
|
5895
5700
|
*/
|
|
5896
5701
|
'block-size': [{
|
|
5897
5702
|
block: ['auto', ...scaleSizingBlock()]
|
|
5898
5703
|
}],
|
|
5899
5704
|
/**
|
|
5900
5705
|
* Min-Block Size
|
|
5901
|
-
* @see https://tailwindcss.com/docs/min-
|
|
5706
|
+
* @see https://tailwindcss.com/docs/min-block-size
|
|
5902
5707
|
*/
|
|
5903
5708
|
'min-block-size': [{
|
|
5904
5709
|
'min-block': ['auto', ...scaleSizingBlock()]
|
|
5905
5710
|
}],
|
|
5906
5711
|
/**
|
|
5907
5712
|
* Max-Block Size
|
|
5908
|
-
* @see https://tailwindcss.com/docs/max-
|
|
5713
|
+
* @see https://tailwindcss.com/docs/max-block-size
|
|
5909
5714
|
*/
|
|
5910
5715
|
'max-block-size': [{
|
|
5911
5716
|
'max-block': ['none', ...scaleSizingBlock()]
|
|
@@ -5955,7 +5760,7 @@ const getDefaultConfig = () => {
|
|
|
5955
5760
|
* @see https://tailwindcss.com/docs/max-height
|
|
5956
5761
|
*/
|
|
5957
5762
|
'max-h': [{
|
|
5958
|
-
'max-h': ['screen', 'lh', ...scaleSizing()]
|
|
5763
|
+
'max-h': ['screen', 'lh', 'none', ...scaleSizing()]
|
|
5959
5764
|
}],
|
|
5960
5765
|
// ------------------
|
|
5961
5766
|
// --- Typography ---
|
|
@@ -6054,7 +5859,7 @@ const getDefaultConfig = () => {
|
|
|
6054
5859
|
* @see https://tailwindcss.com/docs/line-height
|
|
6055
5860
|
*/
|
|
6056
5861
|
leading: [{
|
|
6057
|
-
leading: [/** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */
|
|
5862
|
+
leading: ['none', /** Deprecated since Tailwind CSS v4.0.0. @see https://github.com/tailwindlabs/tailwindcss.com/issues/2027#issuecomment-2620152757 */
|
|
6058
5863
|
themeLeading, ...scaleUnambiguousSpacing()]
|
|
6059
5864
|
}],
|
|
6060
5865
|
/**
|
|
@@ -6157,6 +5962,13 @@ const getDefaultConfig = () => {
|
|
|
6157
5962
|
indent: [{
|
|
6158
5963
|
indent: scaleUnambiguousSpacing()
|
|
6159
5964
|
}],
|
|
5965
|
+
/**
|
|
5966
|
+
* Tab Size
|
|
5967
|
+
* @see https://tailwindcss.com/docs/tab-size
|
|
5968
|
+
*/
|
|
5969
|
+
'tab-size': [{
|
|
5970
|
+
tab: [isInteger, isArbitraryVariable, isArbitraryValue]
|
|
5971
|
+
}],
|
|
6160
5972
|
/**
|
|
6161
5973
|
* Vertical Alignment
|
|
6162
5974
|
* @see https://tailwindcss.com/docs/vertical-align
|
|
@@ -6254,7 +6066,7 @@ const getDefaultConfig = () => {
|
|
|
6254
6066
|
to: ['t', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl']
|
|
6255
6067
|
}, isInteger, isArbitraryVariable, isArbitraryValue],
|
|
6256
6068
|
radial: ['', isArbitraryVariable, isArbitraryValue],
|
|
6257
|
-
conic: [isInteger, isArbitraryVariable, isArbitraryValue]
|
|
6069
|
+
conic: ['', isInteger, isArbitraryVariable, isArbitraryValue]
|
|
6258
6070
|
}, isArbitraryVariableImage, isArbitraryImage]
|
|
6259
6071
|
}],
|
|
6260
6072
|
/**
|
|
@@ -6651,7 +6463,9 @@ const getDefaultConfig = () => {
|
|
|
6651
6463
|
shadow: [{
|
|
6652
6464
|
shadow: [
|
|
6653
6465
|
// Deprecated since Tailwind CSS v4.0.0
|
|
6654
|
-
'',
|
|
6466
|
+
'',
|
|
6467
|
+
// Deprecated since Tailwind CSS v4.0.0
|
|
6468
|
+
'inner', 'none', themeShadow, isArbitraryVariableShadow, isArbitraryShadow]
|
|
6655
6469
|
}],
|
|
6656
6470
|
/**
|
|
6657
6471
|
* Box Shadow Color
|
|
@@ -7364,6 +7178,13 @@ const getDefaultConfig = () => {
|
|
|
7364
7178
|
* @see https://tailwindcss.com/docs/translate
|
|
7365
7179
|
*/
|
|
7366
7180
|
'translate-none': ['translate-none'],
|
|
7181
|
+
/**
|
|
7182
|
+
* Zoom
|
|
7183
|
+
* @see https://tailwindcss.com/docs/zoom
|
|
7184
|
+
*/
|
|
7185
|
+
zoom: [{
|
|
7186
|
+
zoom: [isInteger, isArbitraryVariable, isArbitraryValue]
|
|
7187
|
+
}],
|
|
7367
7188
|
// ---------------------
|
|
7368
7189
|
// --- Interactivity ---
|
|
7369
7190
|
// ---------------------
|
|
@@ -7430,6 +7251,34 @@ const getDefaultConfig = () => {
|
|
|
7430
7251
|
'scroll-behavior': [{
|
|
7431
7252
|
scroll: ['auto', 'smooth']
|
|
7432
7253
|
}],
|
|
7254
|
+
/**
|
|
7255
|
+
* Scrollbar Thumb Color
|
|
7256
|
+
* @see https://tailwindcss.com/docs/scrollbar-color
|
|
7257
|
+
*/
|
|
7258
|
+
'scrollbar-thumb-color': [{
|
|
7259
|
+
'scrollbar-thumb': scaleColor()
|
|
7260
|
+
}],
|
|
7261
|
+
/**
|
|
7262
|
+
* Scrollbar Track Color
|
|
7263
|
+
* @see https://tailwindcss.com/docs/scrollbar-color
|
|
7264
|
+
*/
|
|
7265
|
+
'scrollbar-track-color': [{
|
|
7266
|
+
'scrollbar-track': scaleColor()
|
|
7267
|
+
}],
|
|
7268
|
+
/**
|
|
7269
|
+
* Scrollbar Gutter
|
|
7270
|
+
* @see https://tailwindcss.com/docs/scrollbar-gutter
|
|
7271
|
+
*/
|
|
7272
|
+
'scrollbar-gutter': [{
|
|
7273
|
+
'scrollbar-gutter': ['auto', 'stable', 'both']
|
|
7274
|
+
}],
|
|
7275
|
+
/**
|
|
7276
|
+
* Scrollbar Width
|
|
7277
|
+
* @see https://tailwindcss.com/docs/scrollbar-width
|
|
7278
|
+
*/
|
|
7279
|
+
'scrollbar-w': [{
|
|
7280
|
+
scrollbar: ['auto', 'thin', 'none']
|
|
7281
|
+
}],
|
|
7433
7282
|
/**
|
|
7434
7283
|
* Scroll Margin
|
|
7435
7284
|
* @see https://tailwindcss.com/docs/scroll-margin
|
|
@@ -7688,19 +7537,20 @@ const getDefaultConfig = () => {
|
|
|
7688
7537
|
}]
|
|
7689
7538
|
},
|
|
7690
7539
|
conflictingClassGroups: {
|
|
7540
|
+
'container-named': ['container-type'],
|
|
7691
7541
|
overflow: ['overflow-x', 'overflow-y'],
|
|
7692
7542
|
overscroll: ['overscroll-x', 'overscroll-y'],
|
|
7693
7543
|
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'],
|
|
7544
|
+
'inset-x': ['start', 'end', 'right', 'left'],
|
|
7545
|
+
'inset-y': ['inset-bs', 'inset-be', 'top', 'bottom'],
|
|
7696
7546
|
flex: ['basis', 'grow', 'shrink'],
|
|
7697
7547
|
gap: ['gap-x', 'gap-y'],
|
|
7698
7548
|
p: ['px', 'py', 'ps', 'pe', 'pbs', 'pbe', 'pt', 'pr', 'pb', 'pl'],
|
|
7699
|
-
px: ['pr', 'pl'],
|
|
7700
|
-
py: ['pt', 'pb'],
|
|
7549
|
+
px: ['ps', 'pe', 'pr', 'pl'],
|
|
7550
|
+
py: ['pbs', 'pbe', 'pt', 'pb'],
|
|
7701
7551
|
m: ['mx', 'my', 'ms', 'me', 'mbs', 'mbe', 'mt', 'mr', 'mb', 'ml'],
|
|
7702
|
-
mx: ['mr', 'ml'],
|
|
7703
|
-
my: ['mt', 'mb'],
|
|
7552
|
+
mx: ['ms', 'me', 'mr', 'ml'],
|
|
7553
|
+
my: ['mbs', 'mbe', 'mt', 'mb'],
|
|
7704
7554
|
size: ['w', 'h'],
|
|
7705
7555
|
'font-size': ['leading'],
|
|
7706
7556
|
'fvn-normal': ['fvn-ordinal', 'fvn-slashed-zero', 'fvn-figure', 'fvn-spacing', 'fvn-fraction'],
|
|
@@ -7719,19 +7569,19 @@ const getDefaultConfig = () => {
|
|
|
7719
7569
|
'rounded-l': ['rounded-tl', 'rounded-bl'],
|
|
7720
7570
|
'border-spacing': ['border-spacing-x', 'border-spacing-y'],
|
|
7721
7571
|
'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'],
|
|
7572
|
+
'border-w-x': ['border-w-s', 'border-w-e', 'border-w-r', 'border-w-l'],
|
|
7573
|
+
'border-w-y': ['border-w-bs', 'border-w-be', 'border-w-t', 'border-w-b'],
|
|
7724
7574
|
'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'],
|
|
7575
|
+
'border-color-x': ['border-color-s', 'border-color-e', 'border-color-r', 'border-color-l'],
|
|
7576
|
+
'border-color-y': ['border-color-bs', 'border-color-be', 'border-color-t', 'border-color-b'],
|
|
7727
7577
|
translate: ['translate-x', 'translate-y', 'translate-none'],
|
|
7728
7578
|
'translate-none': ['translate', 'translate-x', 'translate-y', 'translate-z'],
|
|
7729
7579
|
'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'],
|
|
7580
|
+
'scroll-mx': ['scroll-ms', 'scroll-me', 'scroll-mr', 'scroll-ml'],
|
|
7581
|
+
'scroll-my': ['scroll-mbs', 'scroll-mbe', 'scroll-mt', 'scroll-mb'],
|
|
7732
7582
|
'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'],
|
|
7583
|
+
'scroll-px': ['scroll-ps', 'scroll-pe', 'scroll-pr', 'scroll-pl'],
|
|
7584
|
+
'scroll-py': ['scroll-pbs', 'scroll-pbe', 'scroll-pt', 'scroll-pb'],
|
|
7735
7585
|
touch: ['touch-x', 'touch-y', 'touch-pz'],
|
|
7736
7586
|
'touch-x': ['touch'],
|
|
7737
7587
|
'touch-y': ['touch'],
|
|
@@ -7740,485 +7590,663 @@ const getDefaultConfig = () => {
|
|
|
7740
7590
|
conflictingClassGroupModifiers: {
|
|
7741
7591
|
'font-size': ['leading']
|
|
7742
7592
|
},
|
|
7593
|
+
postfixLookupClassGroups: ['container-type'],
|
|
7743
7594
|
orderSensitiveModifiers: ['*', '**', 'after', 'backdrop', 'before', 'details-content', 'file', 'first-letter', 'first-line', 'marker', 'placeholder', 'selection']
|
|
7744
7595
|
};
|
|
7745
7596
|
};
|
|
7746
7597
|
const twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);
|
|
7747
7598
|
|
|
7748
|
-
function cn(...inputs) {
|
|
7749
|
-
return twMerge(clsx(inputs));
|
|
7750
|
-
}
|
|
7599
|
+
function cn(...inputs) {
|
|
7600
|
+
return twMerge(clsx(inputs));
|
|
7601
|
+
}
|
|
7602
|
+
|
|
7603
|
+
/**
|
|
7604
|
+
* Get icon for tenant type
|
|
7605
|
+
*/
|
|
7606
|
+
function getTenantIcon(type) {
|
|
7607
|
+
switch (type) {
|
|
7608
|
+
case "organization":
|
|
7609
|
+
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" }) }));
|
|
7610
|
+
case "project":
|
|
7611
|
+
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" }) }));
|
|
7612
|
+
case "user":
|
|
7613
|
+
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" }) }));
|
|
7614
|
+
case "platform":
|
|
7615
|
+
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" }) }));
|
|
7616
|
+
default:
|
|
7617
|
+
return null;
|
|
7618
|
+
}
|
|
7619
|
+
}
|
|
7620
|
+
/**
|
|
7621
|
+
* Get badge variant based on tenant type
|
|
7622
|
+
*/
|
|
7623
|
+
function getTenantBadgeVariant(type) {
|
|
7624
|
+
switch (type) {
|
|
7625
|
+
case "organization":
|
|
7626
|
+
return "default";
|
|
7627
|
+
case "project":
|
|
7628
|
+
return "secondary";
|
|
7629
|
+
case "user":
|
|
7630
|
+
return "default";
|
|
7631
|
+
case "platform":
|
|
7632
|
+
return "default";
|
|
7633
|
+
default:
|
|
7634
|
+
return "secondary";
|
|
7635
|
+
}
|
|
7636
|
+
}
|
|
7637
|
+
/**
|
|
7638
|
+
* TenantBadge displays tenant information in a compact badge format
|
|
7639
|
+
* Renders as a clickable link if tenantLinkResolver is provided and returns a URL
|
|
7640
|
+
*/
|
|
7641
|
+
function TenantBadge({ tenant, tenantLinkResolver, className = "", size = "default", }) {
|
|
7642
|
+
const icon = getTenantIcon(tenant.type);
|
|
7643
|
+
const variant = getTenantBadgeVariant(tenant.type);
|
|
7644
|
+
const url = tenantLinkResolver?.(tenant);
|
|
7645
|
+
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 })] }))] }));
|
|
7646
|
+
if (url) {
|
|
7647
|
+
return (jsx("a", { href: url, className: "no-underline", onClick: (e) => e.stopPropagation(), title: `View ${tenant.type}: ${tenant.name}`, children: badgeContent }));
|
|
7648
|
+
}
|
|
7649
|
+
return badgeContent;
|
|
7650
|
+
}
|
|
7651
|
+
|
|
7652
|
+
/**
|
|
7653
|
+
* Extract tenant information from audit event annotations if present
|
|
7654
|
+
* Expected annotations: tenant.type and tenant.name
|
|
7655
|
+
*/
|
|
7656
|
+
function extractTenantFromAnnotations(event) {
|
|
7657
|
+
const annotations = event.annotations;
|
|
7658
|
+
if (!annotations)
|
|
7659
|
+
return undefined;
|
|
7660
|
+
const tenantType = annotations["tenant.type"];
|
|
7661
|
+
const tenantName = annotations["tenant.name"];
|
|
7662
|
+
if (tenantType && tenantName) {
|
|
7663
|
+
const validTypes = [
|
|
7664
|
+
"platform",
|
|
7665
|
+
"organization",
|
|
7666
|
+
"project",
|
|
7667
|
+
"user",
|
|
7668
|
+
];
|
|
7669
|
+
if (validTypes.includes(tenantType)) {
|
|
7670
|
+
return {
|
|
7671
|
+
type: tenantType,
|
|
7672
|
+
name: tenantName,
|
|
7673
|
+
};
|
|
7674
|
+
}
|
|
7675
|
+
}
|
|
7676
|
+
return undefined;
|
|
7677
|
+
}
|
|
7678
|
+
/**
|
|
7679
|
+
* AuditEventViewer displays a list of audit events with details
|
|
7680
|
+
*/
|
|
7681
|
+
function AuditEventViewer({ events, className = "", onEventSelect, tenantLinkResolver, }) {
|
|
7682
|
+
const [selectedEvent, setSelectedEvent] = useState(null);
|
|
7683
|
+
const [expandedEvents, setExpandedEvents] = useState(new Set());
|
|
7684
|
+
const toggleEventExpansion = (auditId) => {
|
|
7685
|
+
const newExpanded = new Set(expandedEvents);
|
|
7686
|
+
if (expandedEvents.has(auditId)) {
|
|
7687
|
+
newExpanded.delete(auditId);
|
|
7688
|
+
}
|
|
7689
|
+
else {
|
|
7690
|
+
newExpanded.add(auditId);
|
|
7691
|
+
}
|
|
7692
|
+
setExpandedEvents(newExpanded);
|
|
7693
|
+
};
|
|
7694
|
+
const handleEventClick = (event) => {
|
|
7695
|
+
setSelectedEvent(event);
|
|
7696
|
+
if (onEventSelect) {
|
|
7697
|
+
onEventSelect(event);
|
|
7698
|
+
}
|
|
7699
|
+
};
|
|
7700
|
+
const formatTimestamp = (timestamp) => {
|
|
7701
|
+
if (!timestamp)
|
|
7702
|
+
return "N/A";
|
|
7703
|
+
try {
|
|
7704
|
+
return format(new Date(timestamp), "yyyy-MM-dd HH:mm:ss");
|
|
7705
|
+
}
|
|
7706
|
+
catch {
|
|
7707
|
+
return timestamp;
|
|
7708
|
+
}
|
|
7709
|
+
};
|
|
7710
|
+
const getVerbBadgeVariant = (verb) => {
|
|
7711
|
+
switch (verb?.toLowerCase()) {
|
|
7712
|
+
case "create":
|
|
7713
|
+
return "success";
|
|
7714
|
+
case "update":
|
|
7715
|
+
case "patch":
|
|
7716
|
+
return "warning";
|
|
7717
|
+
case "delete":
|
|
7718
|
+
return "destructive";
|
|
7719
|
+
case "get":
|
|
7720
|
+
case "list":
|
|
7721
|
+
case "watch":
|
|
7722
|
+
return "default";
|
|
7723
|
+
default:
|
|
7724
|
+
return "secondary";
|
|
7725
|
+
}
|
|
7726
|
+
};
|
|
7727
|
+
if (events.length === 0) {
|
|
7728
|
+
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" }) }));
|
|
7729
|
+
}
|
|
7730
|
+
return (jsx("div", { className: `bg-muted rounded-lg border border-border ${className}`, children: jsx("div", { className: "p-4", children: events.map((event) => {
|
|
7731
|
+
const auditId = event.auditID || "";
|
|
7732
|
+
const isExpanded = expandedEvents.has(auditId);
|
|
7733
|
+
const tenant = extractTenantFromAnnotations(event);
|
|
7734
|
+
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
|
|
7735
|
+
? "border-primary bg-primary/5 shadow-md"
|
|
7736
|
+
: ""}`, 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) => {
|
|
7737
|
+
e.stopPropagation();
|
|
7738
|
+
toggleEventExpansion(auditId);
|
|
7739
|
+
}, 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 &&
|
|
7740
|
+
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));
|
|
7741
|
+
}) }) }));
|
|
7742
|
+
}
|
|
7743
|
+
|
|
7744
|
+
/**
|
|
7745
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7746
|
+
*
|
|
7747
|
+
* This source code is licensed under the ISC license.
|
|
7748
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7749
|
+
*/
|
|
7750
|
+
|
|
7751
|
+
const mergeClasses = (...classes) => classes.filter((className, index, array) => {
|
|
7752
|
+
return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
|
|
7753
|
+
}).join(" ").trim();
|
|
7754
|
+
|
|
7755
|
+
/**
|
|
7756
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7757
|
+
*
|
|
7758
|
+
* This source code is licensed under the ISC license.
|
|
7759
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7760
|
+
*/
|
|
7761
|
+
|
|
7762
|
+
const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
7763
|
+
|
|
7764
|
+
/**
|
|
7765
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7766
|
+
*
|
|
7767
|
+
* This source code is licensed under the ISC license.
|
|
7768
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7769
|
+
*/
|
|
7770
|
+
|
|
7771
|
+
const toCamelCase = (string) => string.replace(
|
|
7772
|
+
/^([A-Z])|[\s-_]+(\w)/g,
|
|
7773
|
+
(match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()
|
|
7774
|
+
);
|
|
7775
|
+
|
|
7776
|
+
/**
|
|
7777
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7778
|
+
*
|
|
7779
|
+
* This source code is licensed under the ISC license.
|
|
7780
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7781
|
+
*/
|
|
7782
|
+
|
|
7783
|
+
|
|
7784
|
+
const toPascalCase = (string) => {
|
|
7785
|
+
const camelCase = toCamelCase(string);
|
|
7786
|
+
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
|
|
7787
|
+
};
|
|
7788
|
+
|
|
7789
|
+
/**
|
|
7790
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7791
|
+
*
|
|
7792
|
+
* This source code is licensed under the ISC license.
|
|
7793
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7794
|
+
*/
|
|
7795
|
+
|
|
7796
|
+
var defaultAttributes = {
|
|
7797
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
7798
|
+
width: 24,
|
|
7799
|
+
height: 24,
|
|
7800
|
+
viewBox: "0 0 24 24",
|
|
7801
|
+
fill: "none",
|
|
7802
|
+
stroke: "currentColor",
|
|
7803
|
+
strokeWidth: 2,
|
|
7804
|
+
strokeLinecap: "round",
|
|
7805
|
+
strokeLinejoin: "round"
|
|
7806
|
+
};
|
|
7807
|
+
|
|
7808
|
+
/**
|
|
7809
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7810
|
+
*
|
|
7811
|
+
* This source code is licensed under the ISC license.
|
|
7812
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7813
|
+
*/
|
|
7814
|
+
|
|
7815
|
+
const hasA11yProp = (props) => {
|
|
7816
|
+
for (const prop in props) {
|
|
7817
|
+
if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
|
|
7818
|
+
return true;
|
|
7819
|
+
}
|
|
7820
|
+
}
|
|
7821
|
+
return false;
|
|
7822
|
+
};
|
|
7823
|
+
|
|
7824
|
+
/**
|
|
7825
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7826
|
+
*
|
|
7827
|
+
* This source code is licensed under the ISC license.
|
|
7828
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7829
|
+
*/
|
|
7830
|
+
|
|
7831
|
+
|
|
7832
|
+
const Icon = forwardRef(
|
|
7833
|
+
({
|
|
7834
|
+
color = "currentColor",
|
|
7835
|
+
size = 24,
|
|
7836
|
+
strokeWidth = 2,
|
|
7837
|
+
absoluteStrokeWidth,
|
|
7838
|
+
className = "",
|
|
7839
|
+
children,
|
|
7840
|
+
iconNode,
|
|
7841
|
+
...rest
|
|
7842
|
+
}, ref) => createElement(
|
|
7843
|
+
"svg",
|
|
7844
|
+
{
|
|
7845
|
+
ref,
|
|
7846
|
+
...defaultAttributes,
|
|
7847
|
+
width: size,
|
|
7848
|
+
height: size,
|
|
7849
|
+
stroke: color,
|
|
7850
|
+
strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
|
|
7851
|
+
className: mergeClasses("lucide", className),
|
|
7852
|
+
...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
|
|
7853
|
+
...rest
|
|
7854
|
+
},
|
|
7855
|
+
[
|
|
7856
|
+
...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
|
|
7857
|
+
...Array.isArray(children) ? children : [children]
|
|
7858
|
+
]
|
|
7859
|
+
)
|
|
7860
|
+
);
|
|
7861
|
+
|
|
7862
|
+
/**
|
|
7863
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7864
|
+
*
|
|
7865
|
+
* This source code is licensed under the ISC license.
|
|
7866
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7867
|
+
*/
|
|
7868
|
+
|
|
7869
|
+
|
|
7870
|
+
const createLucideIcon = (iconName, iconNode) => {
|
|
7871
|
+
const Component = forwardRef(
|
|
7872
|
+
({ className, ...props }, ref) => createElement(Icon, {
|
|
7873
|
+
ref,
|
|
7874
|
+
iconNode,
|
|
7875
|
+
className: mergeClasses(
|
|
7876
|
+
`lucide-${toKebabCase(toPascalCase(iconName))}`,
|
|
7877
|
+
`lucide-${iconName}`,
|
|
7878
|
+
className
|
|
7879
|
+
),
|
|
7880
|
+
...props
|
|
7881
|
+
})
|
|
7882
|
+
);
|
|
7883
|
+
Component.displayName = toPascalCase(iconName);
|
|
7884
|
+
return Component;
|
|
7885
|
+
};
|
|
7886
|
+
|
|
7887
|
+
/**
|
|
7888
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7889
|
+
*
|
|
7890
|
+
* This source code is licensed under the ISC license.
|
|
7891
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7892
|
+
*/
|
|
7893
|
+
|
|
7894
|
+
|
|
7895
|
+
const __iconNode$n = [
|
|
7896
|
+
[
|
|
7897
|
+
"path",
|
|
7898
|
+
{
|
|
7899
|
+
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",
|
|
7900
|
+
key: "169zse"
|
|
7901
|
+
}
|
|
7902
|
+
]
|
|
7903
|
+
];
|
|
7904
|
+
const Activity = createLucideIcon("activity", __iconNode$n);
|
|
7905
|
+
|
|
7906
|
+
/**
|
|
7907
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7908
|
+
*
|
|
7909
|
+
* This source code is licensed under the ISC license.
|
|
7910
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7911
|
+
*/
|
|
7912
|
+
|
|
7913
|
+
|
|
7914
|
+
const __iconNode$m = [
|
|
7915
|
+
["path", { d: "M10.268 21a2 2 0 0 0 3.464 0", key: "vwvbt9" }],
|
|
7916
|
+
[
|
|
7917
|
+
"path",
|
|
7918
|
+
{
|
|
7919
|
+
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",
|
|
7920
|
+
key: "11g9vi"
|
|
7921
|
+
}
|
|
7922
|
+
]
|
|
7923
|
+
];
|
|
7924
|
+
const Bell = createLucideIcon("bell", __iconNode$m);
|
|
7751
7925
|
|
|
7752
7926
|
/**
|
|
7753
|
-
*
|
|
7754
|
-
*
|
|
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.
|
|
7755
7931
|
*/
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
const selectedOption = options.find((opt) => opt.value === value);
|
|
7761
|
-
const displayValue = selectedOption
|
|
7762
|
-
? selectedOption.count !== undefined
|
|
7763
|
-
? `${selectedOption.label} (${selectedOption.count})`
|
|
7764
|
-
: selectedOption.label
|
|
7765
|
-
: value === '' && showAllOption
|
|
7766
|
-
? allOptionLabel
|
|
7767
|
-
: placeholder;
|
|
7768
|
-
const handleSelect = React.useCallback((selectedValue) => {
|
|
7769
|
-
onValueChange(selectedValue === value ? '' : selectedValue);
|
|
7770
|
-
setOpen(false);
|
|
7771
|
-
setSearch('');
|
|
7772
|
-
}, [onValueChange, value]);
|
|
7773
|
-
const handleClear = React.useCallback((e) => {
|
|
7774
|
-
e.stopPropagation();
|
|
7775
|
-
onValueChange('');
|
|
7776
|
-
}, [onValueChange]);
|
|
7777
|
-
// Custom fuzzy filter function
|
|
7778
|
-
const filterOptions = React.useCallback((optionValue, searchQuery) => {
|
|
7779
|
-
if (!searchQuery)
|
|
7780
|
-
return 1;
|
|
7781
|
-
const option = options.find((o) => o.value === optionValue);
|
|
7782
|
-
const label = option?.label || optionValue;
|
|
7783
|
-
const lowerLabel = label.toLowerCase();
|
|
7784
|
-
const lowerSearch = searchQuery.toLowerCase();
|
|
7785
|
-
// Exact match
|
|
7786
|
-
if (lowerLabel === lowerSearch)
|
|
7787
|
-
return 1;
|
|
7788
|
-
// Starts with
|
|
7789
|
-
if (lowerLabel.startsWith(lowerSearch))
|
|
7790
|
-
return 0.9;
|
|
7791
|
-
// Contains
|
|
7792
|
-
if (lowerLabel.includes(lowerSearch))
|
|
7793
|
-
return 0.8;
|
|
7794
|
-
// Fuzzy match - check if all search chars appear in order
|
|
7795
|
-
let searchIdx = 0;
|
|
7796
|
-
for (let i = 0; i < lowerLabel.length && searchIdx < lowerSearch.length; i++) {
|
|
7797
|
-
if (lowerLabel[i] === lowerSearch[searchIdx]) {
|
|
7798
|
-
searchIdx++;
|
|
7799
|
-
}
|
|
7800
|
-
}
|
|
7801
|
-
if (searchIdx === lowerSearch.length)
|
|
7802
|
-
return 0.6;
|
|
7803
|
-
return 0;
|
|
7804
|
-
}, [options]);
|
|
7805
|
-
return (jsxs(Popover.Root, { open: open, onOpenChange: setOpen, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs("button", { type: "button", role: "combobox", "aria-expanded": open, "aria-haspopup": "listbox", disabled: disabled || loading, className: cn('flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background', 'placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', 'disabled:cursor-not-allowed disabled:opacity-50', className), children: [jsx("span", { className: "truncate text-left flex-1", children: loading ? 'Loading...' : displayValue }), jsxs("div", { className: "flex items-center gap-1 ml-2", children: [clearable && value && !disabled && (jsx("span", { role: "button", tabIndex: 0, onClick: handleClear, onKeyDown: (e) => {
|
|
7806
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
7807
|
-
handleClear(e);
|
|
7808
|
-
}
|
|
7809
|
-
}, className: "rounded-sm opacity-50 hover:opacity-100 cursor-pointer", children: jsx(X, { className: "h-3 w-3" }) })), jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50" })] })] }) }), jsx(Popover.Portal, { children: jsx(Popover.Content, { className: cn('z-50 min-w-[var(--radix-popover-trigger-width)] overflow-hidden rounded-md border shadow-md', 'bg-white dark:bg-slate-900 text-foreground', 'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95', 'data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2'), sideOffset: 4, align: "start", children: jsxs(Command, { filter: filterOptions, className: "w-full", children: [jsx("div", { className: "flex items-center border-b px-3", children: jsx(CommandInput, { placeholder: searchPlaceholder, value: search, onValueChange: setSearch, className: "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50" }) }), jsxs(CommandList, { className: "max-h-[300px] overflow-y-auto p-1", children: [jsx(CommandEmpty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }), jsxs(CommandGroup, { children: [showAllOption && (jsxs(CommandItem, { value: "_all", onSelect: () => handleSelect(''), className: cn('relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', 'data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground', 'hover:bg-accent hover:text-accent-foreground'), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', value === '' ? 'opacity-100' : 'opacity-0') }), allOptionLabel] })), options.map((option) => (jsxs(CommandItem, { value: option.value, onSelect: () => handleSelect(option.value), className: cn('relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none', 'data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground', 'hover:bg-accent hover:text-accent-foreground'), children: [jsx(Check, { className: cn('mr-2 h-4 w-4', value === option.value ? 'opacity-100' : 'opacity-0') }), jsx("span", { className: "flex-1 truncate", children: option.label }), option.count !== undefined && (jsxs("span", { className: "ml-2 text-xs text-muted-foreground", children: ["(", option.count, ")"] }))] }, option.value)))] })] })] }) }) })] }));
|
|
7810
|
-
}
|
|
7932
|
+
|
|
7933
|
+
|
|
7934
|
+
const __iconNode$l = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
|
|
7935
|
+
const Check = createLucideIcon("check", __iconNode$l);
|
|
7811
7936
|
|
|
7812
7937
|
/**
|
|
7813
|
-
*
|
|
7938
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7939
|
+
*
|
|
7940
|
+
* This source code is licensed under the ISC license.
|
|
7941
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7814
7942
|
*/
|
|
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
7943
|
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7944
|
+
|
|
7945
|
+
const __iconNode$k = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
|
|
7946
|
+
const ChevronDown = createLucideIcon("chevron-down", __iconNode$k);
|
|
7947
|
+
|
|
7948
|
+
/**
|
|
7949
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7950
|
+
*
|
|
7951
|
+
* This source code is licensed under the ISC license.
|
|
7952
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7953
|
+
*/
|
|
7954
|
+
|
|
7955
|
+
|
|
7956
|
+
const __iconNode$j = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
|
|
7957
|
+
const ChevronRight = createLucideIcon("chevron-right", __iconNode$j);
|
|
7958
|
+
|
|
7959
|
+
/**
|
|
7960
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7961
|
+
*
|
|
7962
|
+
* This source code is licensed under the ISC license.
|
|
7963
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7964
|
+
*/
|
|
7965
|
+
|
|
7966
|
+
|
|
7967
|
+
const __iconNode$i = [["path", { d: "m18 15-6-6-6 6", key: "153udz" }]];
|
|
7968
|
+
const ChevronUp = createLucideIcon("chevron-up", __iconNode$i);
|
|
7969
|
+
|
|
7970
|
+
/**
|
|
7971
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7972
|
+
*
|
|
7973
|
+
* This source code is licensed under the ISC license.
|
|
7974
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7975
|
+
*/
|
|
7976
|
+
|
|
7977
|
+
|
|
7978
|
+
const __iconNode$h = [
|
|
7979
|
+
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
7980
|
+
["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
|
|
7981
|
+
["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
|
|
7982
|
+
];
|
|
7983
|
+
const CircleAlert = createLucideIcon("circle-alert", __iconNode$h);
|
|
7984
|
+
|
|
7985
|
+
/**
|
|
7986
|
+
* @license lucide-react v0.577.0 - ISC
|
|
7987
|
+
*
|
|
7988
|
+
* This source code is licensed under the ISC license.
|
|
7989
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7990
|
+
*/
|
|
7991
|
+
|
|
7992
|
+
|
|
7993
|
+
const __iconNode$g = [
|
|
7994
|
+
["path", { d: "M21.801 10A10 10 0 1 1 17 3.335", key: "yps3ct" }],
|
|
7995
|
+
["path", { d: "m9 11 3 3L22 4", key: "1pflzl" }]
|
|
7996
|
+
];
|
|
7997
|
+
const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$g);
|
|
7998
|
+
|
|
7999
|
+
/**
|
|
8000
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8001
|
+
*
|
|
8002
|
+
* This source code is licensed under the ISC license.
|
|
8003
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8004
|
+
*/
|
|
8005
|
+
|
|
8006
|
+
|
|
8007
|
+
const __iconNode$f = [
|
|
8008
|
+
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
8009
|
+
["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
|
|
8010
|
+
];
|
|
8011
|
+
const CircleCheck = createLucideIcon("circle-check", __iconNode$f);
|
|
8012
|
+
|
|
8013
|
+
/**
|
|
8014
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8015
|
+
*
|
|
8016
|
+
* This source code is licensed under the ISC license.
|
|
8017
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8018
|
+
*/
|
|
8019
|
+
|
|
8020
|
+
|
|
8021
|
+
const __iconNode$e = [
|
|
8022
|
+
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
8023
|
+
["path", { d: "m15 9-6 6", key: "1uzhvr" }],
|
|
8024
|
+
["path", { d: "m9 9 6 6", key: "z0biqf" }]
|
|
8025
|
+
];
|
|
8026
|
+
const CircleX = createLucideIcon("circle-x", __iconNode$e);
|
|
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$d = [
|
|
8037
|
+
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
8038
|
+
["path", { d: "M12 6v6l4 2", key: "mmk7yg" }]
|
|
8039
|
+
];
|
|
8040
|
+
const Clock = createLucideIcon("clock", __iconNode$d);
|
|
8041
|
+
|
|
8042
|
+
/**
|
|
8043
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8044
|
+
*
|
|
8045
|
+
* This source code is licensed under the ISC license.
|
|
8046
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8047
|
+
*/
|
|
8048
|
+
|
|
8049
|
+
|
|
8050
|
+
const __iconNode$c = [
|
|
8051
|
+
["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
|
|
8052
|
+
["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" }]
|
|
8053
|
+
];
|
|
8054
|
+
const Copy = createLucideIcon("copy", __iconNode$c);
|
|
8055
|
+
|
|
8056
|
+
/**
|
|
8057
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8058
|
+
*
|
|
8059
|
+
* This source code is licensed under the ISC license.
|
|
8060
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8061
|
+
*/
|
|
8062
|
+
|
|
8063
|
+
|
|
8064
|
+
const __iconNode$b = [
|
|
8065
|
+
[
|
|
8066
|
+
"path",
|
|
7883
8067
|
{
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
8068
|
+
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",
|
|
8069
|
+
key: "1nclc0"
|
|
8070
|
+
}
|
|
8071
|
+
],
|
|
8072
|
+
["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }]
|
|
8073
|
+
];
|
|
8074
|
+
const Eye = createLucideIcon("eye", __iconNode$b);
|
|
8075
|
+
|
|
8076
|
+
/**
|
|
8077
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8078
|
+
*
|
|
8079
|
+
* This source code is licensed under the ISC license.
|
|
8080
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8081
|
+
*/
|
|
8082
|
+
|
|
8083
|
+
|
|
8084
|
+
const __iconNode$a = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
|
|
8085
|
+
const LoaderCircle = createLucideIcon("loader-circle", __iconNode$a);
|
|
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$9 = [
|
|
8096
|
+
[
|
|
8097
|
+
"path",
|
|
7891
8098
|
{
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
}),
|
|
7898
|
-
},
|
|
8099
|
+
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",
|
|
8100
|
+
key: "1a8usu"
|
|
8101
|
+
}
|
|
8102
|
+
],
|
|
8103
|
+
["path", { d: "m15 5 4 4", key: "1mk7zo" }]
|
|
7899
8104
|
];
|
|
7900
|
-
|
|
7901
|
-
|
|
8105
|
+
const Pencil = createLucideIcon("pencil", __iconNode$9);
|
|
8106
|
+
|
|
7902
8107
|
/**
|
|
7903
|
-
*
|
|
8108
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8109
|
+
*
|
|
8110
|
+
* This source code is licensed under the ISC license.
|
|
8111
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7904
8112
|
*/
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
|
7913
|
-
};
|
|
7914
|
-
function SimpleQueryBuilder({ client, onFilterChange, initialLimit = 100, className = '', disabled = false, }) {
|
|
7915
|
-
const [isAdvancedExpanded, setIsAdvancedExpanded] = useState(false);
|
|
7916
|
-
const [filters, setFilters] = useState({
|
|
7917
|
-
verb: '',
|
|
7918
|
-
resourceType: '',
|
|
7919
|
-
namespace: '',
|
|
7920
|
-
resourceName: '',
|
|
7921
|
-
username: '',
|
|
7922
|
-
});
|
|
7923
|
-
const [limit, setLimit] = useState(initialLimit);
|
|
7924
|
-
const [advancedFilter, setAdvancedFilter] = useState('');
|
|
7925
|
-
const [useAdvancedFilter, setUseAdvancedFilter] = useState(false);
|
|
7926
|
-
// Time range state
|
|
7927
|
-
const [selectedPreset, setSelectedPreset] = useState('last24hours');
|
|
7928
|
-
const [timeRange, setTimeRange] = useState(null);
|
|
7929
|
-
const [customStart, setCustomStart] = useState(() => formatDatetimeLocal$4(formatISO(subDays(new Date(), 1))));
|
|
7930
|
-
const [customEnd, setCustomEnd] = useState(() => formatDatetimeLocal$4(formatISO(new Date())));
|
|
7931
|
-
// Fetch facets for dynamic filter options
|
|
7932
|
-
const { verbs, resources, namespaces, usernames, isLoading: facetsLoading, error: facetsError } = useAuditLogFacets(client, timeRange);
|
|
7933
|
-
// Log facets error for debugging
|
|
7934
|
-
if (facetsError) {
|
|
7935
|
-
console.error('Failed to load audit log facets:', facetsError);
|
|
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
|
-
}
|
|
8113
|
+
|
|
8114
|
+
|
|
8115
|
+
const __iconNode$8 = [
|
|
8116
|
+
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
8117
|
+
["path", { d: "M12 5v14", key: "s699le" }]
|
|
8118
|
+
];
|
|
8119
|
+
const Plus = createLucideIcon("plus", __iconNode$8);
|
|
8081
8120
|
|
|
8082
8121
|
/**
|
|
8083
|
-
*
|
|
8122
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8123
|
+
*
|
|
8124
|
+
* This source code is licensed under the ISC license.
|
|
8125
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8084
8126
|
*/
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8127
|
+
|
|
8128
|
+
|
|
8129
|
+
const __iconNode$7 = [
|
|
8130
|
+
["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
|
|
8131
|
+
["path", { d: "M21 3v5h-5", key: "1q7to0" }],
|
|
8132
|
+
["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
|
|
8133
|
+
["path", { d: "M8 16H3v5", key: "1cv678" }]
|
|
8134
|
+
];
|
|
8135
|
+
const RefreshCw = createLucideIcon("refresh-cw", __iconNode$7);
|
|
8136
|
+
|
|
8137
|
+
/**
|
|
8138
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8139
|
+
*
|
|
8140
|
+
* This source code is licensed under the ISC license.
|
|
8141
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8142
|
+
*/
|
|
8143
|
+
|
|
8144
|
+
|
|
8145
|
+
const __iconNode$6 = [
|
|
8146
|
+
["path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8", key: "1p45f6" }],
|
|
8147
|
+
["path", { d: "M21 3v5h-5", key: "1q7to0" }]
|
|
8148
|
+
];
|
|
8149
|
+
const RotateCw = createLucideIcon("rotate-cw", __iconNode$6);
|
|
8150
|
+
|
|
8151
|
+
/**
|
|
8152
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8153
|
+
*
|
|
8154
|
+
* This source code is licensed under the ISC license.
|
|
8155
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8156
|
+
*/
|
|
8157
|
+
|
|
8158
|
+
|
|
8159
|
+
const __iconNode$5 = [
|
|
8160
|
+
["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
|
|
8161
|
+
["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
|
|
8162
|
+
];
|
|
8163
|
+
const Search = createLucideIcon("search", __iconNode$5);
|
|
8164
|
+
|
|
8099
8165
|
/**
|
|
8100
|
-
*
|
|
8166
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8167
|
+
*
|
|
8168
|
+
* This source code is licensed under the ISC license.
|
|
8169
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8101
8170
|
*/
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
return "default";
|
|
8112
|
-
default:
|
|
8113
|
-
return "secondary";
|
|
8171
|
+
|
|
8172
|
+
|
|
8173
|
+
const __iconNode$4 = [
|
|
8174
|
+
["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" }],
|
|
8175
|
+
[
|
|
8176
|
+
"path",
|
|
8177
|
+
{
|
|
8178
|
+
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",
|
|
8179
|
+
key: "ohrbg2"
|
|
8114
8180
|
}
|
|
8115
|
-
|
|
8181
|
+
]
|
|
8182
|
+
];
|
|
8183
|
+
const SquarePen = createLucideIcon("square-pen", __iconNode$4);
|
|
8184
|
+
|
|
8116
8185
|
/**
|
|
8117
|
-
*
|
|
8118
|
-
*
|
|
8186
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8187
|
+
*
|
|
8188
|
+
* This source code is licensed under the ISC license.
|
|
8189
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8119
8190
|
*/
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8191
|
+
|
|
8192
|
+
|
|
8193
|
+
const __iconNode$3 = [
|
|
8194
|
+
["path", { d: "M10 11v6", key: "nco0om" }],
|
|
8195
|
+
["path", { d: "M14 11v6", key: "outv1u" }],
|
|
8196
|
+
["path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6", key: "miytrc" }],
|
|
8197
|
+
["path", { d: "M3 6h18", key: "d0wm0j" }],
|
|
8198
|
+
["path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2", key: "e791ji" }]
|
|
8199
|
+
];
|
|
8200
|
+
const Trash2 = createLucideIcon("trash-2", __iconNode$3);
|
|
8130
8201
|
|
|
8131
8202
|
/**
|
|
8132
|
-
*
|
|
8133
|
-
*
|
|
8203
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8204
|
+
*
|
|
8205
|
+
* This source code is licensed under the ISC license.
|
|
8206
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8134
8207
|
*/
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
"platform",
|
|
8144
|
-
"organization",
|
|
8145
|
-
"project",
|
|
8146
|
-
"user",
|
|
8147
|
-
];
|
|
8148
|
-
if (validTypes.includes(tenantType)) {
|
|
8149
|
-
return {
|
|
8150
|
-
type: tenantType,
|
|
8151
|
-
name: tenantName,
|
|
8152
|
-
};
|
|
8153
|
-
}
|
|
8208
|
+
|
|
8209
|
+
|
|
8210
|
+
const __iconNode$2 = [
|
|
8211
|
+
[
|
|
8212
|
+
"path",
|
|
8213
|
+
{
|
|
8214
|
+
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",
|
|
8215
|
+
key: "wmoenq"
|
|
8154
8216
|
}
|
|
8155
|
-
|
|
8156
|
-
}
|
|
8217
|
+
],
|
|
8218
|
+
["path", { d: "M12 9v4", key: "juzpu7" }],
|
|
8219
|
+
["path", { d: "M12 17h.01", key: "p32p05" }]
|
|
8220
|
+
];
|
|
8221
|
+
const TriangleAlert = createLucideIcon("triangle-alert", __iconNode$2);
|
|
8222
|
+
|
|
8157
8223
|
/**
|
|
8158
|
-
*
|
|
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$1 = [
|
|
8232
|
+
["path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2", key: "975kel" }],
|
|
8233
|
+
["circle", { cx: "12", cy: "7", r: "4", key: "17ys0d" }]
|
|
8234
|
+
];
|
|
8235
|
+
const User = createLucideIcon("user", __iconNode$1);
|
|
8236
|
+
|
|
8237
|
+
/**
|
|
8238
|
+
* @license lucide-react v0.577.0 - ISC
|
|
8239
|
+
*
|
|
8240
|
+
* This source code is licensed under the ISC license.
|
|
8241
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8159
8242
|
*/
|
|
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
|
-
}
|
|
8243
|
+
|
|
8244
|
+
|
|
8245
|
+
const __iconNode = [
|
|
8246
|
+
["path", { d: "M18 6 6 18", key: "1bl5f8" }],
|
|
8247
|
+
["path", { d: "m6 6 12 12", key: "d8bk6v" }]
|
|
8248
|
+
];
|
|
8249
|
+
const X = createLucideIcon("x", __iconNode);
|
|
8222
8250
|
|
|
8223
8251
|
/**
|
|
8224
8252
|
* FilterChip - A compact pill/chip showing "Label: Values" with clear button
|
|
@@ -8343,7 +8371,7 @@ function FilterChip({ label, values, options = [], onValuesChange, onClear, inpu
|
|
|
8343
8371
|
}, [options]);
|
|
8344
8372
|
// Find selected options for chips display
|
|
8345
8373
|
const selectedOptions = options.filter((opt) => values.includes(opt.value));
|
|
8346
|
-
return (jsxs(
|
|
8374
|
+
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
8375
|
}
|
|
8348
8376
|
|
|
8349
8377
|
/**
|
|
@@ -8360,12 +8388,12 @@ function AddFilterDropdown({ availableFilters, activeFilterIds, onAddFilter, has
|
|
|
8360
8388
|
const isFilterActive = (filterId) => activeFilterIds.includes(filterId);
|
|
8361
8389
|
// Get button label
|
|
8362
8390
|
const buttonLabel = hasActiveFilters ? 'Filters' : 'Add Filters';
|
|
8363
|
-
return (jsxs(Popover
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8391
|
+
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) => {
|
|
8392
|
+
const active = isFilterActive(filter.id);
|
|
8393
|
+
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
|
|
8394
|
+
? 'cursor-not-allowed opacity-40'
|
|
8395
|
+
: '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));
|
|
8396
|
+
}) }) })] }));
|
|
8369
8397
|
}
|
|
8370
8398
|
|
|
8371
8399
|
/**
|
|
@@ -8389,10 +8417,10 @@ function ActionMultiSelect({ value, onChange, className = '', disabled = false,
|
|
|
8389
8417
|
return '1 action';
|
|
8390
8418
|
return `${value.length} actions`;
|
|
8391
8419
|
}, [value.length]);
|
|
8392
|
-
return (jsxs(Popover
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8420
|
+
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) => {
|
|
8421
|
+
const checked = value.includes(option.value);
|
|
8422
|
+
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));
|
|
8423
|
+
})) }) })] }));
|
|
8396
8424
|
}
|
|
8397
8425
|
|
|
8398
8426
|
/**
|
|
@@ -8424,7 +8452,7 @@ function UserSelect({ value, options, onChange, className = '', disabled = false
|
|
|
8424
8452
|
e.stopPropagation();
|
|
8425
8453
|
onChange(undefined);
|
|
8426
8454
|
}, [onChange]);
|
|
8427
|
-
return (jsxs(Popover
|
|
8455
|
+
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
8456
|
}
|
|
8429
8457
|
|
|
8430
8458
|
/**
|
|
@@ -8771,7 +8799,7 @@ function AuditLogFilters({ client, filters, timeRange, onFiltersChange, onTimeRa
|
|
|
8771
8799
|
label: facet.value,
|
|
8772
8800
|
count: facet.count,
|
|
8773
8801
|
}));
|
|
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: "
|
|
8802
|
+
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
8803
|
const config = FILTER_CONFIGS$2[filterId];
|
|
8776
8804
|
return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
|
|
8777
8805
|
? getFilterOptions(filterId)
|
|
@@ -8907,7 +8935,7 @@ function CopyButton({ value, label }) {
|
|
|
8907
8935
|
console.error('Failed to copy:', err);
|
|
8908
8936
|
}
|
|
8909
8937
|
};
|
|
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-
|
|
8938
|
+
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
8939
|
}
|
|
8912
8940
|
/**
|
|
8913
8941
|
* A label / value row inside a Section. The value is truncated to one
|
|
@@ -8985,8 +9013,8 @@ function AuditLogExpandedDetails({ event, compact = true }) {
|
|
|
8985
9013
|
const status = event.responseStatus;
|
|
8986
9014
|
const isOk = status?.code != null && status.code >= 200 && status.code < 300;
|
|
8987
9015
|
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-
|
|
9016
|
+
? 'text-[var(--success-500)]'
|
|
9017
|
+
: '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
9018
|
(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
9019
|
return (jsx(TooltipProvider, { children: compact ? (jsx(DetailPanelShell, { children: body })) : (jsx("div", { className: "mt-4 pt-4 border-t border-border", children: body })) }));
|
|
8992
9020
|
}
|
|
@@ -8994,22 +9022,21 @@ function AuditLogExpandedDetails({ event, compact = true }) {
|
|
|
8994
9022
|
// Number of columns rendered for the audit log table. Used by the colSpan
|
|
8995
9023
|
// on the expanded-detail row so it spans the full width.
|
|
8996
9024
|
const AUDIT_LOG_COLUMN_COUNT = 5;
|
|
9025
|
+
const VERB_BADGE_CLASSES = 'text-[0.55rem] h-5 px-2 py-1 leading-3';
|
|
8997
9026
|
/**
|
|
8998
|
-
* Get
|
|
9027
|
+
* Get the badge variant for a verb
|
|
8999
9028
|
*/
|
|
9000
|
-
function
|
|
9001
|
-
|
|
9002
|
-
const normalized = verb?.toLowerCase();
|
|
9003
|
-
switch (normalized) {
|
|
9029
|
+
function getVerbBadgeVariant(verb) {
|
|
9030
|
+
switch (verb?.toLowerCase()) {
|
|
9004
9031
|
case 'create':
|
|
9005
|
-
return
|
|
9032
|
+
return 'success';
|
|
9006
9033
|
case 'update':
|
|
9007
9034
|
case 'patch':
|
|
9008
|
-
return
|
|
9035
|
+
return 'warning';
|
|
9009
9036
|
case 'delete':
|
|
9010
|
-
return
|
|
9037
|
+
return 'destructive';
|
|
9011
9038
|
default:
|
|
9012
|
-
return
|
|
9039
|
+
return 'outline';
|
|
9013
9040
|
}
|
|
9014
9041
|
}
|
|
9015
9042
|
/**
|
|
@@ -9020,9 +9047,9 @@ function getResponseStatusIndicator(code) {
|
|
|
9020
9047
|
return { icon: '?', className: 'text-muted-foreground' };
|
|
9021
9048
|
}
|
|
9022
9049
|
if (code >= 200 && code < 300) {
|
|
9023
|
-
return { icon: '✓', className: 'text-
|
|
9050
|
+
return { icon: '✓', className: 'text-[var(--success-500)]' };
|
|
9024
9051
|
}
|
|
9025
|
-
return { icon: '✗', className: 'text-
|
|
9052
|
+
return { icon: '✗', className: 'text-destructive' };
|
|
9026
9053
|
}
|
|
9027
9054
|
/**
|
|
9028
9055
|
* Build human-readable summary
|
|
@@ -9063,14 +9090,14 @@ function AuditLogFeedItem({ event, onEventClick, isSelected = false, className =
|
|
|
9063
9090
|
verb === 'delete' ? Trash2 :
|
|
9064
9091
|
verb === 'update' || verb === 'patch' ? Pencil :
|
|
9065
9092
|
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-
|
|
9093
|
+
const iconBg = verb === 'create' ? 'bg-[var(--success-100)]' :
|
|
9094
|
+
verb === 'delete' ? 'bg-destructive/10' :
|
|
9095
|
+
verb === 'update' || verb === 'patch' ? 'bg-[color-mix(in_oklab,var(--badge-warning)_15%,transparent)]' :
|
|
9096
|
+
'bg-muted';
|
|
9097
|
+
const iconColor = verb === 'create' ? 'text-[var(--success-500)]' :
|
|
9098
|
+
verb === 'delete' ? 'text-destructive' :
|
|
9099
|
+
verb === 'update' || verb === 'patch' ? 'text-[var(--badge-warning)]' :
|
|
9100
|
+
'text-muted-foreground';
|
|
9074
9101
|
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
9102
|
display: 'block',
|
|
9076
9103
|
overflow: 'hidden',
|
|
@@ -9079,10 +9106,10 @@ function AuditLogFeedItem({ event, onEventClick, isSelected = false, className =
|
|
|
9079
9106
|
cursor: 'help',
|
|
9080
9107
|
}, 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
9108
|
}
|
|
9082
|
-
return (jsxs(Fragment, { children: [jsxs(TableRow, { "data-state": isSelected ? 'selected' : undefined, className: cn('cursor-pointer', isNew && 'bg-
|
|
9109
|
+
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
9110
|
toggleExpand(e);
|
|
9084
9111
|
handleClick();
|
|
9085
|
-
}, "aria-expanded": isExpanded, children: [jsx(TableCell, { className: "py-2 align-middle whitespace-nowrap", children: jsx(Badge, {
|
|
9112
|
+
}, "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
9113
|
overflow: 'hidden',
|
|
9087
9114
|
textOverflow: 'ellipsis',
|
|
9088
9115
|
whiteSpace: 'nowrap',
|
|
@@ -9092,7 +9119,7 @@ function AuditLogFeedItem({ event, onEventClick, isSelected = false, className =
|
|
|
9092
9119
|
/**
|
|
9093
9120
|
* React hook for executing audit log queries
|
|
9094
9121
|
*/
|
|
9095
|
-
function useAuditLogQuery({ client,
|
|
9122
|
+
function useAuditLogQuery({ client, }) {
|
|
9096
9123
|
const [query, setQuery] = useState(null);
|
|
9097
9124
|
const [events, setEvents] = useState([]);
|
|
9098
9125
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -10259,7 +10286,7 @@ function ActivityExpandedDetails({ activity, compact = false, }) {
|
|
|
10259
10286
|
const timestamp = metadata?.creationTimestamp;
|
|
10260
10287
|
const actorDisplay = actor.displayName || actor.name;
|
|
10261
10288
|
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-
|
|
10289
|
+
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
10290
|
return (jsx(TooltipProvider, { children: compact ? (jsx(DetailPanelShell, { children: body })) : (jsx("div", { className: "mt-4 pt-4 border-t border-border", children: body })) }));
|
|
10264
10291
|
}
|
|
10265
10292
|
|
|
@@ -10328,23 +10355,23 @@ function getActionIconClasses(verb) {
|
|
|
10328
10355
|
switch (normalizedVerb) {
|
|
10329
10356
|
case "create":
|
|
10330
10357
|
return {
|
|
10331
|
-
container: "bg-
|
|
10332
|
-
icon: "text-
|
|
10358
|
+
container: "bg-[var(--success-100)]",
|
|
10359
|
+
icon: "text-[var(--success-500)]",
|
|
10333
10360
|
};
|
|
10334
10361
|
case "update":
|
|
10335
10362
|
return {
|
|
10336
|
-
container: "bg-
|
|
10337
|
-
icon: "text-
|
|
10363
|
+
container: "bg-[var(--info-100)]",
|
|
10364
|
+
icon: "text-[var(--info-500)]",
|
|
10338
10365
|
};
|
|
10339
10366
|
case "delete":
|
|
10340
10367
|
return {
|
|
10341
|
-
container: "bg-
|
|
10342
|
-
icon: "text-
|
|
10368
|
+
container: "bg-destructive/10",
|
|
10369
|
+
icon: "text-destructive",
|
|
10343
10370
|
};
|
|
10344
10371
|
default:
|
|
10345
10372
|
return {
|
|
10346
|
-
container: "bg-
|
|
10347
|
-
icon: "text-
|
|
10373
|
+
container: "bg-muted",
|
|
10374
|
+
icon: "text-muted-foreground",
|
|
10348
10375
|
};
|
|
10349
10376
|
}
|
|
10350
10377
|
}
|
|
@@ -10402,7 +10429,7 @@ function ActivityFeedItem({ activity, onResourceClick, resourceLinkResolver, ten
|
|
|
10402
10429
|
// surfaced via a hover tooltip on the avatar so the column stays narrow.
|
|
10403
10430
|
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
10431
|
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-
|
|
10432
|
+
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
10433
|
// Row-level click toggles expansion. Inner interactive elements
|
|
10407
10434
|
// (links, avatar, expand button) call e.stopPropagation() so
|
|
10408
10435
|
// they don't double-fire. If a parent passed onActivityClick,
|
|
@@ -10573,6 +10600,18 @@ function useFacets(client, timeRange, filters = {}) {
|
|
|
10573
10600
|
};
|
|
10574
10601
|
}
|
|
10575
10602
|
|
|
10603
|
+
/**
|
|
10604
|
+
* SegmentedToggle - a single-select control built on datum-ui's ButtonGroup.
|
|
10605
|
+
* The active segment is a solid primary Button; inactive segments sit on the
|
|
10606
|
+
* neutral (muted) button surface so the control reads in both themes.
|
|
10607
|
+
*/
|
|
10608
|
+
function SegmentedToggle({ options, value, onChange, ariaLabel, className, disabled = false, }) {
|
|
10609
|
+
return (jsx(ButtonGroup, { "aria-label": ariaLabel, className: className, children: options.map((option) => {
|
|
10610
|
+
const active = value === option.value;
|
|
10611
|
+
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));
|
|
10612
|
+
}) }));
|
|
10613
|
+
}
|
|
10614
|
+
|
|
10576
10615
|
/**
|
|
10577
10616
|
* Options for the change source toggle
|
|
10578
10617
|
*/
|
|
@@ -10596,17 +10635,8 @@ const OPTIONS$1 = [
|
|
|
10596
10635
|
/**
|
|
10597
10636
|
* ChangeSourceToggle provides a segmented control for filtering by change source
|
|
10598
10637
|
*/
|
|
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
|
-
}) }));
|
|
10638
|
+
function ChangeSourceToggle({ value, onChange, className, disabled = false, }) {
|
|
10639
|
+
return (jsx(SegmentedToggle, { options: OPTIONS$1, value: value, onChange: onChange, ariaLabel: "Filter by change source", className: className, disabled: disabled }));
|
|
10610
10640
|
}
|
|
10611
10641
|
|
|
10612
10642
|
/**
|
|
@@ -10678,7 +10708,7 @@ const getSelectedPreset$1 = (timeRange) => {
|
|
|
10678
10708
|
/**
|
|
10679
10709
|
* ActivityFeedFilters provides filter controls for the activity feed
|
|
10680
10710
|
*/
|
|
10681
|
-
function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTimeRangeChange, disabled = false, hiddenFilters = [], className = "", }) {
|
|
10711
|
+
function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTimeRangeChange, disabled = false, hiddenFilters = [], className = "", extraFilters, }) {
|
|
10682
10712
|
const { resourceKinds, actorNames, apiGroups, resourceNamespaces, error: facetsError, } = useFacets(client, timeRange, filters);
|
|
10683
10713
|
// Log facets error for debugging
|
|
10684
10714
|
if (facetsError) {
|
|
@@ -10898,12 +10928,12 @@ function ActivityFeedFilters({ client, filters, timeRange, onFiltersChange, onTi
|
|
|
10898
10928
|
clearTimeout(searchDebounceRef.current);
|
|
10899
10929
|
onFiltersChangeRef.current({ ...filtersRef.current, search: undefined });
|
|
10900
10930
|
}, []);
|
|
10901
|
-
return (jsx("div", { className: `border-b border-border py-4 ${className}`, children: jsxs("div", { className: "flex flex-wrap gap-2 items-center", children: [!hiddenFilters.includes("changeSource") && (jsx(ChangeSourceToggle, { value: filters.changeSource || "all", onChange: handleChangeSourceChange, disabled: disabled })), jsxs("div", { className: "relative min-w-[200px] flex-1 max-w-xs", children: [jsx(Search, { className: "absolute left-
|
|
10931
|
+
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
10932
|
const config = FILTER_CONFIGS$1[filterId];
|
|
10903
10933
|
return (jsx(FilterChip, { label: config.label, values: getFilterValues(filterId), options: config.inputMode === "typeahead"
|
|
10904
10934
|
? getFilterOptions(filterId)
|
|
10905
10935
|
: 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 }), jsx("div", { className: "flex-1 min-w-[20px]" }), jsx(TimeRangeDropdown, { presets: TIME_PRESETS$1, selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() })] }) }));
|
|
10936
|
+
}), jsx(AddFilterDropdown, { availableFilters: availableFilters, activeFilterIds: activeFilterIds, onAddFilter: handleAddFilter, hasActiveFilters: activeFilterIds.length > 0, disabled: disabled }), extraFilters, jsx("div", { className: "flex-1 min-w-[20px]" }), jsx(TimeRangeDropdown, { presets: TIME_PRESETS$1, selectedPreset: selectedPreset, onPresetSelect: handleTimePresetSelect, onCustomRangeApply: handleCustomRangeApply, customStart: customStart, customEnd: customEnd, disabled: disabled, displayLabel: getTimeRangeLabel() })] }) }));
|
|
10907
10937
|
}
|
|
10908
10938
|
|
|
10909
10939
|
/**
|
|
@@ -10973,26 +11003,28 @@ function ActivityFeed({ client, initialFilters = { changeSource: "human" }, init
|
|
|
10973
11003
|
useEffect(() => {
|
|
10974
11004
|
isLoadingRef.current = isLoading;
|
|
10975
11005
|
}, [isLoading]);
|
|
10976
|
-
// Infinite scroll using Intersection Observer
|
|
11006
|
+
// Infinite scroll using Intersection Observer.
|
|
11007
|
+
// The list container is the root only when it actually scrolls. Host apps
|
|
11008
|
+
// such as cloud-portal scroll at the page level, so the container never
|
|
11009
|
+
// overflows; in that case observe against the viewport instead.
|
|
10977
11010
|
useEffect(() => {
|
|
10978
11011
|
if (!infiniteScroll || !hasMore || !loadMoreTriggerRef.current)
|
|
10979
11012
|
return;
|
|
11013
|
+
const container = scrollContainerRef.current;
|
|
11014
|
+
const containerScrolls = !!container && container.scrollHeight > container.clientHeight + 1;
|
|
10980
11015
|
const observer = new IntersectionObserver((entries) => {
|
|
10981
11016
|
const entry = entries[0];
|
|
10982
11017
|
if (entry.isIntersecting && !isLoadingRef.current) {
|
|
10983
|
-
// Call through the ref to always use the latest function
|
|
10984
11018
|
loadMoreRef.current();
|
|
10985
11019
|
}
|
|
10986
11020
|
}, {
|
|
10987
|
-
root:
|
|
11021
|
+
root: containerScrolls ? container : null,
|
|
10988
11022
|
rootMargin: `${loadMoreThreshold}px`,
|
|
10989
11023
|
threshold: 0,
|
|
10990
11024
|
});
|
|
10991
11025
|
observer.observe(loadMoreTriggerRef.current);
|
|
10992
|
-
return () =>
|
|
10993
|
-
|
|
10994
|
-
};
|
|
10995
|
-
}, [infiniteScroll, loadMoreThreshold, hasMore]);
|
|
11026
|
+
return () => observer.disconnect();
|
|
11027
|
+
}, [infiniteScroll, loadMoreThreshold, hasMore, activities.length]);
|
|
10996
11028
|
// Handle filter changes - refresh is automatic via the hook
|
|
10997
11029
|
const handleFiltersChange = useCallback((newFilters) => {
|
|
10998
11030
|
setFilters(newFilters);
|
|
@@ -11032,7 +11064,7 @@ function ActivityFeed({ client, initialFilters = { changeSource: "human" }, init
|
|
|
11032
11064
|
// Parent containers must have proper height constraints (h-screen/h-full + overflow-hidden)
|
|
11033
11065
|
const effectiveMaxHeight = maxHeight === "none" ? undefined : maxHeight;
|
|
11034
11066
|
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-
|
|
11067
|
+
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 })), 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
11068
|
activities.length === 0 &&
|
|
11037
11069
|
hasPolicies !== false &&
|
|
11038
11070
|
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 &&
|
|
@@ -11686,7 +11718,7 @@ function useEventsFeed({ client, initialFilters = {}, initialTimeRange = { start
|
|
|
11686
11718
|
function getRegarding$1(event) {
|
|
11687
11719
|
return event.regarding || event.involvedObject || {};
|
|
11688
11720
|
}
|
|
11689
|
-
function getReportingController
|
|
11721
|
+
function getReportingController(event) {
|
|
11690
11722
|
return event.reportingController || event.reportingComponent || event.source?.component;
|
|
11691
11723
|
}
|
|
11692
11724
|
function getReportingInstance(event) {
|
|
@@ -11699,7 +11731,7 @@ function getReportingInstance(event) {
|
|
|
11699
11731
|
*/
|
|
11700
11732
|
function EventExpandedDetails({ event, compact = true }) {
|
|
11701
11733
|
const regarding = getRegarding$1(event);
|
|
11702
|
-
const reportingController = getReportingController
|
|
11734
|
+
const reportingController = getReportingController(event);
|
|
11703
11735
|
const reportingInstance = getReportingInstance(event);
|
|
11704
11736
|
const { eventTime, action, metadata, related } = event;
|
|
11705
11737
|
const firstTimestamp = event.firstTimestamp || event.deprecatedFirstTimestamp;
|
|
@@ -11726,12 +11758,6 @@ function getRegarding(event) {
|
|
|
11726
11758
|
function getNote(event) {
|
|
11727
11759
|
return event.note || event.message;
|
|
11728
11760
|
}
|
|
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
11761
|
/**
|
|
11736
11762
|
* Get the event count (handling both new and deprecated field names)
|
|
11737
11763
|
*/
|
|
@@ -11767,7 +11793,6 @@ function EventFeedItem({ event, onEventClick, onResourceClick, isSelected = fals
|
|
|
11767
11793
|
const note = getNote(event);
|
|
11768
11794
|
const count = getCount(event);
|
|
11769
11795
|
const timestamp = getTimestamp(event);
|
|
11770
|
-
getReportingController(event);
|
|
11771
11796
|
const { type, reason } = event;
|
|
11772
11797
|
const handleClick = () => {
|
|
11773
11798
|
onEventClick?.(event);
|
|
@@ -11810,11 +11835,11 @@ function EventFeedItem({ event, onEventClick, onResourceClick, isSelected = fals
|
|
|
11810
11835
|
if (variant === 'timeline') {
|
|
11811
11836
|
const TypeIcon = isWarning ? TriangleAlert : Bell;
|
|
11812
11837
|
const iconBg = isWarning
|
|
11813
|
-
? 'bg-
|
|
11814
|
-
: 'bg-
|
|
11838
|
+
? 'bg-destructive/10'
|
|
11839
|
+
: 'bg-[var(--info-100)]';
|
|
11815
11840
|
const iconColor = isWarning
|
|
11816
|
-
? 'text-
|
|
11817
|
-
: 'text-
|
|
11841
|
+
? 'text-destructive'
|
|
11842
|
+
: 'text-[var(--info-500)]';
|
|
11818
11843
|
const objectLabel = regarding.namespace
|
|
11819
11844
|
? `${regarding.kind || 'Unknown'} · ${regarding.namespace}/${regarding.name || ''}`
|
|
11820
11845
|
: `${regarding.kind || 'Unknown'} · ${regarding.name || ''}`;
|
|
@@ -11827,18 +11852,16 @@ function EventFeedItem({ event, onEventClick, onResourceClick, isSelected = fals
|
|
|
11827
11852
|
cursor: 'help',
|
|
11828
11853
|
}, 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
11854
|
}
|
|
11830
|
-
return (jsxs(Fragment, { children: [jsxs(TableRow, { "data-state": isSelected ? 'selected' : undefined, className: cn('cursor-pointer', isNew && 'bg-
|
|
11855
|
+
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
11856
|
toggleExpand(e);
|
|
11832
11857
|
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: {
|
|
11858
|
+
}, "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
11859
|
overflow: 'hidden',
|
|
11837
11860
|
textOverflow: 'ellipsis',
|
|
11838
11861
|
whiteSpace: 'nowrap',
|
|
11839
11862
|
}, 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
11863
|
? `${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-
|
|
11864
|
+
: `${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
11865
|
}
|
|
11843
11866
|
|
|
11844
11867
|
/**
|
|
@@ -11951,11 +11974,8 @@ const OPTIONS = [
|
|
|
11951
11974
|
/**
|
|
11952
11975
|
* EventTypeToggle provides a segmented control for filtering by event type
|
|
11953
11976
|
*/
|
|
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
|
-
}) }));
|
|
11977
|
+
function EventTypeToggle({ value, onChange, className, disabled = false, }) {
|
|
11978
|
+
return (jsx(SegmentedToggle, { options: OPTIONS, value: value, onChange: onChange, ariaLabel: "Filter by event type", className: className, disabled: disabled }));
|
|
11959
11979
|
}
|
|
11960
11980
|
|
|
11961
11981
|
/**
|
|
@@ -12199,7 +12219,7 @@ function EventsFeedFilters({ client, filters, timeRange, onFiltersChange, onTime
|
|
|
12199
12219
|
search: value || undefined,
|
|
12200
12220
|
});
|
|
12201
12221
|
}, [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-
|
|
12222
|
+
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
12223
|
const config = FILTER_CONFIGS[filterId];
|
|
12204
12224
|
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
12225
|
}), 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 +12287,7 @@ function EventsFeed({ client, initialFilters = {}, initialTimeRange = { start: "
|
|
|
12267
12287
|
: `flex-1 min-h-0 flex flex-col p-3 gap-0 ${className}`;
|
|
12268
12288
|
// Build list classes - use flex-1 min-h-0 for flex-based scrolling
|
|
12269
12289
|
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-
|
|
12290
|
+
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
12291
|
? 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
12292
|
: 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
12293
|
? 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 +12467,14 @@ function PolicyList({ client, onViewPolicy, onEditPolicy, onCreatePolicy, groupB
|
|
|
12447
12467
|
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
12468
|
const rules = countRules(policy);
|
|
12449
12469
|
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-
|
|
12470
|
+
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"
|
|
12471
|
+
? "text-destructive"
|
|
12452
12472
|
: policyStatus.status === "pending"
|
|
12453
|
-
? "text-
|
|
12454
|
-
: "text-
|
|
12455
|
-
? "bg-
|
|
12473
|
+
? "text-[var(--badge-warning)]"
|
|
12474
|
+
: "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
|
|
12475
|
+
? "bg-muted text-muted-foreground"
|
|
12456
12476
|
: "", 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-
|
|
12477
|
+
? "bg-muted text-muted-foreground"
|
|
12458
12478
|
: "", children: rules.event }) })] }, policy.metadata?.name));
|
|
12459
12479
|
}) })] }) }))] }, group.apiGroup))) }))] })] }));
|
|
12460
12480
|
}
|
|
@@ -12902,7 +12922,7 @@ function PolicyRuleListItem({ rule, ruleType, index, onEdit, onDelete, onMoveUp,
|
|
|
12902
12922
|
flex items-center gap-2 p-2 rounded-md border transition-all duration-200
|
|
12903
12923
|
border-border bg-background hover:bg-muted/50
|
|
12904
12924
|
${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-
|
|
12925
|
+
`, 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
12926
|
}
|
|
12907
12927
|
|
|
12908
12928
|
/**
|
|
@@ -13023,7 +13043,7 @@ const registeredLanguages = new Set();
|
|
|
13023
13043
|
* Lazy-loaded Monaco editor for CEL expressions and templates
|
|
13024
13044
|
* Provides autocomplete based on available field paths
|
|
13025
13045
|
*/
|
|
13026
|
-
function CelEditor({ value, onChange, language = 'cel', availableFields = [],
|
|
13046
|
+
function CelEditor({ value, onChange, language = 'cel', availableFields = [], height = '100px', className, error = false, readOnly = false, 'data-testid': testId, }) {
|
|
13027
13047
|
const [Editor, setEditor] = useState(null);
|
|
13028
13048
|
const [loadError, setLoadError] = useState(null);
|
|
13029
13049
|
const [isLoading, setIsLoading] = useState(true);
|
|
@@ -13182,7 +13202,7 @@ function CelEditor({ value, onChange, language = 'cel', availableFields = [], pl
|
|
|
13182
13202
|
*/
|
|
13183
13203
|
function requiresBracketNotation(key) {
|
|
13184
13204
|
// Keys with dots, slashes, or other special characters need bracket notation
|
|
13185
|
-
return /[
|
|
13205
|
+
return /[./\-:]/.test(key) || /^\d/.test(key);
|
|
13186
13206
|
}
|
|
13187
13207
|
/**
|
|
13188
13208
|
* Build the CEL path for a key, using bracket notation when needed
|
|
@@ -13389,7 +13409,7 @@ function PolicyRuleEditorDialog({ open, onOpenChange, rule, ruleType, existingNa
|
|
|
13389
13409
|
const [previewError, setPreviewError] = useState(null);
|
|
13390
13410
|
// Sample data for autocomplete (fetched once when dialog opens)
|
|
13391
13411
|
const [sampleData, setSampleData] = useState([]);
|
|
13392
|
-
const [
|
|
13412
|
+
const [, setSampleDataLoading] = useState(false);
|
|
13393
13413
|
// Extract field paths from sample data OR preview data for autocomplete
|
|
13394
13414
|
const availableFields = useMemo(() => {
|
|
13395
13415
|
// First try to use preview data if available
|
|
@@ -13838,10 +13858,10 @@ function PolicyEditor({ client, policyName, onSaveSuccess, onCancel, onResourceC
|
|
|
13838
13858
|
};
|
|
13839
13859
|
const policyStatus = getPolicyStatus();
|
|
13840
13860
|
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-
|
|
13861
|
+
? 'bg-[var(--success-500)]'
|
|
13842
13862
|
: policyStatus.status === 'error'
|
|
13843
|
-
? 'bg-
|
|
13844
|
-
: 'bg-
|
|
13863
|
+
? 'bg-destructive'
|
|
13864
|
+
: '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
13865
|
}
|
|
13846
13866
|
|
|
13847
13867
|
/**
|
|
@@ -13925,12 +13945,12 @@ function PolicyDetailView({ client, policyName, onEdit, onResourceClick, classNa
|
|
|
13925
13945
|
const policyStatus = getPolicyStatus();
|
|
13926
13946
|
const isUnhealthy = policyStatus && policyStatus.status !== 'ready';
|
|
13927
13947
|
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-
|
|
13948
|
+
? 'bg-[var(--success-500)]'
|
|
13929
13949
|
: policyStatus?.status === 'error'
|
|
13930
|
-
? 'bg-
|
|
13950
|
+
? 'bg-destructive'
|
|
13931
13951
|
: policyStatus?.status === 'pending'
|
|
13932
|
-
? 'bg-
|
|
13933
|
-
: 'bg-
|
|
13952
|
+
? 'bg-[var(--badge-warning)]'
|
|
13953
|
+
: '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
13954
|
}
|
|
13935
13955
|
|
|
13936
13956
|
/**
|
|
@@ -14030,7 +14050,7 @@ function PolicyEditView({ client, policyName, onSaveSuccess, onCancel, className
|
|
|
14030
14050
|
};
|
|
14031
14051
|
const policyStatus = getPolicyStatus();
|
|
14032
14052
|
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-
|
|
14053
|
+
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
14054
|
}
|
|
14035
14055
|
|
|
14036
14056
|
/**
|
|
@@ -14111,8 +14131,8 @@ function PolicyRuleEditor({ rule, index, ruleType, isHighlighted = false, onChan
|
|
|
14111
14131
|
onChange({ ...rule, summary: example });
|
|
14112
14132
|
};
|
|
14113
14133
|
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-
|
|
14134
|
+
? 'border-[var(--success-500)] bg-[var(--success-100)]'
|
|
14135
|
+
: '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
14136
|
}
|
|
14117
14137
|
|
|
14118
14138
|
/**
|
|
@@ -14770,7 +14790,7 @@ function ReindexJobList({ client, onViewJob, onCreateJob, watch = true, classNam
|
|
|
14770
14790
|
const bTime = b.metadata?.creationTimestamp || '';
|
|
14771
14791
|
return bTime.localeCompare(aTime);
|
|
14772
14792
|
});
|
|
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-
|
|
14793
|
+
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
14794
|
const duration = getReindexJobDuration(job);
|
|
14775
14795
|
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
14796
|
}) })] }) }))] })] }));
|
|
@@ -14782,11 +14802,11 @@ function ReindexJobList({ client, onViewJob, onCreateJob, watch = true, classNam
|
|
|
14782
14802
|
function getPhaseIcon(phase) {
|
|
14783
14803
|
switch (phase) {
|
|
14784
14804
|
case 'Succeeded':
|
|
14785
|
-
return jsx(CircleCheck, { className: "h-8 w-8 text-
|
|
14805
|
+
return jsx(CircleCheck, { className: "h-8 w-8 text-[var(--success-500)]" });
|
|
14786
14806
|
case 'Failed':
|
|
14787
|
-
return jsx(CircleX, { className: "h-8 w-8 text-
|
|
14807
|
+
return jsx(CircleX, { className: "h-8 w-8 text-destructive" });
|
|
14788
14808
|
case 'Running':
|
|
14789
|
-
return jsx(LoaderCircle, { className: "h-8 w-8 text-
|
|
14809
|
+
return jsx(LoaderCircle, { className: "h-8 w-8 text-[var(--info-500)] animate-spin" });
|
|
14790
14810
|
case 'Pending':
|
|
14791
14811
|
default:
|
|
14792
14812
|
return jsx(Clock, { className: "h-8 w-8 text-muted-foreground" });
|
|
@@ -14867,7 +14887,7 @@ function ReindexJobDetailView({ client, jobName, onDelete, watch = true, classNa
|
|
|
14867
14887
|
const duration = job ? getReindexJobDuration(job) : null;
|
|
14868
14888
|
const statusMessage = job ? getReindexJobStatusMessage(job) : '';
|
|
14869
14889
|
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-
|
|
14890
|
+
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
14891
|
}
|
|
14872
14892
|
|
|
14873
14893
|
const TIME_RANGE_OPTIONS$1 = [
|
|
@@ -14970,76 +14990,11 @@ function ReindexJobDialog({ client, policyName, open, onOpenChange, onSuccess, e
|
|
|
14970
14990
|
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
14991
|
}
|
|
14972
14992
|
|
|
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))) })] })] }) }) })] }));
|
|
14993
|
+
function MultiCombobox({ options, values, onValuesChange, placeholder = 'Select…',
|
|
14994
|
+
// searchPlaceholder is accepted for API compatibility but MultiSelect has no counterpart.
|
|
14995
|
+
emptyMessage = 'No results', disabled, loading, className, maxDisplayed = 3, }) {
|
|
14996
|
+
const items = React.useMemo(() => options.map((o) => ({ value: o.value, label: o.label })), [options]);
|
|
14997
|
+
return (jsx(MultiSelect, { className: className, options: items, value: values, onValueChange: onValuesChange, placeholder: placeholder, emptyContent: emptyMessage, isLoading: loading, disabled: disabled, maxCount: maxDisplayed }));
|
|
15043
14998
|
}
|
|
15044
14999
|
|
|
15045
15000
|
/**
|
|
@@ -15749,7 +15704,7 @@ class ActivityApiClient {
|
|
|
15749
15704
|
const reader = response.body.getReader();
|
|
15750
15705
|
const decoder = new TextDecoder();
|
|
15751
15706
|
let buffer = '';
|
|
15752
|
-
|
|
15707
|
+
for (;;) {
|
|
15753
15708
|
const { done, value } = await reader.read();
|
|
15754
15709
|
if (done) {
|
|
15755
15710
|
options?.onClose?.();
|
|
@@ -16149,7 +16104,7 @@ class ActivityApiClient {
|
|
|
16149
16104
|
const reader = response.body.getReader();
|
|
16150
16105
|
const decoder = new TextDecoder();
|
|
16151
16106
|
let buffer = '';
|
|
16152
|
-
|
|
16107
|
+
for (;;) {
|
|
16153
16108
|
const { done, value } = await reader.read();
|
|
16154
16109
|
if (done) {
|
|
16155
16110
|
options?.onClose?.();
|
|
@@ -16265,7 +16220,7 @@ class ActivityApiClient {
|
|
|
16265
16220
|
const reader = response.body.getReader();
|
|
16266
16221
|
const decoder = new TextDecoder();
|
|
16267
16222
|
let buffer = '';
|
|
16268
|
-
|
|
16223
|
+
for (;;) {
|
|
16269
16224
|
const { done, value } = await reader.read();
|
|
16270
16225
|
if (done) {
|
|
16271
16226
|
options?.onClose?.();
|
|
@@ -16501,5 +16456,5 @@ const POLICY_FILTER_FIELDS = [
|
|
|
16501
16456
|
},
|
|
16502
16457
|
];
|
|
16503
16458
|
|
|
16504
|
-
export { ACTIVITY_FILTER_FIELDS, AUDIT_TEMPLATES, ActionMultiSelect, ActionToggle, ActivityApiClient, ActivityExpandedDetails, ActivityFeed, 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 };
|
|
16459
|
+
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 };
|
|
16505
16460
|
//# sourceMappingURL=index.esm.js.map
|