@burdenoff/microfe-bigconsole 2026.805.3 → 2026.812.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/components/DrilldownDashboardRenderer.js +118 -106
- package/dist/bigconsole/components/DrilldownDashboardRenderer.js.map +1 -1
- package/dist/bigconsole/components/DrilldownModal.js +54 -50
- package/dist/bigconsole/components/DrilldownModal.js.map +1 -1
- package/dist/bigconsole/components/builder/BuilderCanvas.js +2 -2
- package/dist/bigconsole/components/builder/BuilderCanvas.js.map +1 -1
- package/dist/bigconsole/components/builder/BuilderTopbar.js +3 -3
- package/dist/bigconsole/components/builder/BuilderTopbar.js.map +1 -1
- package/dist/bigconsole/components/builder/inspector/AdvancedSection.js +24 -25
- package/dist/bigconsole/components/builder/inspector/AdvancedSection.js.map +1 -1
- package/dist/bigconsole/components/builder/inspector/AppearanceSection.js +40 -29
- package/dist/bigconsole/components/builder/inspector/AppearanceSection.js.map +1 -1
- package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +135 -97
- package/dist/bigconsole/components/builder/inspector/DataBindingSection.js.map +1 -1
- package/dist/bigconsole/components/builder/inspector/GeneralSection.js +58 -44
- package/dist/bigconsole/components/builder/inspector/GeneralSection.js.map +1 -1
- package/dist/bigconsole/components/common/Section.js +70 -0
- package/dist/bigconsole/components/common/Section.js.map +1 -0
- package/dist/bigconsole/components/common/index.js +2 -0
- package/dist/bigconsole/components/common/sectionStyles.js +42 -0
- package/dist/bigconsole/components/common/sectionStyles.js.map +1 -0
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +209 -193
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js +213 -231
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardGrid.js +12 -18
- package/dist/bigconsole/components/dashboard/DashboardGrid.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js +109 -102
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +81 -70
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js +125 -159
- package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js +25 -23
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +3 -3
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js +5 -3
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +170 -284
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +147 -151
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/LiveBoardBanner.js +45 -53
- package/dist/bigconsole/components/dashboard/LiveBoardBanner.js.map +1 -1
- package/dist/bigconsole/components/dashboard/PageTabsManager/PageTabsManager.js +157 -148
- package/dist/bigconsole/components/dashboard/PageTabsManager/PageTabsManager.js.map +1 -1
- package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +17 -8
- package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.js +101 -95
- package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/StoreTemplatePicker.js +20 -18
- package/dist/bigconsole/components/dashboard/StoreTemplatePicker.js.map +1 -1
- package/dist/bigconsole/components/dashboard/TemplateSetupBanner.js +26 -58
- package/dist/bigconsole/components/dashboard/TemplateSetupBanner.js.map +1 -1
- package/dist/bigconsole/components/dashboard/WidgetPalette.js +307 -231
- package/dist/bigconsole/components/dashboard/WidgetPalette.js.map +1 -1
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js +29 -29
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkCard.js +78 -79
- package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkDataViewer.js +33 -34
- package/dist/bigconsole/components/datasink/DataSinkDataViewer.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +79 -56
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +75 -65
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js +1 -1
- package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkTableViewer.js +20 -19
- package/dist/bigconsole/components/datasink/DataSinkTableViewer.js.map +1 -1
- package/dist/bigconsole/components/datasink/ExportDataSinkDialog.js +127 -171
- package/dist/bigconsole/components/datasink/ExportDataSinkDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +263 -243
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/LinkedParsersPanel.js +65 -66
- package/dist/bigconsole/components/datasink/LinkedParsersPanel.js.map +1 -1
- package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js +121 -127
- package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js.map +1 -1
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +33 -26
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js.map +1 -1
- package/dist/bigconsole/components/datasink/SetupStateBadge.js +3 -3
- package/dist/bigconsole/components/datasink/SetupStateBadge.js.map +1 -1
- package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js +95 -95
- package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js.map +1 -1
- package/dist/bigconsole/components/parser/DataSinkSelector.js +154 -123
- package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ExpressionEditor.js +3 -3
- package/dist/bigconsole/components/parser/ExpressionEditor.js.map +1 -1
- package/dist/bigconsole/components/parser/ImportParserDialog.js +235 -275
- package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserCard.js +133 -114
- package/dist/bigconsole/components/parser/ParserCard.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserSelector.js +191 -174
- package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserStatusBadge.js +2 -2
- package/dist/bigconsole/components/parser/ParserStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserTypeBadge.js +4 -4
- package/dist/bigconsole/components/parser/ParserTypeBadge.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineCard.js +134 -89
- package/dist/bigconsole/components/pipeline/PipelineCard.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js +77 -64
- package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js +7 -10
- package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewContent.js +31 -32
- package/dist/bigconsole/components/preview/AiPreviewContent.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +152 -106
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewPanel.js +193 -152
- package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
- package/dist/bigconsole/components/preview/previewShared.js +41 -23
- package/dist/bigconsole/components/preview/previewShared.js.map +1 -1
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js +342 -308
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js.map +1 -1
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +262 -238
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +783 -867
- package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetErrorBoundary.js +7 -5
- package/dist/bigconsole/components/widgets/WidgetErrorBoundary.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetHeader.js +7 -8
- package/dist/bigconsole/components/widgets/WidgetHeader.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetRegistry.js +4 -4
- package/dist/bigconsole/components/widgets/WidgetRegistry.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetWrapper.js +8 -7
- package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
- package/dist/bigconsole/components/widgets/adaptive-card/AdaptiveCardWidget.js +1 -1
- package/dist/bigconsole/components/widgets/adaptive-card/AdaptiveCardWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +2 -2
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/ChartConfig.js +11 -5
- package/dist/bigconsole/components/widgets/chart/ChartConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js +19 -15
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js.map +1 -1
- package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +154 -102
- package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/CircularGauge.js +36 -32
- package/dist/bigconsole/components/widgets/gauge/CircularGauge.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +85 -60
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js +64 -35
- package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/LinearGauge.js +46 -45
- package/dist/bigconsole/components/widgets/gauge/LinearGauge.js.map +1 -1
- package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js +35 -24
- package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/iframe-widget/IframeWidget.js +2 -2
- package/dist/bigconsole/components/widgets/iframe-widget/IframeWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +4 -4
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js +153 -90
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/list/ListConfig.js +8 -2
- package/dist/bigconsole/components/widgets/list/ListConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/list/ListItem.js +1 -1
- package/dist/bigconsole/components/widgets/list/ListItem.js.map +1 -1
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js +1 -1
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +14 -4
- package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +112 -89
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +21 -13
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/index.js +4 -4
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +3 -3
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +34 -25
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressWidget.js +129 -114
- package/dist/bigconsole/components/widgets/progress/ProgressWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/retention/RetentionWidget.js +3 -3
- package/dist/bigconsole/components/widgets/retention/RetentionWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/states/EmptyState.js +6 -4
- package/dist/bigconsole/components/widgets/states/EmptyState.js.map +1 -1
- package/dist/bigconsole/components/widgets/states/ErrorState.js +7 -5
- package/dist/bigconsole/components/widgets/states/ErrorState.js.map +1 -1
- package/dist/bigconsole/components/widgets/states/LoadingState.js +5 -4
- package/dist/bigconsole/components/widgets/states/LoadingState.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableConfig.js +10 -4
- package/dist/bigconsole/components/widgets/table/TableConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableHeader.js +1 -1
- package/dist/bigconsole/components/widgets/table/TableHeader.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableWidget.js +1 -1
- package/dist/bigconsole/components/widgets/table/TableWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/text-widget/TextWidget.js +1 -1
- package/dist/bigconsole/components/widgets/text-widget/TextWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js +3 -3
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/utils/conditionalFormat.js +2 -2
- package/dist/bigconsole/components/widgets/utils/conditionalFormat.js.map +1 -1
- package/dist/bigconsole/components/widgets/utils/targetProgress.js +13 -0
- package/dist/bigconsole/components/widgets/utils/targetProgress.js.map +1 -0
- package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +112 -85
- package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js +28 -20
- package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowCard.js +92 -77
- package/dist/bigconsole/components/workflow/WorkflowCard.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js +70 -51
- package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowStatusBadge.js +16 -17
- package/dist/bigconsole/components/workflow/WorkflowStatusBadge.js.map +1 -1
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +6 -4
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js.map +1 -1
- package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js +360 -338
- package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js.map +1 -1
- package/dist/bigconsole/hooks/useModalFocusTrap.js +53 -0
- package/dist/bigconsole/hooks/useModalFocusTrap.js.map +1 -0
- package/dist/bigconsole/pages/DashboardBuilderPage.js +209 -203
- package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardViewPage.js +126 -120
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +838 -800
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/DataParsersPage.js +263 -315
- package/dist/bigconsole/pages/DataParsersPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkBuilderPage.js +301 -341
- package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +346 -339
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinksPage.js +356 -458
- package/dist/bigconsole/pages/DataSinksPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSourcesPage.js +7 -2
- package/dist/bigconsole/pages/DataSourcesPage.js.map +1 -1
- package/dist/bigconsole/pages/HomePage.js +186 -193
- package/dist/bigconsole/pages/HomePage.js.map +1 -1
- package/dist/bigconsole/pages/InsightsPage.js +887 -740
- package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserBuilderPage.js +679 -637
- package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserViewPage.js +368 -342
- package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineBuilderPage.js +537 -444
- package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineViewPage.js +412 -377
- package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelinesPage.js +248 -298
- package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
- package/dist/bigconsole/pages/WidgetDemoPage.js +251 -153
- package/dist/bigconsole/pages/WidgetDemoPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowViewPage.js +381 -371
- package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowsPage.js +171 -286
- package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetPalette.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/WidgetPalette.tsx"],"sourcesContent":["/**\n * WidgetPalette Component\n *\n * Sidebar panel with draggable widget types for the dashboard builder.\n * Uses semantic tokens for consistent theming.\n * Features categorized widgets, search, and drag-and-drop support.\n */\n\nimport { type FC, memo, useState, useCallback, useMemo } from 'react';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Search,\n ChevronDown,\n ChevronRight,\n TrendingUp,\n BarChart3,\n Filter,\n Table,\n Grid3x3,\n Gauge,\n Activity,\n List,\n FileText,\n Type,\n ExternalLink,\n MapPin,\n Calendar,\n Columns,\n GitBranch,\n Puzzle,\n LayoutDashboard,\n GripVertical,\n X,\n Layers,\n Store,\n type LucideIcon,\n} from 'lucide-react';\nimport {\n getWidgetCategories,\n getWidgetKey,\n WIDGET_CATEGORY_LABELS,\n type WidgetDefinition,\n} from '../widgets/WidgetRegistry';\nimport type { WidgetType, WidgetCategory } from '../../types';\n\n// ============================================================================\n// Icon Mapping\n// ============================================================================\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n TrendingUp,\n BarChart3,\n Filter,\n Table,\n Grid3x3,\n Gauge,\n Activity,\n List,\n FileText,\n Type,\n ExternalLink,\n MapPin,\n Calendar,\n Columns,\n GitBranch,\n Puzzle,\n LayoutDashboard,\n};\n\nfunction getIconComponent(iconName: string): LucideIcon {\n return ICON_MAP[iconName] || Puzzle;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetPaletteProps {\n /** Whether the palette is open */\n isOpen: boolean;\n /** Callback to close the palette */\n onClose: () => void;\n /** Callback when a widget is selected (for quick add without drag) */\n onWidgetSelect?: (type: WidgetType, definition?: WidgetDefinition) => void;\n /** Callback when drag starts */\n onDragStart?: (type: WidgetType, definition: WidgetDefinition) => void;\n /** Callback when drag ends */\n onDragEnd?: () => void;\n /** Installed widgets from the store (fetched by parent) */\n installedWidgets?: WidgetDefinition[];\n /** Whether installed widgets are still loading */\n installedWidgetsLoading?: boolean;\n /**\n * Built-in widget catalog (from the backend `getWidgetTypes`, fetched by the\n * parent via useWidgetCatalog). When omitted, falls back to the bundled\n * static registry.\n */\n definitions?: WidgetDefinition[];\n}\n\n// ============================================================================\n// Styles using semantic tokens\n// ============================================================================\n\nconst styles = {\n container: {\n width: '300px',\n height: '100%',\n backgroundColor: 'var(--color-bg-surface)',\n borderRight: '1px solid var(--color-border)',\n display: 'flex',\n flexDirection: 'column' as const,\n flexShrink: 0,\n overflow: 'hidden',\n position: 'relative' as const,\n },\n header: {\n padding: 'var(--space-formGap)',\n borderBottom: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-sunken)',\n flexShrink: 0,\n },\n headerTop: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: 'var(--space-controlGap)',\n },\n headerTitle: {\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n },\n headerIcon: {\n width: '32px',\n height: '32px',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-action-primary-bg)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n },\n title: {\n fontSize: 'var(--font-size-body-md)',\n fontWeight: 'var(--font-weight-semibold)' as const,\n color: 'var(--color-text-primary)',\n margin: 0,\n },\n subtitle: {\n fontSize: 'var(--font-size-body-xs)',\n color: 'var(--color-text-muted)',\n margin: '2px 0 0 0',\n },\n closeButton: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '28px',\n height: '28px',\n border: 'none',\n background: 'transparent',\n borderRadius: 'var(--radius-button)',\n cursor: 'pointer',\n color: 'var(--color-text-secondary)',\n transition: 'background-color 0.15s',\n },\n searchContainer: {\n position: 'relative' as const,\n },\n searchIcon: {\n position: 'absolute' as const,\n left: '10px',\n top: '50%',\n transform: 'translateY(-50%)',\n width: '16px',\n height: '16px',\n color: 'var(--color-text-muted)',\n pointerEvents: 'none' as const,\n },\n searchInput: {\n width: '100%',\n paddingLeft: '36px',\n height: 'var(--size-controlHeight-sm)',\n fontSize: 'var(--font-size-body-sm)',\n },\n content: {\n flex: '1 1 0',\n minHeight: 0,\n overflow: 'hidden',\n position: 'relative' as const,\n },\n scrollArea: {\n position: 'absolute' as const,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n overflowY: 'auto' as const,\n overflowX: 'hidden' as const,\n padding: 'var(--space-controlGap)',\n },\n categoryContainer: {\n marginBottom: 'var(--space-controlGap)',\n },\n categoryHeader: {\n width: '100%',\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n padding: 'var(--space-controlGap) var(--space-iconGap)',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n borderRadius: 'var(--radius-button)',\n transition: 'background-color 0.15s',\n },\n categoryChevron: {\n width: '16px',\n height: '16px',\n color: 'var(--color-text-muted)',\n flexShrink: 0,\n },\n categoryLabel: {\n flex: 1,\n textAlign: 'left' as const,\n fontSize: 'var(--font-size-body-xs)',\n fontWeight: 'var(--font-weight-semibold)' as const,\n color: 'var(--color-text-secondary)',\n textTransform: 'uppercase' as const,\n letterSpacing: '0.05em',\n },\n categoryCount: {\n fontSize: 'var(--font-size-body-xs)',\n color: 'var(--color-text-muted)',\n backgroundColor: 'var(--color-bg-sunken)',\n padding: '2px 8px',\n borderRadius: 'var(--radius-full)',\n },\n widgetList: {\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '4px',\n marginTop: 'var(--space-controlGap)',\n paddingLeft: '4px',\n },\n widgetCard: {\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n padding: 'var(--space-controlGap)',\n borderRadius: 'var(--radius-card)',\n cursor: 'grab',\n backgroundColor: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border)',\n transition: 'all 0.15s ease',\n },\n widgetCardHover: {\n backgroundColor: 'var(--color-bg-hover)',\n borderColor: 'var(--color-action-primary-border)',\n boxShadow: 'var(--shadow-elevation-1)',\n },\n dragHandle: {\n width: '16px',\n height: '16px',\n color: 'var(--color-text-muted)',\n opacity: 0.5,\n flexShrink: 0,\n },\n widgetIcon: {\n width: '36px',\n height: '36px',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-bg-sunken)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n },\n widgetIconInner: {\n width: '18px',\n height: '18px',\n color: 'var(--color-action-primary-bg)',\n },\n widgetText: {\n flex: 1,\n minWidth: 0,\n overflow: 'hidden',\n },\n widgetName: {\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)' as const,\n color: 'var(--color-text-primary)',\n whiteSpace: 'nowrap' as const,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n marginBottom: '2px',\n },\n widgetDescription: {\n fontSize: 'var(--font-size-body-xs)',\n color: 'var(--color-text-muted)',\n lineHeight: 1.3,\n overflow: 'hidden',\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical' as const,\n },\n emptyState: {\n textAlign: 'center' as const,\n padding: 'var(--space-pagePadding)',\n },\n emptyIcon: {\n width: '48px',\n height: '48px',\n borderRadius: 'var(--radius-card)',\n backgroundColor: 'var(--color-bg-sunken)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n margin: '0 auto var(--space-formGap)',\n },\n emptyText: {\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n margin: 0,\n },\n clearButton: {\n marginTop: 'var(--space-controlGap)',\n padding: 'var(--space-controlGap) var(--space-formGap)',\n fontSize: 'var(--font-size-body-xs)',\n fontWeight: 'var(--font-weight-medium)' as const,\n color: 'var(--color-action-primary-text)',\n backgroundColor: 'var(--color-action-primary-bg)',\n border: 'none',\n borderRadius: 'var(--radius-button)',\n cursor: 'pointer',\n },\n footer: {\n padding: 'var(--space-controlGap) var(--space-formGap)',\n borderTop: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-sunken)',\n flexShrink: 0,\n },\n footerText: {\n fontSize: 'var(--font-size-body-xs)',\n color: 'var(--color-text-muted)',\n margin: 0,\n textAlign: 'center' as const,\n },\n browseStoreButton: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 'var(--space-iconGap)',\n width: '100%',\n padding: 'var(--space-controlGap)',\n marginBottom: 'var(--space-controlGap)',\n borderRadius: 'var(--radius-button)',\n border: '1px dashed var(--color-border)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-secondary)',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)' as const,\n cursor: 'pointer',\n transition: 'border-color 0.15s, color 0.15s',\n },\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetPalette: FC<WidgetPaletteProps> = memo(function WidgetPalette({\n isOpen,\n onClose,\n onWidgetSelect,\n onDragStart,\n onDragEnd,\n installedWidgets = [],\n installedWidgetsLoading = false,\n definitions,\n}) {\n const { t } = useI18n();\n const [searchQuery, setSearchQuery] = useState('');\n const [expandedCategories, setExpandedCategories] = useState<Set<WidgetCategory>>(\n new Set(['KPI', 'Visualization', 'Data', 'Content', 'Temporal', 'Custom', 'Installed'])\n );\n const [hoveredWidget, setHoveredWidget] = useState<string | null>(null);\n\n // Get categorized widgets, merging in installed widgets. When the parent\n // supplies a backend-sourced catalog (`definitions`), group that by category;\n // otherwise fall back to the bundled static registry.\n const categories = useMemo(() => {\n const baseCategories = definitions\n ? (Object.keys(WIDGET_CATEGORY_LABELS) as WidgetCategory[]).map((category) => ({\n category,\n label: WIDGET_CATEGORY_LABELS[category],\n widgets: definitions.filter((def) => def.category === category),\n }))\n : getWidgetCategories();\n\n return baseCategories\n .map((cat) => {\n if (cat.category === 'Installed') {\n // Two sources feed \"Installed from Store\": the registry-driven backend\n // catalog (getWidgetTypes store types — rich metadata, install-gated,\n // BOFF-2858) which arrives via `definitions` under this category, and\n // the legacy workspaceInstalledApps listing passed as `installedWidgets`.\n // Merge both, deduping by name (the catalog entry wins) so a widget\n // present in both sources shows once with the richer definition, while\n // legacy installs not yet in the registry are still preserved.\n const byName = new Map<string, WidgetDefinition>();\n for (const w of installedWidgets) byName.set(w.name.toLowerCase(), w);\n for (const w of cat.widgets) {\n const key = w.name.toLowerCase();\n const legacy = byName.get(key);\n // Catalog wins for metadata but MERGE defaultConfig so the legacy\n // workspaceInstalledApps fields survive — notably `componentId`, which the\n // current render path needs and the catalog store entry lacks until\n // widgetTypeKey rendering is wired. Keeps overlapping store widgets renderable.\n byName.set(key, legacy ? { ...w, defaultConfig: { ...legacy.defaultConfig, ...w.defaultConfig } } : w);\n }\n return { ...cat, widgets: Array.from(byName.values()) };\n }\n return cat;\n })\n .filter((cat) => cat.category !== 'Installed' || cat.widgets.length > 0);\n }, [installedWidgets, definitions]);\n\n // Filter widgets by search query\n const filteredCategories = useMemo(() => {\n if (!searchQuery.trim()) {\n return categories;\n }\n\n const query = searchQuery.toLowerCase();\n return categories\n .map((cat) => ({\n ...cat,\n widgets: cat.widgets.filter(\n (w) =>\n w.name.toLowerCase().includes(query) ||\n w.description.toLowerCase().includes(query) ||\n w.tags.some((tag) => tag.includes(query))\n ),\n }))\n .filter((cat) => cat.widgets.length > 0);\n }, [categories, searchQuery]);\n\n // Toggle category expansion\n const toggleCategory = useCallback((category: WidgetCategory) => {\n setExpandedCategories((prev) => {\n const next = new Set(prev);\n if (next.has(category)) {\n next.delete(category);\n } else {\n next.add(category);\n }\n return next;\n });\n }, []);\n\n // Handle drag start\n const handleDragStart = useCallback(\n (e: React.DragEvent, widget: WidgetDefinition) => {\n e.dataTransfer.setData(\n 'application/json',\n JSON.stringify({\n type: widget.type,\n name: widget.name,\n defaultSize: widget.defaultSize,\n defaultConfig: widget.defaultConfig,\n category: widget.category,\n })\n );\n e.dataTransfer.effectAllowed = 'copy';\n\n // Create custom drag image\n const dragImage = document.createElement('div');\n dragImage.style.cssText = `\n position: absolute;\n top: -1000px;\n left: -1000px;\n padding: 8px 12px;\n background: var(--color-bg-surface);\n border: 2px solid var(--color-action-primary-bg);\n border-radius: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--color-text-primary);\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n `;\n dragImage.textContent = widget.name;\n document.body.appendChild(dragImage);\n e.dataTransfer.setDragImage(dragImage, 0, 0);\n\n setTimeout(() => {\n document.body.removeChild(dragImage);\n }, 0);\n\n onDragStart?.(widget.type, widget);\n },\n [onDragStart]\n );\n\n // Handle drag end\n const handleDragEnd = useCallback(() => {\n onDragEnd?.();\n }, [onDragEnd]);\n\n // Handle click to quick-add\n const handleWidgetClick = useCallback(\n (widget: WidgetDefinition) => {\n onWidgetSelect?.(widget.type, widget);\n },\n [onWidgetSelect]\n );\n\n // Open the widget store in a new tab so the builder (and any unsaved edits)\n // stays intact. Preserves the current org/workspace/project query params.\n const handleBrowseStore = useCallback(() => {\n if (typeof window === 'undefined') return;\n const url = `${window.location.origin}/bigconsole/store${window.location.search}`;\n window.open(url, '_blank', 'noopener,noreferrer');\n }, []);\n\n if (!isOpen) {\n return null;\n }\n\n return (\n <div style={styles.container}>\n {/* Header */}\n <div style={styles.header}>\n <div style={styles.headerTop}>\n <div style={styles.headerTitle}>\n <div style={styles.headerIcon}>\n <Layers style={{ width: '18px', height: '18px', color: 'var(--color-action-primary-text)' }} />\n </div>\n <div>\n <h3 style={styles.title}>{t('bigconsole.dashboards.widgetPalette.title', 'Widgets')}</h3>\n <p style={styles.subtitle}>{t('bigconsole.dashboards.widgetPalette.subtitle', 'Drag or click to add')}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n style={styles.closeButton}\n onMouseOver={(e) => (e.currentTarget.style.backgroundColor = 'var(--color-bg-hover)')}\n onMouseOut={(e) => (e.currentTarget.style.backgroundColor = 'transparent')}\n >\n <X style={{ width: '16px', height: '16px' }} />\n </button>\n </div>\n\n {/* Search */}\n <div style={styles.searchContainer}>\n <Search style={styles.searchIcon} />\n <Input\n type=\"text\"\n placeholder={t('bigconsole.dashboards.widgetPalette.searchPlaceholder', 'Search widgets...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n style={styles.searchInput}\n />\n </div>\n </div>\n\n {/* Widget Categories */}\n <div style={styles.content}>\n <div style={styles.scrollArea}>\n {filteredCategories.map((category) => (\n <div key={category.category} style={styles.categoryContainer}>\n {/* Category Header */}\n <button\n type=\"button\"\n onClick={() => toggleCategory(category.category)}\n style={styles.categoryHeader}\n onMouseOver={(e) => (e.currentTarget.style.backgroundColor = 'var(--color-bg-hover)')}\n onMouseOut={(e) => (e.currentTarget.style.backgroundColor = 'transparent')}\n >\n {expandedCategories.has(category.category) ? (\n <ChevronDown style={styles.categoryChevron} />\n ) : (\n <ChevronRight style={styles.categoryChevron} />\n )}\n <span style={styles.categoryLabel}>{category.label}</span>\n <span style={styles.categoryCount}>{category.widgets.length}</span>\n </button>\n\n {/* Widget Items */}\n {expandedCategories.has(category.category) && (\n <div style={styles.widgetList}>\n {installedWidgetsLoading && category.category === 'Installed' && (\n <div\n style={{\n fontSize: 'var(--font-size-body-xs)',\n color: 'var(--color-text-muted)',\n padding: 'var(--space-controlGap)',\n }}\n >\n {t('bigconsole.dashboards.widgetPalette.loading', 'Loading...')}\n </div>\n )}\n {category.widgets.map((widget) => {\n const IconComponent = getIconComponent(widget.icon);\n const widgetKey = getWidgetKey(widget);\n const isHovered = hoveredWidget === widgetKey;\n\n return (\n <div\n key={widgetKey}\n draggable\n onDragStart={(e) => handleDragStart(e, widget)}\n onDragEnd={handleDragEnd}\n onClick={() => handleWidgetClick(widget)}\n onMouseEnter={() => setHoveredWidget(widgetKey)}\n onMouseLeave={() => setHoveredWidget(null)}\n style={{\n ...styles.widgetCard,\n ...(isHovered ? styles.widgetCardHover : {}),\n }}\n >\n {/* Drag Handle */}\n <GripVertical style={styles.dragHandle} />\n\n {/* Icon */}\n <div style={styles.widgetIcon}>\n <IconComponent style={styles.widgetIconInner} />\n </div>\n\n {/* Text */}\n <div style={styles.widgetText}>\n <div style={styles.widgetName}>{widget.name}</div>\n <div style={styles.widgetDescription}>{widget.description}</div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </div>\n ))}\n\n {filteredCategories.length === 0 && (\n <div style={styles.emptyState}>\n <div style={styles.emptyIcon}>\n <Search style={{ width: '24px', height: '24px', color: 'var(--color-text-muted)' }} />\n </div>\n <p style={styles.emptyText}>\n {t('bigconsole.dashboards.widgetPalette.noResultsBefore', 'No widgets found for')} \"\n <strong>{searchQuery}</strong>\"\n </p>\n <button type=\"button\" onClick={() => setSearchQuery('')} style={styles.clearButton}>\n {t('bigconsole.dashboards.widgetPalette.clearSearch', 'Clear search')}\n </button>\n </div>\n )}\n </div>\n </div>\n\n {/* Footer */}\n <div style={styles.footer}>\n <button\n type=\"button\"\n onClick={handleBrowseStore}\n style={styles.browseStoreButton}\n onMouseOver={(e) => {\n e.currentTarget.style.borderColor = 'var(--color-action-primary-bg)';\n e.currentTarget.style.color = 'var(--color-action-primary-text)';\n }}\n onMouseOut={(e) => {\n e.currentTarget.style.borderColor = 'var(--color-border)';\n e.currentTarget.style.color = 'var(--color-text-secondary)';\n }}\n >\n <Store style={{ width: '15px', height: '15px' }} />\n {t('bigconsole.dashboards.widgetPalette.browseStore', 'Browse widget store')}\n <ExternalLink style={{ width: '13px', height: '13px', opacity: 0.7 }} />\n </button>\n <p style={styles.footerText}>\n {t('bigconsole.dashboards.widgetPalette.footerHint', 'Drag widgets to position them on the canvas')}\n </p>\n </div>\n </div>\n );\n});\n\nexport default WidgetPalette;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WidgetPalette.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/WidgetPalette.tsx"],"sourcesContent":["/**\n * WidgetPalette Component\n *\n * Sidebar panel with draggable widget types for the dashboard builder.\n * Uses semantic tokens for consistent theming.\n * Features categorized widgets, search, and drag-and-drop support.\n *\n * Section emphasis (BOFF-5636). The palette used to be three bands that all\n * looked alike — sunken header, surface body, sunken footer — with cards drawn\n * on exactly the same fill as the panel behind them, so a card read as a row\n * rather than as a draggable object. It now follows one rule:\n *\n * header (surface, the panel's identity)\n * → scroll body (sunken, a recessed tray)\n * → widget cards (surface + elevation, objects lifted off the tray)\n * → footer (surface, terminal band, lighter type)\n *\n * and the category headings sit above their cards with heading-weight type, so\n * the group is never lighter than the things it groups.\n */\n\nimport { type FC, memo, useState, useCallback, useMemo } from 'react';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { SECTION_FOCUS_RING } from '../common';\nimport {\n Search,\n ChevronDown,\n ChevronRight,\n TrendingUp,\n BarChart3,\n Filter,\n Table,\n Grid3x3,\n Gauge,\n Activity,\n List,\n FileText,\n Type,\n ExternalLink,\n MapPin,\n Calendar,\n Columns,\n GitBranch,\n Puzzle,\n LayoutDashboard,\n GripVertical,\n X,\n Layers,\n Store,\n type LucideIcon,\n} from 'lucide-react';\nimport {\n getWidgetCategories,\n getWidgetKey,\n WIDGET_CATEGORY_LABELS,\n type WidgetDefinition,\n} from '../widgets/WidgetRegistry';\nimport type { WidgetType, WidgetCategory } from '../../types';\n\n// ============================================================================\n// Icon Mapping\n// ============================================================================\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n TrendingUp,\n BarChart3,\n Filter,\n Table,\n Grid3x3,\n Gauge,\n Activity,\n List,\n FileText,\n Type,\n ExternalLink,\n MapPin,\n Calendar,\n Columns,\n GitBranch,\n Puzzle,\n LayoutDashboard,\n};\n\nfunction getIconComponent(iconName: string): LucideIcon {\n return ICON_MAP[iconName] || Puzzle;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetPaletteProps {\n /** Whether the palette is open */\n isOpen: boolean;\n /** Callback to close the palette */\n onClose: () => void;\n /** Callback when a widget is selected (for quick add without drag) */\n onWidgetSelect?: (type: WidgetType, definition?: WidgetDefinition) => void;\n /** Callback when drag starts */\n onDragStart?: (type: WidgetType, definition: WidgetDefinition) => void;\n /** Callback when drag ends */\n onDragEnd?: () => void;\n /** Installed widgets from the store (fetched by parent) */\n installedWidgets?: WidgetDefinition[];\n /** Whether installed widgets are still loading */\n installedWidgetsLoading?: boolean;\n /**\n * Built-in widget catalog (from the backend `getWidgetTypes`, fetched by the\n * parent via useWidgetCatalog). When omitted, falls back to the bundled\n * static registry.\n */\n definitions?: WidgetDefinition[];\n}\n\n// ============================================================================\n// Styles using semantic tokens\n// ============================================================================\n\nconst styles = {\n // Width, borders and max-height live in `className` so they can be\n // breakpoint-aware — a hard 300px pane next to a canvas is unusable on a\n // phone, where the palette now becomes a full-width, height-capped drawer.\n container: {\n backgroundColor: 'var(--color-bg-surface)',\n display: 'flex',\n flexDirection: 'column' as const,\n flexShrink: 0,\n overflow: 'hidden',\n position: 'relative' as const,\n },\n header: {\n padding: 'var(--space-formGap)',\n borderBottom: '1px solid var(--color-border-subtle)',\n backgroundColor: 'var(--color-bg-surface)',\n flexShrink: 0,\n },\n headerTop: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: 'var(--space-controlGap)',\n },\n headerTitle: {\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n },\n headerIcon: {\n width: '32px',\n height: '32px',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-action-primary-bg)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n },\n title: {\n fontSize: 'var(--font-size-body-md)',\n fontWeight: 'var(--font-weight-bold)' as const,\n letterSpacing: '-0.01em',\n color: 'var(--color-text-primary)',\n margin: 0,\n },\n subtitle: {\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n margin: '2px 0 0 0',\n },\n closeButton: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n // 40px keeps the only way to dismiss the palette above the minimum\n // touch-target size on a phone (was 28px).\n width: 'var(--size-controlHeight-md)',\n height: 'var(--size-controlHeight-md)',\n border: 'none',\n background: 'transparent',\n borderRadius: 'var(--radius-button)',\n cursor: 'pointer',\n color: 'var(--color-text-secondary)',\n flexShrink: 0,\n transition: 'background-color 0.15s',\n },\n searchContainer: {\n position: 'relative' as const,\n },\n searchIcon: {\n position: 'absolute' as const,\n left: '10px',\n top: '50%',\n transform: 'translateY(-50%)',\n width: '16px',\n height: '16px',\n color: 'var(--color-text-muted)',\n pointerEvents: 'none' as const,\n },\n searchInput: {\n width: '100%',\n paddingLeft: '36px',\n height: 'var(--size-controlHeight-md)',\n fontSize: 'var(--font-size-body-sm)',\n },\n content: {\n flex: '1 1 0',\n minHeight: 0,\n overflow: 'hidden',\n position: 'relative' as const,\n },\n // Recessed tray: the cards below sit ON this, so they can be lifted off it.\n scrollArea: {\n position: 'absolute' as const,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n overflowY: 'auto' as const,\n overflowX: 'hidden' as const,\n padding: 'var(--space-controlGap)',\n backgroundColor: 'var(--color-bg-sunken)',\n },\n categoryContainer: {\n marginBottom: 'var(--space-formGap)',\n },\n categoryHeading: {\n margin: 0,\n },\n categoryHeader: {\n width: '100%',\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n minHeight: 'var(--size-controlHeight-md)',\n padding: 'var(--space-iconGap) var(--space-controlGap)',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n borderRadius: 'var(--radius-button)',\n transition: 'background-color 0.15s',\n },\n categoryChevron: {\n width: '16px',\n height: '16px',\n color: 'var(--color-text-secondary)',\n flexShrink: 0,\n },\n // A group label must not be lighter than its members: heading weight,\n // primary text colour, uppercase for rhythm.\n categoryLabel: {\n flex: 1,\n textAlign: 'left' as const,\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-bold)' as const,\n color: 'var(--color-text-primary)',\n textTransform: 'uppercase' as const,\n letterSpacing: '0.06em',\n },\n categoryCount: {\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)' as const,\n color: 'var(--color-text-secondary)',\n backgroundColor: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-subtle)',\n padding: '1px 8px',\n // `--radius-full` is not a token in the build; the undefined var made the\n // declaration invalid, so the \"pill\" rendered as a square (BOFF-5636).\n borderRadius: 'var(--radius-pill, 9999px)',\n flexShrink: 0,\n },\n widgetList: {\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '4px',\n marginTop: 'var(--space-controlGap)',\n paddingLeft: '4px',\n },\n widgetCard: {\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-iconGap)',\n minHeight: 'var(--size-controlHeight-lg)',\n padding: 'var(--space-controlGap)',\n borderRadius: 'var(--radius-card)',\n cursor: 'grab',\n backgroundColor: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-subtle)',\n // Elevation is what says \"this is an object you can pick up\". Previously the\n // card shared the panel's fill and had no shadow, so it read as a table row.\n boxShadow: 'var(--shadow-elevation-1)',\n transition: 'box-shadow 0.15s ease, border-color 0.15s ease, background-color 0.15s ease',\n },\n // `--color-bg-hover` is not a token in the build — the undefined var made every\n // hover declaration in this file invalid, so there was no hover feedback at all.\n // `--color-action-ghost-bgHover` is the real per-theme hover fill (BOFF-5636).\n widgetCardHover: {\n backgroundColor: 'var(--color-action-ghost-bgHover)',\n borderColor: 'var(--color-action-primary-border)',\n boxShadow: 'var(--shadow-elevation-2)',\n },\n dragHandle: {\n width: '16px',\n height: '16px',\n color: 'var(--color-text-muted)',\n opacity: 0.5,\n flexShrink: 0,\n },\n widgetIcon: {\n width: '36px',\n height: '36px',\n borderRadius: 'var(--radius-button)',\n backgroundColor: 'var(--color-bg-sunken)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n },\n widgetIconInner: {\n width: '18px',\n height: '18px',\n color: 'var(--color-action-primary-bg)',\n },\n widgetText: {\n flex: 1,\n minWidth: 0,\n overflow: 'hidden',\n },\n widgetName: {\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)' as const,\n color: 'var(--color-text-primary)',\n whiteSpace: 'nowrap' as const,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n marginBottom: '2px',\n },\n widgetDescription: {\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n lineHeight: 1.3,\n overflow: 'hidden',\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical' as const,\n },\n emptyState: {\n textAlign: 'center' as const,\n padding: 'var(--space-pagePadding) var(--space-formGap)',\n backgroundColor: 'var(--color-status-info-bgSubtle)',\n border: '1px solid var(--color-status-info-border)',\n borderRadius: 'var(--radius-card)',\n },\n emptyIcon: {\n width: '48px',\n height: '48px',\n borderRadius: 'var(--radius-card)',\n backgroundColor: 'var(--color-bg-surface)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n margin: '0 auto var(--space-formGap)',\n },\n emptyTitle: {\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)' as const,\n color: 'var(--color-status-info-text)',\n margin: 0,\n },\n emptyText: {\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n margin: 'var(--space-iconGap) 0 0 0',\n },\n clearButton: {\n marginTop: 'var(--space-controlGap)',\n minHeight: 'var(--size-controlHeight-md)',\n padding: '0 var(--space-formGap)',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)' as const,\n color: 'var(--color-action-primary-text)',\n backgroundColor: 'var(--color-action-primary-bg)',\n border: 'none',\n borderRadius: 'var(--radius-button)',\n cursor: 'pointer',\n },\n // Terminal band. Surface fill + a hairline, matching the header's rule rather\n // than repeating the sunken tray, so top and bottom are no longer identical.\n footer: {\n padding: 'var(--space-controlGap) var(--space-formGap)',\n borderTop: '1px solid var(--color-border-subtle)',\n backgroundColor: 'var(--color-bg-surface)',\n flexShrink: 0,\n },\n footerText: {\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n margin: 0,\n textAlign: 'center' as const,\n },\n browseStoreButton: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 'var(--space-iconGap)',\n width: '100%',\n minHeight: 'var(--size-controlHeight-md)',\n padding: 'var(--space-controlGap)',\n marginBottom: 'var(--space-controlGap)',\n borderRadius: 'var(--radius-button)',\n border: '1px dashed var(--color-border-subtle)',\n backgroundColor: 'transparent',\n color: 'var(--color-text-secondary)',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)' as const,\n cursor: 'pointer',\n transition: 'border-color 0.15s, color 0.15s',\n },\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetPalette: FC<WidgetPaletteProps> = memo(function WidgetPalette({\n isOpen,\n onClose,\n onWidgetSelect,\n onDragStart,\n onDragEnd,\n installedWidgets = [],\n installedWidgetsLoading = false,\n definitions,\n}) {\n const { t } = useI18n();\n const [searchQuery, setSearchQuery] = useState('');\n const [expandedCategories, setExpandedCategories] = useState<Set<WidgetCategory>>(\n new Set(['KPI', 'Visualization', 'Data', 'Content', 'Temporal', 'Custom', 'Installed'])\n );\n const [hoveredWidget, setHoveredWidget] = useState<string | null>(null);\n\n // Get categorized widgets, merging in installed widgets. When the parent\n // supplies a backend-sourced catalog (`definitions`), group that by category;\n // otherwise fall back to the bundled static registry.\n const categories = useMemo(() => {\n const baseCategories = definitions\n ? (Object.keys(WIDGET_CATEGORY_LABELS) as WidgetCategory[]).map((category) => ({\n category,\n label: WIDGET_CATEGORY_LABELS[category],\n widgets: definitions.filter((def) => def.category === category),\n }))\n : getWidgetCategories();\n\n return baseCategories\n .map((cat) => {\n if (cat.category === 'Installed') {\n // Two sources feed \"Installed from Store\": the registry-driven backend\n // catalog (getWidgetTypes store types — rich metadata, install-gated,\n // BOFF-2858) which arrives via `definitions` under this category, and\n // the legacy workspaceInstalledApps listing passed as `installedWidgets`.\n // Merge both, deduping by name (the catalog entry wins) so a widget\n // present in both sources shows once with the richer definition, while\n // legacy installs not yet in the registry are still preserved.\n const byName = new Map<string, WidgetDefinition>();\n for (const w of installedWidgets) byName.set(w.name.toLowerCase(), w);\n for (const w of cat.widgets) {\n const key = w.name.toLowerCase();\n const legacy = byName.get(key);\n // Catalog wins for metadata but MERGE defaultConfig so the legacy\n // workspaceInstalledApps fields survive — notably `componentId`, which the\n // current render path needs and the catalog store entry lacks until\n // widgetTypeKey rendering is wired. Keeps overlapping store widgets renderable.\n byName.set(key, legacy ? { ...w, defaultConfig: { ...legacy.defaultConfig, ...w.defaultConfig } } : w);\n }\n return { ...cat, widgets: Array.from(byName.values()) };\n }\n return cat;\n })\n .filter((cat) => cat.category !== 'Installed' || cat.widgets.length > 0);\n }, [installedWidgets, definitions]);\n\n // Filter widgets by search query\n const filteredCategories = useMemo(() => {\n if (!searchQuery.trim()) {\n return categories;\n }\n\n const query = searchQuery.toLowerCase();\n return categories\n .map((cat) => ({\n ...cat,\n widgets: cat.widgets.filter(\n (w) =>\n w.name.toLowerCase().includes(query) ||\n w.description.toLowerCase().includes(query) ||\n w.tags.some((tag) => tag.includes(query))\n ),\n }))\n .filter((cat) => cat.widgets.length > 0);\n }, [categories, searchQuery]);\n\n // Toggle category expansion\n const toggleCategory = useCallback((category: WidgetCategory) => {\n setExpandedCategories((prev) => {\n const next = new Set(prev);\n if (next.has(category)) {\n next.delete(category);\n } else {\n next.add(category);\n }\n return next;\n });\n }, []);\n\n // Handle drag start\n const handleDragStart = useCallback(\n (e: React.DragEvent, widget: WidgetDefinition) => {\n e.dataTransfer.setData(\n 'application/json',\n JSON.stringify({\n type: widget.type,\n name: widget.name,\n defaultSize: widget.defaultSize,\n defaultConfig: widget.defaultConfig,\n category: widget.category,\n })\n );\n e.dataTransfer.effectAllowed = 'copy';\n\n // Create custom drag image\n const dragImage = document.createElement('div');\n dragImage.style.cssText = `\n position: absolute;\n top: -1000px;\n left: -1000px;\n padding: 8px 12px;\n background: var(--color-bg-surface);\n border: 2px solid var(--color-action-primary-bg);\n border-radius: var(--radius-card);\n font-size: var(--font-size-body-sm);\n font-weight: 500;\n color: var(--color-text-primary);\n box-shadow: var(--shadow-elevation-3);\n `;\n dragImage.textContent = widget.name;\n document.body.appendChild(dragImage);\n e.dataTransfer.setDragImage(dragImage, 0, 0);\n\n setTimeout(() => {\n document.body.removeChild(dragImage);\n }, 0);\n\n onDragStart?.(widget.type, widget);\n },\n [onDragStart]\n );\n\n // Handle drag end\n const handleDragEnd = useCallback(() => {\n onDragEnd?.();\n }, [onDragEnd]);\n\n // Handle click to quick-add\n const handleWidgetClick = useCallback(\n (widget: WidgetDefinition) => {\n onWidgetSelect?.(widget.type, widget);\n },\n [onWidgetSelect]\n );\n\n // Open the widget store in a new tab so the builder (and any unsaved edits)\n // stays intact. Preserves the current org/workspace/project query params.\n const handleBrowseStore = useCallback(() => {\n if (typeof window === 'undefined') return;\n const url = `${window.location.origin}/bigconsole/store${window.location.search}`;\n window.open(url, '_blank', 'noopener,noreferrer');\n }, []);\n\n if (!isOpen) {\n return null;\n }\n\n return (\n <section\n aria-label=\"Widget palette\"\n data-testid=\"widget-palette\"\n style={styles.container}\n className=\"h-auto max-h-[45vh] w-full border-b border-[var(--color-border-subtle)] md:h-full md:max-h-none md:w-[300px] md:border-b-0 md:border-r\"\n >\n {/* Header — the panel's identity band */}\n <div style={styles.header}>\n <div style={styles.headerTop}>\n <div style={styles.headerTitle}>\n <div style={styles.headerIcon}>\n <Layers\n aria-hidden=\"true\"\n style={{ width: '18px', height: '18px', color: 'var(--color-action-primary-text)' }}\n />\n </div>\n <div>\n <h2 style={styles.title}>{t('bigconsole.dashboards.widgetPalette.title', 'Widgets')}</h2>\n <p style={styles.subtitle}>{t('bigconsole.dashboards.widgetPalette.subtitle', 'Drag or click to add')}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close widget palette\"\n title=\"Close widget palette\"\n style={styles.closeButton}\n className={`hover:bg-action-ghost-bgHover motion-safe:transition-colors ${SECTION_FOCUS_RING}`}\n >\n <X aria-hidden=\"true\" style={{ width: '16px', height: '16px' }} />\n </button>\n </div>\n\n {/* Search */}\n <div style={styles.searchContainer}>\n <label htmlFor=\"widget-palette-search\" className=\"sr-only\">\n Search widgets\n </label>\n <Search aria-hidden=\"true\" style={styles.searchIcon} />\n <Input\n id=\"widget-palette-search\"\n type=\"text\"\n placeholder={t('bigconsole.dashboards.widgetPalette.searchPlaceholder', 'Search widgets...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n style={styles.searchInput}\n />\n </div>\n </div>\n\n {/* Widget Categories — recessed tray */}\n <div style={styles.content}>\n <div style={styles.scrollArea}>\n {filteredCategories.map((category) => {\n const isExpanded = expandedCategories.has(category.category);\n const panelId = `widget-palette-${category.category.toLowerCase()}`;\n return (\n <div key={category.category} style={styles.categoryContainer}>\n {/* Category Header */}\n <h3 style={styles.categoryHeading}>\n <button\n type=\"button\"\n onClick={() => toggleCategory(category.category)}\n aria-expanded={isExpanded}\n aria-controls={panelId}\n style={styles.categoryHeader}\n className={`hover:bg-action-ghost-bgHover motion-safe:transition-colors ${SECTION_FOCUS_RING}`}\n >\n {isExpanded ? (\n <ChevronDown aria-hidden=\"true\" style={styles.categoryChevron} />\n ) : (\n <ChevronRight aria-hidden=\"true\" style={styles.categoryChevron} />\n )}\n <span style={styles.categoryLabel}>{category.label}</span>\n <span style={styles.categoryCount}>{category.widgets.length}</span>\n </button>\n </h3>\n\n {/* Widget Items */}\n {isExpanded && (\n <div id={panelId} style={styles.widgetList}>\n {installedWidgetsLoading && category.category === 'Installed' && (\n <div\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n padding: 'var(--space-controlGap)',\n }}\n >\n {t('bigconsole.dashboards.widgetPalette.loading', 'Loading...')}\n </div>\n )}\n {category.widgets.map((widget) => {\n const IconComponent = getIconComponent(widget.icon);\n const widgetKey = getWidgetKey(widget);\n const isHovered = hoveredWidget === widgetKey;\n\n return (\n <div\n key={widgetKey}\n draggable\n role=\"button\"\n tabIndex={0}\n aria-label={`Add ${widget.name} widget`}\n onDragStart={(e) => handleDragStart(e, widget)}\n onDragEnd={handleDragEnd}\n onClick={() => handleWidgetClick(widget)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleWidgetClick(widget);\n }\n }}\n onMouseEnter={() => setHoveredWidget(widgetKey)}\n onMouseLeave={() => setHoveredWidget(null)}\n className={SECTION_FOCUS_RING}\n style={{\n ...styles.widgetCard,\n ...(isHovered ? styles.widgetCardHover : {}),\n }}\n >\n {/* Drag Handle */}\n <GripVertical aria-hidden=\"true\" style={styles.dragHandle} />\n\n {/* Icon */}\n <div style={styles.widgetIcon}>\n <IconComponent aria-hidden=\"true\" style={styles.widgetIconInner} />\n </div>\n\n {/* Text */}\n <div style={styles.widgetText}>\n <div style={styles.widgetName}>{widget.name}</div>\n <div style={styles.widgetDescription}>{widget.description}</div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n })}\n\n {filteredCategories.length === 0 && (\n <div style={styles.emptyState} role=\"status\">\n <div style={styles.emptyIcon}>\n <Search\n aria-hidden=\"true\"\n style={{ width: '24px', height: '24px', color: 'var(--color-text-muted)' }}\n />\n </div>\n <p style={styles.emptyTitle}>\n {t('bigconsole.dashboards.widgetPalette.noResultsBefore', 'No widgets found for')} “\n <strong>{searchQuery}</strong>”\n </p>\n {/* No catalog key exists for this hint — literal until one lands in\n src/translations/en.ts. */}\n <p style={styles.emptyText}>Try a shorter term, or browse the widget store for more types.</p>\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n style={styles.clearButton}\n className={SECTION_FOCUS_RING}\n >\n {t('bigconsole.dashboards.widgetPalette.clearSearch', 'Clear search')}\n </button>\n </div>\n )}\n </div>\n </div>\n\n {/* Footer — terminal band */}\n <div style={styles.footer}>\n <button\n type=\"button\"\n onClick={handleBrowseStore}\n style={styles.browseStoreButton}\n className={`hover:border-action-primary-bg hover:text-action-primary-text motion-safe:transition-colors ${SECTION_FOCUS_RING}`}\n >\n <Store aria-hidden=\"true\" style={{ width: '15px', height: '15px' }} />\n {t('bigconsole.dashboards.widgetPalette.browseStore', 'Browse widget store')}\n <ExternalLink aria-hidden=\"true\" style={{ width: '13px', height: '13px', opacity: 0.7 }} />\n </button>\n <p style={styles.footerText}>\n {t('bigconsole.dashboards.widgetPalette.footerHint', 'Drag widgets to position them on the canvas')}\n </p>\n </div>\n </section>\n );\n});\n\nexport default WidgetPalette;\n"],"mappings":"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"}
|
|
@@ -73,7 +73,7 @@ var f = e(function({ value: e, onChange: u, disabled: f = !1, error: p, hideLabe
|
|
|
73
73
|
C(null), u(n);
|
|
74
74
|
}, [u, h]);
|
|
75
75
|
return /* @__PURE__ */ l("div", {
|
|
76
|
-
className: "space-y-
|
|
76
|
+
className: "space-y-form-gap",
|
|
77
77
|
children: [
|
|
78
78
|
!m && /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
|
|
79
79
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
@@ -83,16 +83,17 @@ var f = e(function({ value: e, onChange: u, disabled: f = !1, error: p, hideLabe
|
|
|
83
83
|
children: h("bigconsole.dataSinks.cacheTtl.headingDescription", "How long should data be cached before refreshing?")
|
|
84
84
|
})] }),
|
|
85
85
|
/* @__PURE__ */ l("div", {
|
|
86
|
-
|
|
86
|
+
role: "group",
|
|
87
|
+
"aria-label": h("bigconsole.dataSinks.cacheTtl.heading", "Cache Duration"),
|
|
88
|
+
className: "space-y-list-gap",
|
|
87
89
|
children: [g.map((t) => /* @__PURE__ */ l("label", {
|
|
88
90
|
className: `
|
|
89
|
-
flex items-start gap-3
|
|
90
|
-
|
|
91
|
-
border
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
${
|
|
95
|
-
${!v && e === t.value ? "border-action-primary-bg bg-action-primary-bg/5" : "border-border-default hover:border-border-hover"}
|
|
91
|
+
flex cursor-pointer items-start gap-3
|
|
92
|
+
rounded-input
|
|
93
|
+
border p-3
|
|
94
|
+
motion-safe:transition-colors
|
|
95
|
+
${f ? "cursor-not-allowed opacity-50" : ""}
|
|
96
|
+
${!v && e === t.value ? "border-[var(--color-action-primary-bg)] bg-bg-accentSubtle" : "border-[var(--color-border-subtle)] hover:border-[var(--color-border-strong)]"}
|
|
96
97
|
`,
|
|
97
98
|
children: [/* @__PURE__ */ c("input", {
|
|
98
99
|
type: "radio",
|
|
@@ -100,7 +101,7 @@ var f = e(function({ value: e, onChange: u, disabled: f = !1, error: p, hideLabe
|
|
|
100
101
|
checked: !v && e === t.value,
|
|
101
102
|
onChange: () => T(t.value),
|
|
102
103
|
disabled: f,
|
|
103
|
-
className: "\n mt-0.5\n
|
|
104
|
+
className: "\n mt-0.5\n h-4 w-4\n border-[var(--color-border-default)]\n text-action-primary-bg\n focus-visible:ring-2\n focus-visible:ring-ring\n "
|
|
104
105
|
}), /* @__PURE__ */ l("div", {
|
|
105
106
|
className: "flex-1",
|
|
106
107
|
children: [/* @__PURE__ */ l("div", {
|
|
@@ -119,13 +120,12 @@ var f = e(function({ value: e, onChange: u, disabled: f = !1, error: p, hideLabe
|
|
|
119
120
|
})]
|
|
120
121
|
}, t.value)), /* @__PURE__ */ l("label", {
|
|
121
122
|
className: `
|
|
122
|
-
flex items-start gap-3
|
|
123
|
-
|
|
124
|
-
border
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
${
|
|
128
|
-
${v ? "border-action-primary-bg bg-action-primary-bg/5" : "border-border-default hover:border-border-hover"}
|
|
123
|
+
flex cursor-pointer items-start gap-3
|
|
124
|
+
rounded-input
|
|
125
|
+
border p-3
|
|
126
|
+
motion-safe:transition-colors
|
|
127
|
+
${f ? "cursor-not-allowed opacity-50" : ""}
|
|
128
|
+
${v ? "border-[var(--color-action-primary-bg)] bg-bg-accentSubtle" : "border-[var(--color-border-subtle)] hover:border-[var(--color-border-strong)]"}
|
|
129
129
|
`,
|
|
130
130
|
children: [/* @__PURE__ */ c("input", {
|
|
131
131
|
type: "radio",
|
|
@@ -133,7 +133,7 @@ var f = e(function({ value: e, onChange: u, disabled: f = !1, error: p, hideLabe
|
|
|
133
133
|
checked: v,
|
|
134
134
|
onChange: E,
|
|
135
135
|
disabled: f,
|
|
136
|
-
className: "\n mt-0.5\n
|
|
136
|
+
className: "\n mt-0.5\n h-4 w-4\n border-[var(--color-border-default)]\n text-action-primary-bg\n focus-visible:ring-2\n focus-visible:ring-ring\n "
|
|
137
137
|
}), /* @__PURE__ */ l("div", {
|
|
138
138
|
className: "flex-1",
|
|
139
139
|
children: [
|
|
@@ -146,10 +146,11 @@ var f = e(function({ value: e, onChange: u, disabled: f = !1, error: p, hideLabe
|
|
|
146
146
|
children: h("bigconsole.dataSinks.cacheTtl.customDescription", "Enter a custom cache duration (30 - 86400 seconds)")
|
|
147
147
|
}),
|
|
148
148
|
v && /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ l("div", {
|
|
149
|
-
className: "mt-3 flex items-center gap-2",
|
|
149
|
+
className: "mt-3 flex flex-wrap items-center gap-2",
|
|
150
150
|
children: [
|
|
151
151
|
/* @__PURE__ */ c("input", {
|
|
152
152
|
type: "number",
|
|
153
|
+
"aria-label": "Custom cache duration in seconds",
|
|
153
154
|
value: b,
|
|
154
155
|
onChange: D,
|
|
155
156
|
disabled: f,
|
|
@@ -157,16 +158,15 @@ var f = e(function({ value: e, onChange: u, disabled: f = !1, error: p, hideLabe
|
|
|
157
158
|
max: 86400,
|
|
158
159
|
placeholder: "300",
|
|
159
160
|
className: `
|
|
160
|
-
w-
|
|
161
|
-
bg-bg-sunken
|
|
162
|
-
|
|
161
|
+
h-control-md w-full min-w-0 rounded-input
|
|
162
|
+
border bg-bg-sunken
|
|
163
|
+
px-3
|
|
163
164
|
text-sm text-text-primary
|
|
164
|
-
|
|
165
|
-
focus:
|
|
166
|
-
focus:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
${p || S ? "border-status-error-border" : "border-border-default"}
|
|
165
|
+
focus-visible:outline-none
|
|
166
|
+
focus-visible:ring-2
|
|
167
|
+
focus-visible:ring-ring
|
|
168
|
+
sm:w-32
|
|
169
|
+
${p || S ? "border-[var(--color-status-error-border)]" : "border-[var(--color-border-default)]"}
|
|
170
170
|
`
|
|
171
171
|
}),
|
|
172
172
|
/* @__PURE__ */ c("span", {
|
|
@@ -174,7 +174,7 @@ var f = e(function({ value: e, onChange: u, disabled: f = !1, error: p, hideLabe
|
|
|
174
174
|
children: h("bigconsole.dataSinks.cacheTtl.secondsUnit", "seconds")
|
|
175
175
|
}),
|
|
176
176
|
b && !isNaN(parseInt(b)) && !S && /* @__PURE__ */ l("span", {
|
|
177
|
-
className: "text-xs text-text-
|
|
177
|
+
className: "text-xs text-text-muted",
|
|
178
178
|
children: [
|
|
179
179
|
"(",
|
|
180
180
|
_(parseInt(b)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CacheTTLSelector.js","names":[],"sources":["../../../../src/bigconsole/components/datasink/CacheTTLSelector.tsx"],"sourcesContent":["/**\n * Cache TTL Selector\n *\n * Component for selecting cache duration with preset options and custom input.\n */\n\nimport { memo, useState, useEffect, useCallback, useMemo, useRef } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface CacheTTLOption {\n value: number;\n label: string;\n description: string;\n recommended?: boolean;\n}\n\ninterface CacheTTLSelectorProps {\n /** Current TTL value in seconds (null for no cache) */\n value: number | null;\n /** Callback when value changes */\n onChange: (value: number) => void;\n /** Whether selector is disabled */\n disabled?: boolean;\n /** Error message */\n error?: string;\n /**\n * Hide the built-in \"Cache Duration\" heading + description. Use when the\n * surrounding form already renders its own label for this control, to avoid a\n * duplicated heading.\n */\n hideLabel?: boolean;\n}\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst PRESET_VALUES: number[] = [300, 900, 3600, 86400];\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\n/**\n * Check if value is a preset option\n */\nfunction isPresetValue(value: number | null): boolean {\n if (value === null) return false;\n return PRESET_VALUES.includes(value);\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const CacheTTLSelector = memo(function CacheTTLSelector({\n value,\n onChange,\n disabled = false,\n error,\n hideLabel = false,\n}: CacheTTLSelectorProps) {\n const { t } = useI18n();\n\n const PRESET_OPTIONS = useMemo<CacheTTLOption[]>(\n () => [\n {\n value: 300, // 5 minutes\n label: t('bigconsole.dataSinks.cacheTtl.preset5MinutesLabel', '5 minutes'),\n description: t('bigconsole.dataSinks.cacheTtl.preset5MinutesDescription', 'Best for real-time dashboards'),\n },\n {\n value: 900, // 15 minutes\n label: t('bigconsole.dataSinks.cacheTtl.preset15MinutesLabel', '15 minutes'),\n description: t('bigconsole.dataSinks.cacheTtl.preset15MinutesDescription', 'Balanced performance'),\n recommended: true,\n },\n {\n value: 3600, // 1 hour\n label: t('bigconsole.dataSinks.cacheTtl.preset1HourLabel', '1 hour'),\n description: t('bigconsole.dataSinks.cacheTtl.preset1HourDescription', 'For data that changes hourly'),\n },\n {\n value: 86400, // 24 hours\n label: t('bigconsole.dataSinks.cacheTtl.preset24HoursLabel', '24 hours'),\n description: t('bigconsole.dataSinks.cacheTtl.preset24HoursDescription', 'For daily reports'),\n },\n ],\n [t]\n );\n\n // Format seconds to human-readable string\n const formatDuration = useCallback(\n (seconds: number): string => {\n if (seconds < 60) {\n return t('bigconsole.dataSinks.cacheTtl.durationSeconds', '{{count}} seconds', { count: seconds });\n }\n if (seconds < 3600) {\n return t('bigconsole.dataSinks.cacheTtl.durationMinutes', '{{count}} minutes', {\n count: Math.floor(seconds / 60),\n });\n }\n if (seconds < 86400) {\n return t('bigconsole.dataSinks.cacheTtl.durationHours', '{{count}} hours', {\n count: Math.floor(seconds / 3600),\n });\n }\n return t('bigconsole.dataSinks.cacheTtl.durationDays', '{{count}} days', {\n count: Math.floor(seconds / 86400),\n });\n },\n [t]\n );\n\n // Track whether custom mode is active\n const [isCustom, setIsCustom] = useState(!isPresetValue(value) && value !== null);\n const [customValue, setCustomValue] = useState(value?.toString() || '');\n const [validationError, setValidationError] = useState<string | null>(null);\n const prevValueRef = useRef(value);\n\n // Sync custom value when external value changes (using deferred update to avoid cascading renders)\n useEffect(() => {\n if (prevValueRef.current !== value) {\n prevValueRef.current = value;\n if (value !== null && !isPresetValue(value)) {\n // Defer state update to avoid synchronous setState in effect\n queueMicrotask(() => {\n setCustomValue(value.toString());\n setIsCustom(true);\n });\n }\n }\n }, [value]);\n\n // Handle preset selection\n const handlePresetChange = useCallback(\n (presetValue: number) => {\n if (disabled) return;\n setIsCustom(false);\n onChange(presetValue);\n },\n [disabled, onChange]\n );\n\n // Handle custom mode activation\n const handleCustomSelect = useCallback(() => {\n if (disabled) return;\n setIsCustom(true);\n const parsed = parseInt(customValue);\n if (!isNaN(parsed) && parsed >= 30 && parsed <= 86400) {\n onChange(parsed);\n }\n }, [disabled, customValue, onChange]);\n\n // Handle custom value change with validation\n const handleCustomChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const inputValue = e.target.value;\n setCustomValue(inputValue);\n\n // Validate input\n if (inputValue === '') {\n setValidationError(null);\n return;\n }\n\n const parsed = parseInt(inputValue);\n\n if (isNaN(parsed)) {\n setValidationError(t('bigconsole.dataSinks.cacheTtl.errorInvalidNumber', 'Please enter a valid number'));\n return;\n }\n\n if (parsed < 30) {\n setValidationError(t('bigconsole.dataSinks.cacheTtl.errorMinDuration', 'Minimum cache duration is 30 seconds'));\n return;\n }\n\n if (parsed > 86400) {\n setValidationError(\n t('bigconsole.dataSinks.cacheTtl.errorMaxDuration', 'Maximum cache duration is 86400 seconds (24 hours)')\n );\n return;\n }\n\n // Valid value\n setValidationError(null);\n onChange(parsed);\n },\n [onChange, t]\n );\n\n return (\n <div className=\"space-y-3\">\n {/* Label (optional — hidden when the surrounding form supplies its own) */}\n {!hideLabel && (\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {t('bigconsole.dataSinks.cacheTtl.heading', 'Cache Duration')}\n </label>\n <p className=\"text-xs text-text-secondary\">\n {t('bigconsole.dataSinks.cacheTtl.headingDescription', 'How long should data be cached before refreshing?')}\n </p>\n </div>\n )}\n\n {/* Options */}\n <div className=\"space-y-2\">\n {/* Preset options */}\n {PRESET_OPTIONS.map((option) => (\n <label\n key={option.value}\n className={`\n flex items-start gap-3\n p-3\n border rounded-lg\n cursor-pointer\n transition-all\n ${disabled ? 'opacity-50 cursor-not-allowed' : ''}\n ${\n !isCustom && value === option.value\n ? 'border-action-primary-bg bg-action-primary-bg/5'\n : 'border-border-default hover:border-border-hover'\n }\n `}\n >\n <input\n type=\"radio\"\n name=\"cache-ttl\"\n checked={!isCustom && value === option.value}\n onChange={() => handlePresetChange(option.value)}\n disabled={disabled}\n className={`\n mt-0.5\n w-4 h-4\n text-action-primary-bg\n border-border-default\n focus:ring-action-primary-bg\n focus:ring-offset-0\n `}\n />\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">{option.label}</span>\n {option.recommended && (\n <span className=\"px-1.5 py-0.5 text-xs font-medium bg-action-primary-bg/10 text-action-primary-bg rounded\">\n {t('bigconsole.dataSinks.cacheTtl.recommendedBadge', 'Recommended')}\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary mt-0.5\">{option.description}</p>\n </div>\n </label>\n ))}\n\n {/* Custom option */}\n <label\n className={`\n flex items-start gap-3\n p-3\n border rounded-lg\n cursor-pointer\n transition-all\n ${disabled ? 'opacity-50 cursor-not-allowed' : ''}\n ${\n isCustom\n ? 'border-action-primary-bg bg-action-primary-bg/5'\n : 'border-border-default hover:border-border-hover'\n }\n `}\n >\n <input\n type=\"radio\"\n name=\"cache-ttl\"\n checked={isCustom}\n onChange={handleCustomSelect}\n disabled={disabled}\n className={`\n mt-0.5\n w-4 h-4\n text-action-primary-bg\n border-border-default\n focus:ring-action-primary-bg\n focus:ring-offset-0\n `}\n />\n <div className=\"flex-1\">\n <span className=\"text-sm font-medium text-text-primary\">\n {t('bigconsole.dataSinks.cacheTtl.customLabel', 'Custom')}\n </span>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {t(\n 'bigconsole.dataSinks.cacheTtl.customDescription',\n 'Enter a custom cache duration (30 - 86400 seconds)'\n )}\n </p>\n {isCustom && (\n <>\n <div className=\"mt-3 flex items-center gap-2\">\n <input\n type=\"number\"\n value={customValue}\n onChange={handleCustomChange}\n disabled={disabled}\n min={30}\n max={86400}\n placeholder=\"300\"\n className={`\n w-32 px-3 py-2\n bg-bg-sunken\n border rounded-md\n text-sm text-text-primary\n placeholder-text-tertiary\n focus:outline-none\n focus:border-action-primary-bg\n focus:ring-2\n focus:ring-action-primary-bg/20\n ${error || validationError ? 'border-status-error-border' : 'border-border-default'}\n `}\n />\n <span className=\"text-sm text-text-secondary\">\n {t('bigconsole.dataSinks.cacheTtl.secondsUnit', 'seconds')}\n </span>\n {customValue && !isNaN(parseInt(customValue)) && !validationError && (\n <span className=\"text-xs text-text-tertiary\">({formatDuration(parseInt(customValue))})</span>\n )}\n </div>\n {validationError && (\n <div className=\"flex items-center gap-1 mt-1\">\n <svg\n className=\"w-3.5 h-3.5 text-status-error-text flex-shrink-0\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <span className=\"text-xs text-status-error-text\">{validationError}</span>\n </div>\n )}\n </>\n )}\n </div>\n </label>\n </div>\n\n {/* Error message */}\n {error && (\n <div className=\"flex items-start gap-2\">\n <svg\n className=\"w-4 h-4 text-status-error-text flex-shrink-0 mt-0.5\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <p className=\"text-xs text-status-error-text\">{error}</p>\n </div>\n )}\n\n {/* Info message */}\n {value !== null && !error && (\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <span>\n {t(\n 'bigconsole.dataSinks.cacheTtl.infoCachedFor',\n 'Data will be cached for {{duration}} before being refreshed',\n {\n duration: formatDuration(value),\n }\n )}\n </span>\n </div>\n )}\n </div>\n );\n});\n\nexport default CacheTTLSelector;\n"],"mappings":";;;;AAyCA,IAAM,IAA0B;CAAC;CAAK;CAAK;CAAM;CAAM;AASvD,SAAS,EAAc,GAA+B;AAEpD,QADI,MAAU,OAAa,KACpB,EAAc,SAAS,EAAM;;AAOtC,IAAa,IAAmB,EAAK,SAA0B,EAC7D,UACA,aACA,cAAW,IACX,UACA,eAAY,MACY;CACxB,IAAM,EAAE,SAAM,GAAS,EAEjB,IAAiB,QACf;EACJ;GACE,OAAO;GACP,OAAO,EAAE,qDAAqD,YAAY;GAC1E,aAAa,EAAE,2DAA2D,gCAAgC;GAC3G;EACD;GACE,OAAO;GACP,OAAO,EAAE,sDAAsD,aAAa;GAC5E,aAAa,EAAE,4DAA4D,uBAAuB;GAClG,aAAa;GACd;EACD;GACE,OAAO;GACP,OAAO,EAAE,kDAAkD,SAAS;GACpE,aAAa,EAAE,wDAAwD,+BAA+B;GACvG;EACD;GACE,OAAO;GACP,OAAO,EAAE,oDAAoD,WAAW;GACxE,aAAa,EAAE,0DAA0D,oBAAoB;GAC9F;EACF,EACD,CAAC,EAAE,CACJ,EAGK,IAAiB,GACpB,MACK,IAAU,KACL,EAAE,iDAAiD,qBAAqB,EAAE,OAAO,GAAS,CAAC,GAEhG,IAAU,OACL,EAAE,iDAAiD,qBAAqB,EAC7E,OAAO,KAAK,MAAM,IAAU,GAAG,EAChC,CAAC,GAEA,IAAU,QACL,EAAE,+CAA+C,mBAAmB,EACzE,OAAO,KAAK,MAAM,IAAU,KAAK,EAClC,CAAC,GAEG,EAAE,8CAA8C,kBAAkB,EACvE,OAAO,KAAK,MAAM,IAAU,MAAM,EACnC,CAAC,EAEJ,CAAC,EAAE,CACJ,EAGK,CAAC,GAAU,KAAe,EAAS,CAAC,EAAc,EAAM,IAAI,MAAU,KAAK,EAC3E,CAAC,GAAa,KAAkB,EAAS,GAAO,UAAU,IAAI,GAAG,EACjE,CAAC,GAAiB,KAAsB,EAAwB,KAAK,EACrE,IAAe,EAAO,EAAM;AAGlC,SAAgB;AACd,EAAI,EAAa,YAAY,MAC3B,EAAa,UAAU,GACnB,MAAU,QAAQ,CAAC,EAAc,EAAM,IAEzC,qBAAqB;AAEnB,GADA,EAAe,EAAM,UAAU,CAAC,EAChC,EAAY,GAAK;IACjB;IAGL,CAAC,EAAM,CAAC;CAGX,IAAM,IAAqB,GACxB,MAAwB;AACnB,QACJ,EAAY,GAAM,EAClB,EAAS,EAAY;IAEvB,CAAC,GAAU,EAAS,CACrB,EAGK,IAAqB,QAAkB;AAC3C,MAAI,EAAU;AACd,IAAY,GAAK;EACjB,IAAM,IAAS,SAAS,EAAY;AACpC,EAAI,CAAC,MAAM,EAAO,IAAI,KAAU,MAAM,KAAU,SAC9C,EAAS,EAAO;IAEjB;EAAC;EAAU;EAAa;EAAS,CAAC,EAG/B,IAAqB,GACxB,MAA2C;EAC1C,IAAM,IAAa,EAAE,OAAO;AAI5B,MAHA,EAAe,EAAW,EAGtB,MAAe,IAAI;AACrB,KAAmB,KAAK;AACxB;;EAGF,IAAM,IAAS,SAAS,EAAW;AAEnC,MAAI,MAAM,EAAO,EAAE;AACjB,KAAmB,EAAE,oDAAoD,8BAA8B,CAAC;AACxG;;AAGF,MAAI,IAAS,IAAI;AACf,KAAmB,EAAE,kDAAkD,uCAAuC,CAAC;AAC/G;;AAGF,MAAI,IAAS,OAAO;AAClB,KACE,EAAE,kDAAkD,qDAAqD,CAC1G;AACD;;AAKF,EADA,EAAmB,KAAK,EACxB,EAAS,EAAO;IAElB,CAAC,GAAU,EAAE,CACd;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEG,CAAC,KACA,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;IAAO,WAAU;cACd,EAAE,yCAAyC,iBAAiB;IACvD,CAAA,EACR,kBAAC,KAAD;IAAG,WAAU;cACV,EAAE,oDAAoD,oDAAoD;IACzG,CAAA,CACA,EAAA,CAAA;GAIR,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEG,EAAe,KAAK,MACnB,kBAAC,SAAD;KAEE,WAAW;;;;;;gBAMP,IAAW,kCAAkC,GAAG;gBAEhD,CAAC,KAAY,MAAU,EAAO,QAC1B,oDACA,kDACL;;eAbL,CAgBE,kBAAC,SAAD;MACE,MAAK;MACL,MAAK;MACL,SAAS,CAAC,KAAY,MAAU,EAAO;MACvC,gBAAgB,EAAmB,EAAO,MAAM;MACtC;MACV,WAAW;MAQX,CAAA,EACF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAyC,EAAO;QAAa,CAAA,EAC5E,EAAO,eACN,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAE,kDAAkD,cAAc;QAC9D,CAAA,CAEL;UACN,kBAAC,KAAD;OAAG,WAAU;iBAAsC,EAAO;OAAgB,CAAA,CACtE;QACA;OAzCD,EAAO,MAyCN,CACR,EAGF,kBAAC,SAAD;KACE,WAAW;;;;;;cAMP,IAAW,kCAAkC,GAAG;cAEhD,IACI,oDACA,kDACL;;eAZL,CAeE,kBAAC,SAAD;MACE,MAAK;MACL,MAAK;MACL,SAAS;MACT,UAAU;MACA;MACV,WAAW;MAQX,CAAA,EACF,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAE,6CAA6C,SAAS;QACpD,CAAA;OACP,kBAAC,KAAD;QAAG,WAAU;kBACV,EACC,mDACA,qDACD;QACC,CAAA;OACH,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,SAAD;UACE,MAAK;UACL,OAAO;UACP,UAAU;UACA;UACV,KAAK;UACL,KAAK;UACL,aAAY;UACZ,WAAW;;;;;;;;;;wBAUP,KAAS,IAAkB,+BAA+B,wBAAwB;;UAEtF,CAAA;SACF,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAE,6CAA6C,UAAU;UACrD,CAAA;SACN,KAAe,CAAC,MAAM,SAAS,EAAY,CAAC,IAAI,CAAC,KAChD,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WAA6C;WAAE,EAAe,SAAS,EAAY,CAAC;WAAC;WAAQ;;SAE3F;WACL,KACC,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SACE,WAAU;SACV,MAAK;SACL,QAAO;SACP,SAAQ;mBAER,kBAAC,QAAD;UACE,eAAc;UACd,gBAAe;UACf,aAAa;UACb,GAAE;UACF,CAAA;SACE,CAAA,EACN,kBAAC,QAAD;SAAM,WAAU;mBAAkC;SAAuB,CAAA,CACrE;UAEP,EAAA,CAAA;OAED;QACA;OACJ;;GAGL,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KACE,WAAU;KACV,MAAK;KACL,QAAO;KACP,SAAQ;eAER,kBAAC,QAAD;MACE,eAAc;MACd,gBAAe;MACf,aAAa;MACb,GAAE;MACF,CAAA;KACE,CAAA,EACN,kBAAC,KAAD;KAAG,WAAU;eAAkC;KAAU,CAAA,CACrD;;GAIP,MAAU,QAAQ,CAAC,KAClB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;KAAU,MAAK;KAAO,QAAO;KAAe,SAAQ;eACjE,kBAAC,QAAD;MACE,eAAc;MACd,gBAAe;MACf,aAAa;MACb,GAAE;MACF,CAAA;KACE,CAAA,EACN,kBAAC,QAAD,EAAA,UACG,EACC,+CACA,+DACA,EACE,UAAU,EAAe,EAAM,EAChC,CACF,EACI,CAAA,CACH;;GAEJ;;EAER"}
|
|
1
|
+
{"version":3,"file":"CacheTTLSelector.js","names":[],"sources":["../../../../src/bigconsole/components/datasink/CacheTTLSelector.tsx"],"sourcesContent":["/**\n * Cache TTL Selector\n *\n * Component for selecting cache duration with preset options and custom input.\n */\n\nimport { memo, useState, useEffect, useCallback, useMemo, useRef } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface CacheTTLOption {\n value: number;\n label: string;\n description: string;\n recommended?: boolean;\n}\n\ninterface CacheTTLSelectorProps {\n /** Current TTL value in seconds (null for no cache) */\n value: number | null;\n /** Callback when value changes */\n onChange: (value: number) => void;\n /** Whether selector is disabled */\n disabled?: boolean;\n /** Error message */\n error?: string;\n /**\n * Hide the built-in \"Cache Duration\" heading + description. Use when the\n * surrounding form already renders its own label for this control, to avoid a\n * duplicated heading.\n */\n hideLabel?: boolean;\n}\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst PRESET_VALUES: number[] = [300, 900, 3600, 86400];\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\n/**\n * Check if value is a preset option\n */\nfunction isPresetValue(value: number | null): boolean {\n if (value === null) return false;\n return PRESET_VALUES.includes(value);\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const CacheTTLSelector = memo(function CacheTTLSelector({\n value,\n onChange,\n disabled = false,\n error,\n hideLabel = false,\n}: CacheTTLSelectorProps) {\n const { t } = useI18n();\n\n const PRESET_OPTIONS = useMemo<CacheTTLOption[]>(\n () => [\n {\n value: 300, // 5 minutes\n label: t('bigconsole.dataSinks.cacheTtl.preset5MinutesLabel', '5 minutes'),\n description: t('bigconsole.dataSinks.cacheTtl.preset5MinutesDescription', 'Best for real-time dashboards'),\n },\n {\n value: 900, // 15 minutes\n label: t('bigconsole.dataSinks.cacheTtl.preset15MinutesLabel', '15 minutes'),\n description: t('bigconsole.dataSinks.cacheTtl.preset15MinutesDescription', 'Balanced performance'),\n recommended: true,\n },\n {\n value: 3600, // 1 hour\n label: t('bigconsole.dataSinks.cacheTtl.preset1HourLabel', '1 hour'),\n description: t('bigconsole.dataSinks.cacheTtl.preset1HourDescription', 'For data that changes hourly'),\n },\n {\n value: 86400, // 24 hours\n label: t('bigconsole.dataSinks.cacheTtl.preset24HoursLabel', '24 hours'),\n description: t('bigconsole.dataSinks.cacheTtl.preset24HoursDescription', 'For daily reports'),\n },\n ],\n [t]\n );\n\n // Format seconds to human-readable string\n const formatDuration = useCallback(\n (seconds: number): string => {\n if (seconds < 60) {\n return t('bigconsole.dataSinks.cacheTtl.durationSeconds', '{{count}} seconds', { count: seconds });\n }\n if (seconds < 3600) {\n return t('bigconsole.dataSinks.cacheTtl.durationMinutes', '{{count}} minutes', {\n count: Math.floor(seconds / 60),\n });\n }\n if (seconds < 86400) {\n return t('bigconsole.dataSinks.cacheTtl.durationHours', '{{count}} hours', {\n count: Math.floor(seconds / 3600),\n });\n }\n return t('bigconsole.dataSinks.cacheTtl.durationDays', '{{count}} days', {\n count: Math.floor(seconds / 86400),\n });\n },\n [t]\n );\n\n // Track whether custom mode is active\n const [isCustom, setIsCustom] = useState(!isPresetValue(value) && value !== null);\n const [customValue, setCustomValue] = useState(value?.toString() || '');\n const [validationError, setValidationError] = useState<string | null>(null);\n const prevValueRef = useRef(value);\n\n // Sync custom value when external value changes (using deferred update to avoid cascading renders)\n useEffect(() => {\n if (prevValueRef.current !== value) {\n prevValueRef.current = value;\n if (value !== null && !isPresetValue(value)) {\n // Defer state update to avoid synchronous setState in effect\n queueMicrotask(() => {\n setCustomValue(value.toString());\n setIsCustom(true);\n });\n }\n }\n }, [value]);\n\n // Handle preset selection\n const handlePresetChange = useCallback(\n (presetValue: number) => {\n if (disabled) return;\n setIsCustom(false);\n onChange(presetValue);\n },\n [disabled, onChange]\n );\n\n // Handle custom mode activation\n const handleCustomSelect = useCallback(() => {\n if (disabled) return;\n setIsCustom(true);\n const parsed = parseInt(customValue);\n if (!isNaN(parsed) && parsed >= 30 && parsed <= 86400) {\n onChange(parsed);\n }\n }, [disabled, customValue, onChange]);\n\n // Handle custom value change with validation\n const handleCustomChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const inputValue = e.target.value;\n setCustomValue(inputValue);\n\n // Validate input\n if (inputValue === '') {\n setValidationError(null);\n return;\n }\n\n const parsed = parseInt(inputValue);\n\n if (isNaN(parsed)) {\n setValidationError(t('bigconsole.dataSinks.cacheTtl.errorInvalidNumber', 'Please enter a valid number'));\n return;\n }\n\n if (parsed < 30) {\n setValidationError(t('bigconsole.dataSinks.cacheTtl.errorMinDuration', 'Minimum cache duration is 30 seconds'));\n return;\n }\n\n if (parsed > 86400) {\n setValidationError(\n t('bigconsole.dataSinks.cacheTtl.errorMaxDuration', 'Maximum cache duration is 86400 seconds (24 hours)')\n );\n return;\n }\n\n // Valid value\n setValidationError(null);\n onChange(parsed);\n },\n [onChange, t]\n );\n\n return (\n <div className=\"space-y-form-gap\">\n {/* Label — suppressed with `hideLabel` when the surrounding form already\n renders one for this control, so the section never shows a duplicated\n heading. The `role=\"group\"` name below keeps the control set announced\n to assistive tech either way. */}\n {!hideLabel && (\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {t('bigconsole.dataSinks.cacheTtl.heading', 'Cache Duration')}\n </label>\n <p className=\"text-xs text-text-secondary\">\n {t('bigconsole.dataSinks.cacheTtl.headingDescription', 'How long should data be cached before refreshing?')}\n </p>\n </div>\n )}\n\n {/* Options */}\n <div\n role=\"group\"\n aria-label={t('bigconsole.dataSinks.cacheTtl.heading', 'Cache Duration')}\n className=\"space-y-list-gap\"\n >\n {/* Preset options */}\n {PRESET_OPTIONS.map((option) => (\n <label\n key={option.value}\n className={`\n flex cursor-pointer items-start gap-3\n rounded-input\n border p-3\n motion-safe:transition-colors\n ${disabled ? 'cursor-not-allowed opacity-50' : ''}\n ${\n !isCustom && value === option.value\n ? 'border-[var(--color-action-primary-bg)] bg-bg-accentSubtle'\n : 'border-[var(--color-border-subtle)] hover:border-[var(--color-border-strong)]'\n }\n `}\n >\n <input\n type=\"radio\"\n name=\"cache-ttl\"\n checked={!isCustom && value === option.value}\n onChange={() => handlePresetChange(option.value)}\n disabled={disabled}\n className={`\n mt-0.5\n h-4 w-4\n border-[var(--color-border-default)]\n text-action-primary-bg\n focus-visible:ring-2\n focus-visible:ring-ring\n `}\n />\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">{option.label}</span>\n {option.recommended && (\n <span className=\"px-1.5 py-0.5 text-xs font-medium bg-action-primary-bg/10 text-action-primary-bg rounded\">\n {t('bigconsole.dataSinks.cacheTtl.recommendedBadge', 'Recommended')}\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary mt-0.5\">{option.description}</p>\n </div>\n </label>\n ))}\n\n {/* Custom option */}\n <label\n className={`\n flex cursor-pointer items-start gap-3\n rounded-input\n border p-3\n motion-safe:transition-colors\n ${disabled ? 'cursor-not-allowed opacity-50' : ''}\n ${\n isCustom\n ? 'border-[var(--color-action-primary-bg)] bg-bg-accentSubtle'\n : 'border-[var(--color-border-subtle)] hover:border-[var(--color-border-strong)]'\n }\n `}\n >\n <input\n type=\"radio\"\n name=\"cache-ttl\"\n checked={isCustom}\n onChange={handleCustomSelect}\n disabled={disabled}\n className={`\n mt-0.5\n h-4 w-4\n border-[var(--color-border-default)]\n text-action-primary-bg\n focus-visible:ring-2\n focus-visible:ring-ring\n `}\n />\n <div className=\"flex-1\">\n <span className=\"text-sm font-medium text-text-primary\">\n {t('bigconsole.dataSinks.cacheTtl.customLabel', 'Custom')}\n </span>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {t(\n 'bigconsole.dataSinks.cacheTtl.customDescription',\n 'Enter a custom cache duration (30 - 86400 seconds)'\n )}\n </p>\n {isCustom && (\n <>\n <div className=\"mt-3 flex flex-wrap items-center gap-2\">\n <input\n type=\"number\"\n aria-label=\"Custom cache duration in seconds\"\n value={customValue}\n onChange={handleCustomChange}\n disabled={disabled}\n min={30}\n max={86400}\n placeholder=\"300\"\n className={`\n h-control-md w-full min-w-0 rounded-input\n border bg-bg-sunken\n px-3\n text-sm text-text-primary\n focus-visible:outline-none\n focus-visible:ring-2\n focus-visible:ring-ring\n sm:w-32\n ${error || validationError ? 'border-[var(--color-status-error-border)]' : 'border-[var(--color-border-default)]'}\n `}\n />\n <span className=\"text-sm text-text-secondary\">\n {t('bigconsole.dataSinks.cacheTtl.secondsUnit', 'seconds')}\n </span>\n {customValue && !isNaN(parseInt(customValue)) && !validationError && (\n <span className=\"text-xs text-text-muted\">({formatDuration(parseInt(customValue))})</span>\n )}\n </div>\n {validationError && (\n <div className=\"flex items-center gap-1 mt-1\">\n <svg\n className=\"w-3.5 h-3.5 text-status-error-text flex-shrink-0\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <span className=\"text-xs text-status-error-text\">{validationError}</span>\n </div>\n )}\n </>\n )}\n </div>\n </label>\n </div>\n\n {/* Error message */}\n {error && (\n <div className=\"flex items-start gap-2\">\n <svg\n className=\"w-4 h-4 text-status-error-text flex-shrink-0 mt-0.5\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <p className=\"text-xs text-status-error-text\">{error}</p>\n </div>\n )}\n\n {/* Info message */}\n {value !== null && !error && (\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <span>\n {t(\n 'bigconsole.dataSinks.cacheTtl.infoCachedFor',\n 'Data will be cached for {{duration}} before being refreshed',\n {\n duration: formatDuration(value),\n }\n )}\n </span>\n </div>\n )}\n </div>\n );\n});\n\nexport default CacheTTLSelector;\n"],"mappings":"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"}
|
|
@@ -1,129 +1,128 @@
|
|
|
1
|
-
import e from "
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import { SECTION_FOCUS_RING as e } from "../common/sectionStyles.js";
|
|
2
|
+
import "../common/index.js";
|
|
3
|
+
import t from "./DataSinkStatusBadge.js";
|
|
4
|
+
import { memo as n, useCallback as r } from "react";
|
|
5
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
|
+
import { formatDistanceToNow as o } from "date-fns";
|
|
5
7
|
//#region src/bigconsole/components/datasink/DataSinkCard.tsx
|
|
6
|
-
var
|
|
7
|
-
let
|
|
8
|
-
|
|
9
|
-
}, [
|
|
10
|
-
|
|
11
|
-
}, [
|
|
12
|
-
|
|
13
|
-
}, [
|
|
14
|
-
|
|
15
|
-
}, [
|
|
16
|
-
|
|
17
|
-
}, [
|
|
18
|
-
|
|
19
|
-
}, [
|
|
20
|
-
return /* @__PURE__ */
|
|
21
|
-
className: "\n
|
|
22
|
-
onClick:
|
|
8
|
+
var s = `inline-flex min-h-[var(--size-controlHeight-md)] items-center justify-center rounded-button px-3 py-1.5 text-xs font-medium motion-safe:transition-colors ${e}`, c = n(function({ dataSink: e, setupState: n, onView: c, onEdit: l, onRefresh: u, onExport: d, onDelete: f, onContinueSetup: p, canEdit: m = !0, canRefresh: h = !0, canDelete: g = !0 }) {
|
|
9
|
+
let _ = r((t) => {
|
|
10
|
+
t.stopPropagation(), c?.(e.id);
|
|
11
|
+
}, [e.id, c]), v = r((t) => {
|
|
12
|
+
t.stopPropagation(), l?.(e.id);
|
|
13
|
+
}, [e.id, l]), y = r((t) => {
|
|
14
|
+
t.stopPropagation(), u?.(e.id);
|
|
15
|
+
}, [e.id, u]), b = r((t) => {
|
|
16
|
+
t.stopPropagation(), d?.(e.id);
|
|
17
|
+
}, [e.id, d]), x = r((t) => {
|
|
18
|
+
t.stopPropagation(), f?.(e.id);
|
|
19
|
+
}, [e.id, f]), S = r((t) => {
|
|
20
|
+
t.stopPropagation(), p?.(e.id);
|
|
21
|
+
}, [e.id, p]), C = (n ?? e.setupState) === "completed", w = e.lastRefreshedAt ? o(new Date(e.lastRefreshedAt), { addSuffix: !0 }) : "Never";
|
|
22
|
+
return /* @__PURE__ */ a("article", {
|
|
23
|
+
className: "\n min-w-0\n cursor-pointer\n rounded-card\n border border-transparent\n bg-bg-sunken\n p-card-padding\n hover:border-[var(--color-action-primary-bg)]\n motion-safe:transition-colors\n ",
|
|
24
|
+
onClick: _,
|
|
23
25
|
children: [
|
|
24
|
-
/* @__PURE__ */
|
|
25
|
-
className: "flex items-start justify-between
|
|
26
|
-
children: [/* @__PURE__ */
|
|
27
|
-
className: "
|
|
28
|
-
children: [/* @__PURE__ */
|
|
29
|
-
className: "text-sm font-semibold text-text-primary
|
|
30
|
-
children:
|
|
31
|
-
}), /* @__PURE__ */
|
|
32
|
-
className: "text-xs text-text-secondary
|
|
33
|
-
children:
|
|
26
|
+
/* @__PURE__ */ a("div", {
|
|
27
|
+
className: "mb-3 flex items-start justify-between gap-icon-gap",
|
|
28
|
+
children: [/* @__PURE__ */ a("div", {
|
|
29
|
+
className: "min-w-0 flex-1",
|
|
30
|
+
children: [/* @__PURE__ */ i("h3", {
|
|
31
|
+
className: "truncate text-sm font-semibold text-text-primary",
|
|
32
|
+
children: e.name || "Untitled data sink"
|
|
33
|
+
}), /* @__PURE__ */ i("p", {
|
|
34
|
+
className: "mt-0.5 truncate font-mono text-xs text-text-secondary",
|
|
35
|
+
children: e.key
|
|
34
36
|
})]
|
|
35
|
-
}), /* @__PURE__ */
|
|
36
|
-
status:
|
|
37
|
+
}), /* @__PURE__ */ i(t, {
|
|
38
|
+
status: e.status,
|
|
37
39
|
size: "sm"
|
|
38
40
|
})]
|
|
39
41
|
}),
|
|
40
|
-
|
|
41
|
-
className: "text-xs text-text-secondary
|
|
42
|
-
children:
|
|
42
|
+
e.description && /* @__PURE__ */ i("p", {
|
|
43
|
+
className: "mb-3 line-clamp-2 text-xs text-text-secondary",
|
|
44
|
+
children: e.description
|
|
43
45
|
}),
|
|
44
|
-
/* @__PURE__ */
|
|
45
|
-
className: "flex items-center gap-4 text-xs text-text-secondary
|
|
46
|
-
children: [/* @__PURE__ */
|
|
46
|
+
/* @__PURE__ */ a("div", {
|
|
47
|
+
className: "mb-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-text-secondary",
|
|
48
|
+
children: [/* @__PURE__ */ a("span", {
|
|
47
49
|
className: "flex items-center gap-1",
|
|
48
|
-
children: [/* @__PURE__ */
|
|
50
|
+
children: [/* @__PURE__ */ i("svg", {
|
|
49
51
|
className: "w-3.5 h-3.5",
|
|
50
52
|
fill: "none",
|
|
51
53
|
stroke: "currentColor",
|
|
52
54
|
viewBox: "0 0 24 24",
|
|
53
|
-
children: /* @__PURE__ */
|
|
55
|
+
children: /* @__PURE__ */ i("path", {
|
|
54
56
|
strokeLinecap: "round",
|
|
55
57
|
strokeLinejoin: "round",
|
|
56
58
|
strokeWidth: 2,
|
|
57
59
|
d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
58
60
|
})
|
|
59
|
-
}),
|
|
60
|
-
}),
|
|
61
|
+
}), w]
|
|
62
|
+
}), e.linkedWorkflowCount !== void 0 && e.linkedWorkflowCount > 0 && /* @__PURE__ */ a("span", {
|
|
61
63
|
className: "flex items-center gap-1",
|
|
62
64
|
children: [
|
|
63
|
-
/* @__PURE__ */
|
|
65
|
+
/* @__PURE__ */ i("svg", {
|
|
64
66
|
className: "w-3.5 h-3.5",
|
|
65
67
|
fill: "none",
|
|
66
68
|
stroke: "currentColor",
|
|
67
69
|
viewBox: "0 0 24 24",
|
|
68
|
-
children: /* @__PURE__ */
|
|
70
|
+
children: /* @__PURE__ */ i("path", {
|
|
69
71
|
strokeLinecap: "round",
|
|
70
72
|
strokeLinejoin: "round",
|
|
71
73
|
strokeWidth: 2,
|
|
72
74
|
d: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
|
|
73
75
|
})
|
|
74
76
|
}),
|
|
75
|
-
|
|
77
|
+
e.linkedWorkflowCount,
|
|
76
78
|
" workflow",
|
|
77
|
-
|
|
79
|
+
e.linkedWorkflowCount === 1 ? "" : "s"
|
|
78
80
|
]
|
|
79
81
|
})]
|
|
80
82
|
}),
|
|
81
|
-
!
|
|
82
|
-
className: "mb-3
|
|
83
|
-
children: /* @__PURE__ */ i("
|
|
84
|
-
className: "
|
|
85
|
-
children:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
children: "Continue"
|
|
93
|
-
})]
|
|
94
|
-
})
|
|
83
|
+
!C && /* @__PURE__ */ a("div", {
|
|
84
|
+
className: "mb-3 flex items-center justify-between gap-icon-gap rounded-input border border-[var(--color-status-warning-border)] bg-[var(--color-status-warning-bgSubtle)] p-2",
|
|
85
|
+
children: [/* @__PURE__ */ i("span", {
|
|
86
|
+
className: "text-xs text-[var(--color-status-warning-text)]",
|
|
87
|
+
children: "Setup incomplete"
|
|
88
|
+
}), /* @__PURE__ */ i("button", {
|
|
89
|
+
type: "button",
|
|
90
|
+
onClick: S,
|
|
91
|
+
className: "rounded-button px-1 text-xs font-medium text-text-link hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-bg-surface",
|
|
92
|
+
children: "Continue"
|
|
93
|
+
})]
|
|
95
94
|
}),
|
|
96
|
-
/* @__PURE__ */
|
|
97
|
-
className: "flex items-center gap-2
|
|
95
|
+
/* @__PURE__ */ a("div", {
|
|
96
|
+
className: "flex flex-wrap items-center gap-2 border-t border-[var(--color-border-subtle)] pt-2",
|
|
98
97
|
children: [
|
|
99
|
-
/* @__PURE__ */
|
|
98
|
+
/* @__PURE__ */ i("button", {
|
|
100
99
|
type: "button",
|
|
101
|
-
onClick:
|
|
102
|
-
className:
|
|
100
|
+
onClick: _,
|
|
101
|
+
className: `${s} flex-1 bg-bg-surface text-text-primary hover:bg-bg-elevated`,
|
|
103
102
|
children: "View"
|
|
104
103
|
}),
|
|
105
|
-
|
|
104
|
+
m && /* @__PURE__ */ i("button", {
|
|
106
105
|
type: "button",
|
|
107
|
-
onClick:
|
|
108
|
-
className:
|
|
106
|
+
onClick: v,
|
|
107
|
+
className: `${s} text-text-secondary hover:bg-bg-surface hover:text-text-primary`,
|
|
109
108
|
children: "Edit"
|
|
110
109
|
}),
|
|
111
|
-
|
|
110
|
+
h && /* @__PURE__ */ i("button", {
|
|
112
111
|
type: "button",
|
|
113
|
-
onClick:
|
|
114
|
-
className:
|
|
112
|
+
onClick: y,
|
|
113
|
+
className: `${s} text-text-secondary hover:bg-bg-surface hover:text-text-primary`,
|
|
115
114
|
children: "Refresh"
|
|
116
115
|
}),
|
|
117
|
-
|
|
116
|
+
d && /* @__PURE__ */ i("button", {
|
|
118
117
|
type: "button",
|
|
119
|
-
onClick:
|
|
120
|
-
className:
|
|
118
|
+
onClick: b,
|
|
119
|
+
className: `${s} text-text-secondary hover:bg-bg-surface hover:text-text-primary`,
|
|
121
120
|
children: "Export"
|
|
122
121
|
}),
|
|
123
|
-
|
|
122
|
+
g && /* @__PURE__ */ i("button", {
|
|
124
123
|
type: "button",
|
|
125
|
-
onClick:
|
|
126
|
-
className:
|
|
124
|
+
onClick: x,
|
|
125
|
+
className: `${s} text-status-error-text hover:bg-[var(--color-status-error-bgSubtle)]`,
|
|
127
126
|
children: "Delete"
|
|
128
127
|
})
|
|
129
128
|
]
|
|
@@ -132,6 +131,6 @@ var o = t(function({ dataSink: t, setupState: o, onView: s, onEdit: c, onRefresh
|
|
|
132
131
|
});
|
|
133
132
|
});
|
|
134
133
|
//#endregion
|
|
135
|
-
export {
|
|
134
|
+
export { c as default };
|
|
136
135
|
|
|
137
136
|
//# sourceMappingURL=DataSinkCard.js.map
|