@burdenoff/microfe-bigconsole 2026.624.1 → 2026.625.1
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/BigConsoleRoutes.js +94 -94
- package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
- package/dist/bigconsole/assistant/assistantApi.js +215 -0
- package/dist/bigconsole/assistant/assistantApi.js.map +1 -0
- package/dist/bigconsole/assistant/createSandboxAssistantTransport.js +119 -0
- package/dist/bigconsole/assistant/createSandboxAssistantTransport.js.map +1 -0
- package/dist/bigconsole/assistant/index.js +2 -0
- package/dist/bigconsole/assistant/pageContext.js +51 -0
- package/dist/bigconsole/assistant/pageContext.js.map +1 -0
- package/dist/bigconsole/components/DrilldownModal.js +4 -4
- package/dist/bigconsole/components/DrilldownModal.js.map +1 -1
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +48 -48
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js +1 -1
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js +54 -54
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +1 -1
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js +33 -33
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +205 -205
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/WidgetPalette.js +38 -38
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js +1 -1
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkCard.js +4 -4
- package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkDataViewer.js +2 -2
- package/dist/bigconsole/components/datasink/DataSinkDataViewer.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +2 -2
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +2 -2
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkTableViewer.js +34 -34
- package/dist/bigconsole/components/datasink/DataSinkTableViewer.js.map +1 -1
- package/dist/bigconsole/components/datasink/ExportDataSinkDialog.js +31 -31
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +121 -121
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/LinkedParsersPanel.js +1 -1
- package/dist/bigconsole/components/datasink/LinkedParsersPanel.js.map +1 -1
- package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js +3 -3
- package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js.map +1 -1
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +2 -2
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js.map +1 -1
- package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js +2 -2
- package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js.map +1 -1
- package/dist/bigconsole/components/parser/DataSinkSelector.js +3 -3
- package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ExportParserDialog.js +31 -31
- package/dist/bigconsole/components/parser/ImportParserDialog.js +186 -186
- package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserCard.js +5 -5
- package/dist/bigconsole/components/parser/ParserCard.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserSelector.js +6 -6
- package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserStatusBadge.js +1 -1
- package/dist/bigconsole/components/parser/ParserStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserTypeBadge.js +1 -1
- package/dist/bigconsole/components/parser/ParserTypeBadge.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineCard.js +2 -2
- package/dist/bigconsole/components/pipeline/PipelineCard.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js +1 -1
- package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js +1 -1
- package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js +79 -79
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js.map +1 -1
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +11 -11
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +322 -322
- package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetConfigDialog.js +234 -234
- package/dist/bigconsole/components/widgets/WidgetConfigDialog.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetHeader.js +4 -4
- package/dist/bigconsole/components/widgets/WidgetHeader.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetMenu.js +3 -3
- package/dist/bigconsole/components/widgets/WidgetMenu.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetTypeSelector.js +23 -23
- package/dist/bigconsole/components/widgets/WidgetWrapper.js +1 -1
- package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +1 -1
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js +2 -2
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js.map +1 -1
- package/dist/bigconsole/components/widgets/custom/CustomWidget.js +5 -5
- package/dist/bigconsole/components/widgets/custom/CustomWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/LinearGauge.js +1 -1
- package/dist/bigconsole/components/widgets/gauge/LinearGauge.js.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +2 -2
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kpi/KPIWidget.js +2 -2
- package/dist/bigconsole/components/widgets/kpi/KPIWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js +2 -2
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/list/ListItem.js +3 -3
- package/dist/bigconsole/components/widgets/list/ListItem.js.map +1 -1
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js +3 -3
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/ComparisonBadge.js +1 -1
- package/dist/bigconsole/components/widgets/metric-card/ComparisonBadge.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +2 -2
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +12 -12
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressBar.js +2 -2
- package/dist/bigconsole/components/widgets/progress/ProgressBar.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressWidget.js +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/retention/RetentionWidget.js +2 -2
- package/dist/bigconsole/components/widgets/retention/RetentionWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/states/EmptyState.js +1 -1
- package/dist/bigconsole/components/widgets/states/EmptyState.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableCell.js +1 -1
- package/dist/bigconsole/components/widgets/table/TableCell.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableConfig.js +1 -1
- package/dist/bigconsole/components/widgets/table/TableConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableHeader.js +2 -2
- package/dist/bigconsole/components/widgets/table/TableHeader.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TablePagination.js +4 -4
- package/dist/bigconsole/components/widgets/table/TablePagination.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableWidget.js +5 -5
- 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/widget-actions/WidgetActions.js +3 -3
- package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowCard.js +2 -2
- package/dist/bigconsole/components/workflow/WorkflowCard.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js +1 -1
- package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js.map +1 -1
- package/dist/bigconsole/index.js +3 -0
- package/dist/bigconsole/pages/DashboardBuilderPage.js +76 -76
- package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardViewPage.js +169 -190
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +268 -312
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/DataParsersPage.js +148 -194
- package/dist/bigconsole/pages/DataParsersPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkBuilderPage.js +254 -248
- package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +256 -231
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinksPage.js +173 -219
- package/dist/bigconsole/pages/DataSinksPage.js.map +1 -1
- package/dist/bigconsole/pages/HomePage.js +125 -141
- package/dist/bigconsole/pages/HomePage.js.map +1 -1
- package/dist/bigconsole/pages/InsightsPage.js +159 -184
- package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserBuilderPage.js +404 -396
- package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserViewPage.js +221 -202
- package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineBuilderPage.js +216 -210
- package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineViewPage.js +213 -190
- package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelinesPage.js +107 -150
- package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
- package/dist/bigconsole/pages/SettingsPage.js +184 -171
- package/dist/bigconsole/pages/SettingsPage.js.map +1 -1
- package/dist/bigconsole/pages/WidgetDemoPage.js +71 -83
- package/dist/bigconsole/pages/WidgetDemoPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowViewPage.js +235 -221
- package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowsPage.js +106 -149
- package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +12 -12
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/BigConsoleRenderer.js +2 -2
- package/dist/bigconsole/renderers/adapters/BigConsoleRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +5 -5
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/CustomRenderer.js +2 -2
- package/dist/bigconsole/renderers/adapters/CustomRenderer.js.map +1 -1
- package/dist/generated/wspace-operations.js +87 -36
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/index.js +3 -1
- package/package.json +6 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetConfigDialog.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetConfigDialog.tsx"],"sourcesContent":["/**\n * WidgetConfigDialog Component (v2.0)\n *\n * Modal dialog for full widget configuration.\n * v2.0: Added renderer selection and template editor support.\n * Uses fe-libs shared components for consistent styling.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect, useMemo } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Textarea } from '@burdenoff/fe-libs/ui';\nimport { Label } from '@burdenoff/fe-libs/ui';\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@burdenoff/fe-libs/ui';\nimport { Card, CardDescription, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { Layout, CreditCard, MessageSquare, Code, ExternalLink } from 'lucide-react';\nimport type { Widget, WidgetType, RendererType, DataSourceType } from '../../types';\nimport { getWidgetDefinition, type WidgetDefinition } from './WidgetRegistry';\nimport { WidgetTypeSelector } from './WidgetTypeSelector';\n\n// Config components\nimport { MetricCardConfig } from './metric-card/MetricCardConfig';\nimport { ChartConfig } from './chart/ChartConfig';\nimport { TableConfig } from './table/TableConfig';\nimport { GaugeConfig } from './gauge/GaugeConfig';\nimport { ProgressConfig } from './progress/ProgressConfig';\nimport { ListConfig } from './list/ListConfig';\n\n// DataSink and Parser selectors\nimport { DataSinkSelector } from '../parser/DataSinkSelector';\nimport { ParserSelector } from '../parser/ParserSelector';\n\n// Store for DataSink lookup\nimport { useDataSinkStore } from '../../store';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetConfigDialogProps {\n /** Whether dialog is open */\n isOpen: boolean;\n /** Widget to edit (undefined for new widget) */\n widget?: Widget;\n /** Pre-selected widget type (for drag & drop from palette) */\n preselectedType?: WidgetType;\n /** Pre-selected widget definition from drag & drop (preserves installed/store widget metadata) */\n preselectedDefinition?: WidgetDefinition;\n /** Callback when dialog is closed */\n onClose: () => void;\n /** Callback when widget is saved */\n onSave: (widget: Partial<Widget>) => void;\n /** Dashboard ID for fetching parsers */\n dashboardId?: string;\n /** Installed widgets from the store (fetched by parent) */\n installedWidgets?: WidgetDefinition[];\n}\n\ntype Step = 'type' | 'general' | 'renderer' | 'config';\n\n// ============================================================================\n// Renderer Definitions\n// ============================================================================\n\ninterface RendererOption {\n type: RendererType;\n name: string;\n description: string;\n icon: typeof Layout;\n supportedTypes: WidgetType[];\n}\n\nconst ALL_WIDGET_TYPES: WidgetType[] = [\n 'kpi_card_comparison',\n 'metric_card',\n 'chart',\n 'funnel_chart',\n 'table',\n 'pivot_table',\n 'gauge',\n 'progress',\n 'list',\n 'form',\n 'text',\n 'iframe',\n 'map',\n 'heatmap',\n 'calendar',\n 'kanban',\n 'timeline',\n 'custom',\n];\n\nconst RENDERER_OPTIONS: RendererOption[] = [\n {\n type: 'BIGCONSOLE',\n name: 'BigConsole Native',\n description: 'Full-featured native widgets with all configuration options',\n icon: Layout,\n supportedTypes: ALL_WIDGET_TYPES,\n },\n {\n type: 'ADAPTIVE_CARD',\n name: 'Microsoft Adaptive Cards',\n description: 'Use Microsoft Adaptive Cards format for Teams/Outlook integration',\n icon: CreditCard,\n supportedTypes: ALL_WIDGET_TYPES,\n },\n {\n type: 'BLOCKKIT',\n name: 'Slack Block Kit',\n description: 'Use Slack Block Kit format for Slack integration',\n icon: MessageSquare,\n supportedTypes: ALL_WIDGET_TYPES,\n },\n {\n type: 'CUSTOM',\n name: 'Custom Component',\n description: 'Use a custom React component registered in the frontend',\n icon: Code,\n supportedTypes: ALL_WIDGET_TYPES,\n },\n];\n\n// ============================================================================\n// Template Presets\n// ============================================================================\n\ninterface TemplatePreset {\n id: string;\n name: string;\n description: string;\n renderer: RendererType;\n template: Record<string, unknown>;\n}\n\nconst TEMPLATE_PRESETS: TemplatePreset[] = [\n {\n id: 'ac-simple-text',\n name: 'Simple Text Card',\n description: 'Basic text display',\n renderer: 'ADAPTIVE_CARD',\n template: {\n type: 'AdaptiveCard',\n version: '1.4',\n body: [{ type: 'TextBlock', text: 'Hello World!', size: 'Large', weight: 'Bolder' }],\n },\n },\n {\n id: 'ac-metric-card',\n name: 'Metric Display',\n description: 'Show a key metric with comparison',\n renderer: 'ADAPTIVE_CARD',\n template: {\n type: 'AdaptiveCard',\n version: '1.4',\n body: [\n { type: 'TextBlock', text: '${title}', size: 'Medium', weight: 'Bolder' },\n { type: 'TextBlock', text: '${value}', size: 'ExtraLarge', weight: 'Bolder', color: 'Accent' },\n { type: 'TextBlock', text: '${change} from last period', size: 'Small', color: 'Good' },\n ],\n },\n },\n {\n id: 'bk-status-update',\n name: 'Status Update',\n description: 'System status display',\n renderer: 'BLOCKKIT',\n template: {\n blocks: [\n { type: 'header', text: { type: 'plain_text', text: 'System Status' } },\n { type: 'section', text: { type: 'mrkdwn', text: '*Status:* All systems operational' } },\n { type: 'divider' },\n {\n type: 'section',\n fields: [\n { type: 'mrkdwn', text: '*API:* Online' },\n { type: 'mrkdwn', text: '*Database:* Online' },\n ],\n },\n ],\n },\n },\n {\n id: 'custom-placeholder',\n name: 'Custom Component',\n description: 'Placeholder for custom React component',\n renderer: 'CUSTOM',\n template: { componentName: 'MyCustomWidget', props: { color: 'blue', size: 'medium' } },\n },\n];\n\n// ============================================================================\n// Config Component Map\n// ============================================================================\n\nfunction getConfigComponent(type: WidgetType) {\n switch (type) {\n case 'metric_card':\n case 'kpi_card_comparison':\n return MetricCardConfig;\n case 'chart':\n case 'funnel_chart':\n return ChartConfig;\n case 'table':\n case 'pivot_table':\n return TableConfig;\n case 'gauge':\n return GaugeConfig;\n case 'progress':\n return ProgressConfig;\n case 'list':\n return ListConfig;\n default:\n return null;\n }\n}\n\n// ============================================================================\n// Template Editor Component\n// ============================================================================\n\ninterface TemplateEditorProps {\n renderer: RendererType;\n template: Record<string, unknown> | null;\n onChange: (template: Record<string, unknown>) => void;\n}\n\nconst TemplateEditor: FC<TemplateEditorProps> = memo(function TemplateEditor({ renderer, template, onChange }) {\n const [jsonError, setJsonError] = useState<string | null>(null);\n const [jsonText, setJsonText] = useState(() => {\n try {\n return template && typeof template === 'object' ? JSON.stringify(template, null, 2) : '';\n } catch {\n return '';\n }\n });\n\n const presets = useMemo(() => TEMPLATE_PRESETS.filter((p) => p.renderer === renderer), [renderer]);\n\n const handleJsonChange = useCallback(\n (text: string) => {\n setJsonText(text);\n try {\n if (text.trim()) {\n const parsed = JSON.parse(text);\n onChange(parsed);\n setJsonError(null);\n } else {\n setJsonError(null);\n }\n } catch (e) {\n setJsonError((e as Error).message);\n }\n },\n [onChange]\n );\n\n const handlePresetSelect = useCallback(\n (preset: TemplatePreset) => {\n const text = JSON.stringify(preset.template, null, 2);\n setJsonText(text);\n onChange(preset.template);\n setJsonError(null);\n },\n [onChange]\n );\n\n useEffect(() => {\n if (template && typeof template === 'object') {\n try {\n const newText = JSON.stringify(template, null, 2);\n setJsonText(newText);\n setJsonError(null);\n } catch (e) {}\n } else if (!template) {\n setJsonText('');\n }\n }, [template]);\n\n return (\n <div className=\"space-y-4\">\n {presets.length > 0 && (\n <div>\n <Label className=\"mb-2\">Quick Start Templates</Label>\n <div className=\"grid grid-cols-2 gap-2\">\n {presets.map((preset) => (\n <Card\n key={preset.id}\n className=\"cursor-pointer hover:border-primary transition-colors\"\n onClick={() => handlePresetSelect(preset)}\n >\n <CardHeader className=\"p-3\">\n <CardTitle className=\"text-sm\">{preset.name}</CardTitle>\n <CardDescription className=\"text-xs\">{preset.description}</CardDescription>\n </CardHeader>\n </Card>\n ))}\n </div>\n </div>\n )}\n\n <div>\n <Label className=\"mb-2\">Template JSON</Label>\n <Textarea\n value={jsonText}\n onChange={(e) => handleJsonChange(e.target.value)}\n placeholder={`Enter ${renderer} template JSON...`}\n rows={12}\n spellCheck={false}\n className={`font-mono text-sm ${jsonError ? 'border-destructive focus-visible:ring-destructive' : ''}`}\n />\n {jsonError && <p className=\"mt-1 text-xs text-destructive\">Invalid JSON: {jsonError}</p>}\n </div>\n\n <div className=\"flex items-center justify-between rounded-lg bg-muted p-3\">\n <p className=\"text-xs text-muted-foreground\">\n {renderer === 'ADAPTIVE_CARD' && 'Use Microsoft Adaptive Cards format with data binding.'}\n {renderer === 'BLOCKKIT' && 'Use Slack Block Kit format for Slack integration.'}\n {renderer === 'CUSTOM' && 'Specify componentName and props for your custom React component.'}\n </p>\n {renderer === 'ADAPTIVE_CARD' && (\n <Button variant=\"ghost\" size=\"sm\" asChild>\n <a href=\"https://adaptivecards.io/designer/\" target=\"_blank\" rel=\"noopener noreferrer\">\n <ExternalLink className=\"size-3.5\" />\n Designer\n </a>\n </Button>\n )}\n {renderer === 'BLOCKKIT' && (\n <Button variant=\"ghost\" size=\"sm\" asChild>\n <a href=\"https://app.slack.com/block-kit-builder\" target=\"_blank\" rel=\"noopener noreferrer\">\n <ExternalLink className=\"size-3.5\" />\n Builder\n </a>\n </Button>\n )}\n </div>\n </div>\n );\n});\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetConfigDialog: FC<WidgetConfigDialogProps> = memo(function WidgetConfigDialog({\n isOpen,\n widget,\n preselectedType,\n preselectedDefinition,\n onClose,\n onSave,\n dashboardId,\n installedWidgets,\n}) {\n const isNew = !widget;\n // If preselectedType is provided, skip the type selection step\n const [step, setStep] = useState<Step>(isNew ? (preselectedType ? 'general' : 'type') : 'general');\n\n // Access DataSink store to look up ID from key\n const dataSinks = useDataSinkStore((state) => state.dataSinks);\n\n // Form state - use preselectedType if provided\n const [selectedType, setSelectedType] = useState<WidgetType | undefined>(widget?.type || preselectedType);\n const [title, setTitle] = useState(widget?.title || '');\n const [description, setDescription] = useState(widget?.description || '');\n const [refreshInterval, setRefreshInterval] = useState<number | undefined>(widget?.refreshInterval ?? undefined);\n const [config, setConfig] = useState<Record<string, unknown>>((widget?.config as Record<string, unknown>) || {});\n const [selectedRenderer, setSelectedRenderer] = useState<RendererType>(widget?.renderer || 'BIGCONSOLE');\n const [template, setTemplate] = useState<Record<string, unknown> | null>(widget?.template || null);\n\n // Data Source state\n const [dataSourceType, setDataSourceType] = useState<DataSourceType>(\n ((widget?.config as Record<string, unknown>)?.dataSourceType as DataSourceType) || 'MOCK'\n );\n const [dataSinkKey, setDataSinkKey] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.dataSinkKey as string) || ''\n );\n const [parserId, setParserId] = useState<string>((widget as unknown as Record<string, string>)?.parserId || '');\n\n // REST API config state\n const [endpointUrl, setEndpointUrl] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.endpointUrl as string) || ''\n );\n const [httpMethod, setHttpMethod] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.httpMethod as string) || 'GET'\n );\n const [headers, setHeaders] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.headers as string) || ''\n );\n const [authType, setAuthType] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.authType as string) || 'none'\n );\n const [authValue, setAuthValue] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.authValue as string) || ''\n );\n const [requestBody, setRequestBody] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.requestBody as string) || ''\n );\n const [dataPath, setDataPath] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.dataPath as string) || ''\n );\n\n // GraphQL config state\n const [graphqlEndpoint, setGraphqlEndpoint] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.graphqlEndpoint as string) || ''\n );\n const [graphqlQuery, setGraphqlQuery] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.graphqlQuery as string) || ''\n );\n const [graphqlVariables, setGraphqlVariables] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.graphqlVariables as string) || ''\n );\n\n const availableRenderers = useMemo(() => {\n if (!selectedType) return RENDERER_OPTIONS;\n return RENDERER_OPTIONS.filter((r) => r.supportedTypes.includes(selectedType));\n }, [selectedType]);\n\n useEffect(() => {\n if (selectedType && !availableRenderers.find((r) => r.type === selectedRenderer)) {\n setSelectedRenderer('BIGCONSOLE');\n setTemplate(null);\n }\n }, [selectedType, availableRenderers, selectedRenderer]);\n\n useEffect(() => {\n if (widget) {\n setSelectedType(widget.type);\n setTitle(widget.title);\n setDescription(widget.description || '');\n setRefreshInterval(widget.refreshInterval ?? undefined);\n setConfig((widget.config as Record<string, unknown>) || {});\n setSelectedRenderer(widget.renderer || 'BIGCONSOLE');\n setTemplate(widget.template || null);\n // Load data source settings\n const widgetConfig = (widget.config as Record<string, unknown>) || {};\n setDataSourceType((widgetConfig.dataSourceType as DataSourceType) || 'MOCK');\n setDataSinkKey((widgetConfig.dataSinkKey as string) || '');\n setParserId((widget as unknown as Record<string, string>).parserId || '');\n // Load REST API config\n setEndpointUrl((widgetConfig.endpointUrl as string) || '');\n setHttpMethod((widgetConfig.httpMethod as string) || 'GET');\n setHeaders((widgetConfig.headers as string) || '');\n setAuthType((widgetConfig.authType as string) || 'none');\n setAuthValue((widgetConfig.authValue as string) || '');\n setRequestBody((widgetConfig.requestBody as string) || '');\n setDataPath((widgetConfig.dataPath as string) || '');\n // Load GraphQL config\n setGraphqlEndpoint((widgetConfig.graphqlEndpoint as string) || '');\n setGraphqlQuery((widgetConfig.graphqlQuery as string) || '');\n setGraphqlVariables((widgetConfig.graphqlVariables as string) || '');\n setStep('general');\n } else if (preselectedType) {\n // Handle preselected type from drag & drop.\n // Use preselectedDefinition (if provided) to preserve installed/store widget metadata\n // instead of looking up the generic 'custom' definition from the registry.\n const definition = preselectedDefinition || getWidgetDefinition(preselectedType);\n setSelectedType(preselectedType);\n setTitle(definition.name);\n setDescription('');\n setRefreshInterval(undefined);\n setConfig(definition.defaultConfig || {});\n // Installed/store widgets use CUSTOM renderer; built-in widgets use BIGCONSOLE.\n setSelectedRenderer(definition.category === 'Installed' ? 'CUSTOM' : 'BIGCONSOLE');\n setTemplate(null);\n setDataSourceType('MOCK');\n setDataSinkKey('');\n setParserId('');\n // Reset REST API config\n setEndpointUrl('');\n setHttpMethod('GET');\n setHeaders('');\n setAuthType('none');\n setAuthValue('');\n setRequestBody('');\n setDataPath('');\n // Reset GraphQL config\n setGraphqlEndpoint('');\n setGraphqlQuery('');\n setGraphqlVariables('');\n // Skip type selection, go to general\n setStep('general');\n } else {\n setSelectedType(undefined);\n setTitle('');\n setDescription('');\n setRefreshInterval(undefined);\n setConfig({});\n setSelectedRenderer('BIGCONSOLE');\n setTemplate(null);\n setDataSourceType('MOCK');\n setDataSinkKey('');\n setParserId('');\n // Reset REST API config\n setEndpointUrl('');\n setHttpMethod('GET');\n setHeaders('');\n setAuthType('none');\n setAuthValue('');\n setRequestBody('');\n setDataPath('');\n // Reset GraphQL config\n setGraphqlEndpoint('');\n setGraphqlQuery('');\n setGraphqlVariables('');\n setStep('type');\n }\n }, [widget, preselectedType, preselectedDefinition, isOpen]);\n\n const handleTypeSelect = useCallback((type: WidgetType, installedDefinition?: WidgetDefinition) => {\n setSelectedType(type);\n const definition = installedDefinition || getWidgetDefinition(type);\n if (definition) {\n setTitle(definition.name);\n setConfig(definition.defaultConfig || {});\n // Installed/store widgets must use the CUSTOM renderer so the store\n // component is resolved via WidgetRendererFactory instead of the\n // native BigConsole path.\n if (definition.category === 'Installed') {\n setSelectedRenderer('CUSTOM');\n }\n }\n }, []);\n\n const handleRendererSelect = useCallback((renderer: RendererType) => {\n setSelectedRenderer(renderer);\n if (renderer === 'BIGCONSOLE') {\n setTemplate(null);\n } else {\n const preset = TEMPLATE_PRESETS.find((p) => p.renderer === renderer);\n setTemplate(preset ? preset.template : {});\n }\n }, []);\n\n const handleSave = useCallback(() => {\n if (!selectedType) return;\n\n // Look up the actual DataSink ID (UUID) from the key\n // dataSinks is a Map, so we need to iterate to find by key\n let actualDataSinkId: string | undefined;\n if (dataSinkKey) {\n for (const ds of dataSinks.values()) {\n if (ds.key === dataSinkKey) {\n actualDataSinkId = ds.id;\n break;\n }\n }\n }\n\n const widgetData: Partial<Widget> & { dataSinkId?: string; parserId?: string } = {\n type: selectedType,\n title,\n description: description || undefined,\n refreshInterval,\n config: {\n ...config,\n dataSourceType,\n dataSinkKey: dataSinkKey || undefined,\n // REST API config\n ...(dataSourceType === 'REST_API'\n ? {\n endpointUrl: endpointUrl || undefined,\n httpMethod: httpMethod || 'GET',\n headers: headers || undefined,\n authType: authType || 'none',\n authValue: authValue || undefined,\n requestBody: requestBody || undefined,\n dataPath: dataPath || undefined,\n }\n : {}),\n // GraphQL config\n ...(dataSourceType === 'GRAPHQL'\n ? {\n graphqlEndpoint: graphqlEndpoint || undefined,\n graphqlQuery: graphqlQuery || undefined,\n graphqlVariables: graphqlVariables || undefined,\n headers: headers || undefined,\n dataPath: dataPath || undefined,\n }\n : {}),\n },\n renderer: selectedRenderer,\n template: selectedRenderer !== 'BIGCONSOLE' ? template : undefined,\n // v2.0: Data source fields at top level for API (DataSink → Parser → Widget)\n // Use actual dataSink ID (UUID) looked up from the key\n dataSinkId: actualDataSinkId || undefined,\n parserId: parserId || undefined,\n };\n onSave(widgetData);\n onClose();\n }, [\n selectedType,\n title,\n description,\n refreshInterval,\n config,\n selectedRenderer,\n template,\n dataSourceType,\n dataSinkKey,\n dataSinks,\n parserId,\n endpointUrl,\n httpMethod,\n headers,\n authType,\n authValue,\n requestBody,\n dataPath,\n graphqlEndpoint,\n graphqlQuery,\n graphqlVariables,\n onSave,\n onClose,\n ]);\n\n const definition = selectedType ? getWidgetDefinition(selectedType) : null;\n const ConfigComponent = selectedType ? getConfigComponent(selectedType) : null;\n const showRendererStep = selectedRenderer !== 'BIGCONSOLE';\n // Always show config step - it has Data Source settings needed for all renderers\n const showConfigStep = true;\n const hasWidgetSpecificConfig = selectedRenderer === 'BIGCONSOLE' && ConfigComponent;\n\n const steps = useMemo(() => {\n const allSteps = [\n { id: 'type' as Step, label: 'Type', show: isNew },\n { id: 'general' as Step, label: 'General', show: true },\n { id: 'renderer' as Step, label: 'Template', show: showRendererStep },\n { id: 'config' as Step, label: 'Data Source', show: showConfigStep },\n ];\n return allSteps.filter((s) => s.show);\n }, [isNew, showRendererStep, showConfigStep]);\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent\n style={{\n maxWidth: '520px',\n width: '95vw',\n maxHeight: '85vh',\n display: 'flex',\n flexDirection: 'column',\n padding: 0,\n overflow: 'hidden',\n }}\n >\n <DialogHeader style={{ padding: '16px 16px 8px 16px', flexShrink: 0 }}>\n <DialogTitle style={{ fontSize: '16px' }}>{isNew ? 'Add Widget' : 'Edit Widget'}</DialogTitle>\n {/* Always render DialogDescription (Radix requires it for a11y).\n When no definition is resolved yet, fall back to a generic label. */}\n <DialogDescription className=\"text-xs\">\n {definition ? (\n <>\n {definition.name}\n {selectedRenderer !== 'BIGCONSOLE' && (\n <span className=\"ml-2 px-1.5 py-0.5 text-xs bg-primary/10 text-primary rounded\">\n {RENDERER_OPTIONS.find((r) => r.type === selectedRenderer)?.name}\n </span>\n )}\n </>\n ) : (\n 'Configure the widget data source, appearance, and actions.'\n )}\n </DialogDescription>\n </DialogHeader>\n\n {/* Steps */}\n <Tabs\n value={step}\n onValueChange={(v) => setStep(v as Step)}\n style={{ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0, overflow: 'hidden' }}\n >\n <TabsList\n style={{ margin: '0 16px', width: 'auto', justifyContent: 'flex-start', height: '32px', flexShrink: 0 }}\n >\n {steps.map((s) => (\n <TabsTrigger\n key={s.id}\n value={s.id}\n disabled={s.id !== 'type' && !selectedType}\n style={{ fontSize: '12px', padding: '4px 12px' }}\n >\n {s.label}\n </TabsTrigger>\n ))}\n </TabsList>\n\n <div\n style={{\n flex: 1,\n minHeight: 0,\n marginTop: '8px',\n overflow: 'auto',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <TabsContent value=\"type\" style={{ marginTop: 0, height: '100%', overflow: 'hidden' }}>\n <WidgetTypeSelector\n selectedType={selectedType}\n selectedRenderer={selectedRenderer}\n onSelect={(type, definition) => {\n handleTypeSelect(type, definition);\n setStep('general');\n }}\n onRendererSelect={handleRendererSelect}\n installedWidgets={installedWidgets}\n />\n </TabsContent>\n\n <TabsContent\n value=\"general\"\n style={{\n marginTop: 0,\n flex: 1,\n overflowY: 'auto',\n padding: '12px 16px',\n minHeight: '300px',\n }}\n >\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"title\">Title</Label>\n <Input\n id=\"title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"Widget title\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"description\">Description (optional)</Label>\n <Textarea\n id=\"description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Brief description of this widget\"\n rows={2}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Renderer</Label>\n <div className=\"grid grid-cols-2 gap-2\">\n {availableRenderers.map((renderer) => {\n const Icon = renderer.icon;\n return (\n <Card\n key={renderer.type}\n className={`cursor-pointer transition-colors ${\n selectedRenderer === renderer.type\n ? 'border-primary bg-primary/5'\n : 'hover:border-primary/50'\n }`}\n onClick={() => handleRendererSelect(renderer.type)}\n >\n <CardHeader className=\"p-3\">\n <div className=\"flex items-start gap-3\">\n <div\n className={`p-2 rounded-md ${\n selectedRenderer === renderer.type ? 'bg-primary text-primary-foreground' : 'bg-muted'\n }`}\n >\n <Icon className=\"size-4\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <CardTitle className=\"text-sm\">{renderer.name}</CardTitle>\n <CardDescription className=\"text-xs line-clamp-2\">\n {renderer.description}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n </Card>\n );\n })}\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"refresh\">Auto-Refresh Interval (seconds)</Label>\n <Input\n id=\"refresh\"\n type=\"number\"\n value={refreshInterval || ''}\n onChange={(e) => setRefreshInterval(e.target.value ? Number(e.target.value) : undefined)}\n min={0}\n placeholder=\"No auto-refresh\"\n />\n </div>\n </div>\n </TabsContent>\n\n <TabsContent\n value=\"renderer\"\n style={{\n marginTop: 0,\n flex: 1,\n overflowY: 'auto',\n padding: '12px 16px',\n minHeight: '300px',\n }}\n >\n <TemplateEditor renderer={selectedRenderer} template={template} onChange={setTemplate} />\n </TabsContent>\n\n <TabsContent\n value=\"config\"\n style={{\n marginTop: 0,\n flex: 1,\n overflowY: 'auto',\n padding: '12px 16px',\n minHeight: '300px',\n }}\n >\n <div className=\"space-y-4\">\n {/* Data Source Section */}\n <div className=\"space-y-3 p-3 border border-border rounded-lg bg-muted/30\">\n <h4 className=\"text-sm font-medium\">Data Source</h4>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"dataSourceType\">Source Type</Label>\n <select\n id=\"dataSourceType\"\n value={dataSourceType}\n onChange={(e) => setDataSourceType(e.target.value as DataSourceType)}\n className=\"w-full h-9 px-3 rounded-md border border-input bg-background text-sm\"\n >\n <option value=\"MOCK\">Sample JSON</option>\n <option value=\"DATASINK\">DataSink</option>\n <option value=\"REST_API\">REST API</option>\n <option value=\"GRAPHQL\">GraphQL</option>\n </select>\n </div>\n\n {dataSourceType === 'DATASINK' && (\n <div className=\"space-y-2\">\n <Label htmlFor=\"dataSinkKey\">DataSink</Label>\n <DataSinkSelector\n value={dataSinkKey || null}\n onChange={(key) => setDataSinkKey(key || '')}\n placeholder=\"Select a DataSink...\"\n />\n </div>\n )}\n\n {/* REST API Configuration */}\n {dataSourceType === 'REST_API' && (\n <div className=\"space-y-3\">\n <div className=\"space-y-2\">\n <Label>Endpoint URL *</Label>\n <Input\n value={endpointUrl}\n onChange={(e) => setEndpointUrl(e.target.value)}\n placeholder=\"https://api.example.com/data\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>HTTP Method</Label>\n <select\n value={httpMethod}\n onChange={(e) => setHttpMethod(e.target.value)}\n className=\"w-full h-9 px-3 rounded-md border border-input bg-background text-sm\"\n >\n <option value=\"GET\">GET</option>\n <option value=\"POST\">POST</option>\n <option value=\"PUT\">PUT</option>\n <option value=\"DELETE\">DELETE</option>\n </select>\n </div>\n\n <div className=\"space-y-2\">\n <Label>Headers (JSON)</Label>\n <Textarea\n value={headers}\n onChange={(e) => setHeaders(e.target.value)}\n rows={2}\n placeholder='{\"Content-Type\": \"application/json\"}'\n className=\"font-mono text-xs\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Authentication</Label>\n <select\n value={authType}\n onChange={(e) => setAuthType(e.target.value)}\n className=\"w-full h-9 px-3 rounded-md border border-input bg-background text-sm\"\n >\n <option value=\"none\">None</option>\n <option value=\"api_key\">API Key</option>\n <option value=\"bearer\">Bearer Token</option>\n <option value=\"basic\">Basic Auth</option>\n </select>\n </div>\n\n {authType !== 'none' && (\n <div className=\"space-y-2\">\n <Label>{authType === 'basic' ? 'Credentials (user:pass)' : 'Token/Key'}</Label>\n <Input\n type=\"password\"\n value={authValue}\n onChange={(e) => setAuthValue(e.target.value)}\n placeholder={authType === 'basic' ? 'username:password' : 'Enter token or API key'}\n />\n </div>\n )}\n\n {(httpMethod === 'POST' || httpMethod === 'PUT') && (\n <div className=\"space-y-2\">\n <Label>Request Body (JSON)</Label>\n <Textarea\n value={requestBody}\n onChange={(e) => setRequestBody(e.target.value)}\n rows={3}\n placeholder='{\"key\": \"value\"}'\n className=\"font-mono text-xs\"\n />\n </div>\n )}\n\n <div className=\"space-y-2\">\n <Label>Data Path (optional)</Label>\n <Input\n value={dataPath}\n onChange={(e) => setDataPath(e.target.value)}\n placeholder=\"$.data.items or .data.items\"\n />\n <p className=\"text-xs text-muted-foreground\">Extract data from response using JSONPath</p>\n </div>\n </div>\n )}\n\n {/* GraphQL Configuration */}\n {dataSourceType === 'GRAPHQL' && (\n <div className=\"space-y-3\">\n <div className=\"space-y-2\">\n <Label>GraphQL Endpoint *</Label>\n <Input\n value={graphqlEndpoint}\n onChange={(e) => setGraphqlEndpoint(e.target.value)}\n placeholder=\"https://api.example.com/graphql\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Query *</Label>\n <Textarea\n value={graphqlQuery}\n onChange={(e) => setGraphqlQuery(e.target.value)}\n rows={5}\n placeholder={`query GetData($limit: Int) {\n items(limit: $limit) {\n id\n name\n }\n}`}\n className=\"font-mono text-xs\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Variables (JSON, optional)</Label>\n <Textarea\n value={graphqlVariables}\n onChange={(e) => setGraphqlVariables(e.target.value)}\n rows={2}\n placeholder='{\"limit\": 10}'\n className=\"font-mono text-xs\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Headers (JSON, optional)</Label>\n <Textarea\n value={headers}\n onChange={(e) => setHeaders(e.target.value)}\n rows={2}\n placeholder='{\"Authorization\": \"Bearer token\"}'\n className=\"font-mono text-xs\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Data Path (optional)</Label>\n <Input\n value={dataPath}\n onChange={(e) => setDataPath(e.target.value)}\n placeholder=\".data.items\"\n />\n <p className=\"text-xs text-muted-foreground\">Extract data from GraphQL response</p>\n </div>\n </div>\n )}\n\n {dashboardId && (\n <div className=\"space-y-2\">\n <Label htmlFor=\"parserId\">Parser (optional)</Label>\n <ParserSelector\n consoleId={dashboardId}\n value={parserId || null}\n onChange={(id) => setParserId(id || '')}\n filterInputSinkKey={dataSourceType === 'DATASINK' ? dataSinkKey : undefined}\n filterStatus={undefined}\n placeholder=\"Select a parser...\"\n />\n <p className=\"text-xs text-muted-foreground\">Transform data using a parser before display</p>\n </div>\n )}\n </div>\n\n {/* Widget-specific config (only for BIGCONSOLE renderer) */}\n {hasWidgetSpecificConfig && ConfigComponent && (\n <ConfigComponent config={config as never} onChange={setConfig as never} />\n )}\n </div>\n </TabsContent>\n </div>\n </Tabs>\n\n <DialogFooter\n style={{\n display: 'flex',\n flexDirection: 'row',\n justifyContent: 'space-between',\n padding: '12px 16px',\n borderTop: '1px solid var(--border)',\n flexShrink: 0,\n }}\n >\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n const currentIndex = steps.findIndex((s) => s.id === step);\n if (currentIndex > 0) {\n setStep(steps[currentIndex - 1].id);\n } else {\n onClose();\n }\n }}\n >\n {step === steps[0]?.id ? 'Cancel' : 'Back'}\n </Button>\n\n <div className=\"flex gap-2\">\n {step !== steps[steps.length - 1]?.id && selectedType && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n const currentIndex = steps.findIndex((s) => s.id === step);\n if (currentIndex < steps.length - 1) {\n setStep(steps[currentIndex + 1].id);\n }\n }}\n >\n Next\n </Button>\n )}\n <Button\n size=\"sm\"\n onClick={handleSave}\n disabled={!selectedType || !title.trim() || (selectedRenderer !== 'BIGCONSOLE' && !template)}\n >\n {isNew ? 'Add Widget' : 'Save'}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default WidgetConfigDialog;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WidgetConfigDialog.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetConfigDialog.tsx"],"sourcesContent":["/**\n * WidgetConfigDialog Component (v2.0)\n *\n * Modal dialog for full widget configuration.\n * v2.0: Added renderer selection and template editor support.\n * Uses fe-libs shared components for consistent styling.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect, useMemo } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Textarea } from '@burdenoff/fe-libs/ui';\nimport { Label } from '@burdenoff/fe-libs/ui';\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from '@burdenoff/fe-libs/ui';\nimport { Card, CardDescription, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { Layout, CreditCard, MessageSquare, Code, ExternalLink } from 'lucide-react';\nimport type { Widget, WidgetType, RendererType, DataSourceType } from '../../types';\nimport { getWidgetDefinition, type WidgetDefinition } from './WidgetRegistry';\nimport { WidgetTypeSelector } from './WidgetTypeSelector';\n\n// Config components\nimport { MetricCardConfig } from './metric-card/MetricCardConfig';\nimport { ChartConfig } from './chart/ChartConfig';\nimport { TableConfig } from './table/TableConfig';\nimport { GaugeConfig } from './gauge/GaugeConfig';\nimport { ProgressConfig } from './progress/ProgressConfig';\nimport { ListConfig } from './list/ListConfig';\n\n// DataSink and Parser selectors\nimport { DataSinkSelector } from '../parser/DataSinkSelector';\nimport { ParserSelector } from '../parser/ParserSelector';\n\n// Store for DataSink lookup\nimport { useDataSinkStore } from '../../store';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetConfigDialogProps {\n /** Whether dialog is open */\n isOpen: boolean;\n /** Widget to edit (undefined for new widget) */\n widget?: Widget;\n /** Pre-selected widget type (for drag & drop from palette) */\n preselectedType?: WidgetType;\n /** Pre-selected widget definition from drag & drop (preserves installed/store widget metadata) */\n preselectedDefinition?: WidgetDefinition;\n /** Callback when dialog is closed */\n onClose: () => void;\n /** Callback when widget is saved */\n onSave: (widget: Partial<Widget>) => void;\n /** Dashboard ID for fetching parsers */\n dashboardId?: string;\n /** Installed widgets from the store (fetched by parent) */\n installedWidgets?: WidgetDefinition[];\n}\n\ntype Step = 'type' | 'general' | 'renderer' | 'config';\n\n// ============================================================================\n// Renderer Definitions\n// ============================================================================\n\ninterface RendererOption {\n type: RendererType;\n name: string;\n description: string;\n icon: typeof Layout;\n supportedTypes: WidgetType[];\n}\n\nconst ALL_WIDGET_TYPES: WidgetType[] = [\n 'kpi_card_comparison',\n 'metric_card',\n 'chart',\n 'funnel_chart',\n 'table',\n 'pivot_table',\n 'gauge',\n 'progress',\n 'list',\n 'form',\n 'text',\n 'iframe',\n 'map',\n 'heatmap',\n 'calendar',\n 'kanban',\n 'timeline',\n 'custom',\n];\n\nconst RENDERER_OPTIONS: RendererOption[] = [\n {\n type: 'BIGCONSOLE',\n name: 'BigConsole Native',\n description: 'Full-featured native widgets with all configuration options',\n icon: Layout,\n supportedTypes: ALL_WIDGET_TYPES,\n },\n {\n type: 'ADAPTIVE_CARD',\n name: 'Microsoft Adaptive Cards',\n description: 'Use Microsoft Adaptive Cards format for Teams/Outlook integration',\n icon: CreditCard,\n supportedTypes: ALL_WIDGET_TYPES,\n },\n {\n type: 'BLOCKKIT',\n name: 'Slack Block Kit',\n description: 'Use Slack Block Kit format for Slack integration',\n icon: MessageSquare,\n supportedTypes: ALL_WIDGET_TYPES,\n },\n {\n type: 'CUSTOM',\n name: 'Custom Component',\n description: 'Use a custom React component registered in the frontend',\n icon: Code,\n supportedTypes: ALL_WIDGET_TYPES,\n },\n];\n\n// ============================================================================\n// Template Presets\n// ============================================================================\n\ninterface TemplatePreset {\n id: string;\n name: string;\n description: string;\n renderer: RendererType;\n template: Record<string, unknown>;\n}\n\nconst TEMPLATE_PRESETS: TemplatePreset[] = [\n {\n id: 'ac-simple-text',\n name: 'Simple Text Card',\n description: 'Basic text display',\n renderer: 'ADAPTIVE_CARD',\n template: {\n type: 'AdaptiveCard',\n version: '1.4',\n body: [{ type: 'TextBlock', text: 'Hello World!', size: 'Large', weight: 'Bolder' }],\n },\n },\n {\n id: 'ac-metric-card',\n name: 'Metric Display',\n description: 'Show a key metric with comparison',\n renderer: 'ADAPTIVE_CARD',\n template: {\n type: 'AdaptiveCard',\n version: '1.4',\n body: [\n { type: 'TextBlock', text: '${title}', size: 'Medium', weight: 'Bolder' },\n { type: 'TextBlock', text: '${value}', size: 'ExtraLarge', weight: 'Bolder', color: 'Accent' },\n { type: 'TextBlock', text: '${change} from last period', size: 'Small', color: 'Good' },\n ],\n },\n },\n {\n id: 'bk-status-update',\n name: 'Status Update',\n description: 'System status display',\n renderer: 'BLOCKKIT',\n template: {\n blocks: [\n { type: 'header', text: { type: 'plain_text', text: 'System Status' } },\n { type: 'section', text: { type: 'mrkdwn', text: '*Status:* All systems operational' } },\n { type: 'divider' },\n {\n type: 'section',\n fields: [\n { type: 'mrkdwn', text: '*API:* Online' },\n { type: 'mrkdwn', text: '*Database:* Online' },\n ],\n },\n ],\n },\n },\n {\n id: 'custom-placeholder',\n name: 'Custom Component',\n description: 'Placeholder for custom React component',\n renderer: 'CUSTOM',\n template: { componentName: 'MyCustomWidget', props: { color: 'blue', size: 'medium' } },\n },\n];\n\n// ============================================================================\n// Config Component Map\n// ============================================================================\n\nfunction getConfigComponent(type: WidgetType) {\n switch (type) {\n case 'metric_card':\n case 'kpi_card_comparison':\n return MetricCardConfig;\n case 'chart':\n case 'funnel_chart':\n return ChartConfig;\n case 'table':\n case 'pivot_table':\n return TableConfig;\n case 'gauge':\n return GaugeConfig;\n case 'progress':\n return ProgressConfig;\n case 'list':\n return ListConfig;\n default:\n return null;\n }\n}\n\n// ============================================================================\n// Template Editor Component\n// ============================================================================\n\ninterface TemplateEditorProps {\n renderer: RendererType;\n template: Record<string, unknown> | null;\n onChange: (template: Record<string, unknown>) => void;\n}\n\nconst TemplateEditor: FC<TemplateEditorProps> = memo(function TemplateEditor({ renderer, template, onChange }) {\n const [jsonError, setJsonError] = useState<string | null>(null);\n const [jsonText, setJsonText] = useState(() => {\n try {\n return template && typeof template === 'object' ? JSON.stringify(template, null, 2) : '';\n } catch {\n return '';\n }\n });\n\n const presets = useMemo(() => TEMPLATE_PRESETS.filter((p) => p.renderer === renderer), [renderer]);\n\n const handleJsonChange = useCallback(\n (text: string) => {\n setJsonText(text);\n try {\n if (text.trim()) {\n const parsed = JSON.parse(text);\n onChange(parsed);\n setJsonError(null);\n } else {\n setJsonError(null);\n }\n } catch (e) {\n setJsonError((e as Error).message);\n }\n },\n [onChange]\n );\n\n const handlePresetSelect = useCallback(\n (preset: TemplatePreset) => {\n const text = JSON.stringify(preset.template, null, 2);\n setJsonText(text);\n onChange(preset.template);\n setJsonError(null);\n },\n [onChange]\n );\n\n useEffect(() => {\n if (template && typeof template === 'object') {\n try {\n const newText = JSON.stringify(template, null, 2);\n setJsonText(newText);\n setJsonError(null);\n } catch (e) {}\n } else if (!template) {\n setJsonText('');\n }\n }, [template]);\n\n return (\n <div className=\"space-y-4\">\n {presets.length > 0 && (\n <div>\n <Label className=\"mb-2\">Quick Start Templates</Label>\n <div className=\"grid grid-cols-2 gap-2\">\n {presets.map((preset) => (\n <Card\n key={preset.id}\n className=\"cursor-pointer hover:border-primary transition-colors\"\n onClick={() => handlePresetSelect(preset)}\n >\n <CardHeader className=\"p-3\">\n <CardTitle className=\"text-sm\">{preset.name}</CardTitle>\n <CardDescription className=\"text-xs\">{preset.description}</CardDescription>\n </CardHeader>\n </Card>\n ))}\n </div>\n </div>\n )}\n\n <div>\n <Label className=\"mb-2\">Template JSON</Label>\n <Textarea\n value={jsonText}\n onChange={(e) => handleJsonChange(e.target.value)}\n placeholder={`Enter ${renderer} template JSON...`}\n rows={12}\n spellCheck={false}\n className={`font-mono text-sm ${jsonError ? 'border-status-error-border focus-visible:ring-destructive' : ''}`}\n />\n {jsonError && <p className=\"mt-1 text-xs text-status-error-text\">Invalid JSON: {jsonError}</p>}\n </div>\n\n <div className=\"flex items-center justify-between rounded-lg bg-bg-sunken p-3\">\n <p className=\"text-xs text-text-secondary\">\n {renderer === 'ADAPTIVE_CARD' && 'Use Microsoft Adaptive Cards format with data binding.'}\n {renderer === 'BLOCKKIT' && 'Use Slack Block Kit format for Slack integration.'}\n {renderer === 'CUSTOM' && 'Specify componentName and props for your custom React component.'}\n </p>\n {renderer === 'ADAPTIVE_CARD' && (\n <Button variant=\"ghost\" size=\"sm\" asChild>\n <a href=\"https://adaptivecards.io/designer/\" target=\"_blank\" rel=\"noopener noreferrer\">\n <ExternalLink className=\"size-3.5\" />\n Designer\n </a>\n </Button>\n )}\n {renderer === 'BLOCKKIT' && (\n <Button variant=\"ghost\" size=\"sm\" asChild>\n <a href=\"https://app.slack.com/block-kit-builder\" target=\"_blank\" rel=\"noopener noreferrer\">\n <ExternalLink className=\"size-3.5\" />\n Builder\n </a>\n </Button>\n )}\n </div>\n </div>\n );\n});\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetConfigDialog: FC<WidgetConfigDialogProps> = memo(function WidgetConfigDialog({\n isOpen,\n widget,\n preselectedType,\n preselectedDefinition,\n onClose,\n onSave,\n dashboardId,\n installedWidgets,\n}) {\n const isNew = !widget;\n // If preselectedType is provided, skip the type selection step\n const [step, setStep] = useState<Step>(isNew ? (preselectedType ? 'general' : 'type') : 'general');\n\n // Access DataSink store to look up ID from key\n const dataSinks = useDataSinkStore((state) => state.dataSinks);\n\n // Form state - use preselectedType if provided\n const [selectedType, setSelectedType] = useState<WidgetType | undefined>(widget?.type || preselectedType);\n const [title, setTitle] = useState(widget?.title || '');\n const [description, setDescription] = useState(widget?.description || '');\n const [refreshInterval, setRefreshInterval] = useState<number | undefined>(widget?.refreshInterval ?? undefined);\n const [config, setConfig] = useState<Record<string, unknown>>((widget?.config as Record<string, unknown>) || {});\n const [selectedRenderer, setSelectedRenderer] = useState<RendererType>(widget?.renderer || 'BIGCONSOLE');\n const [template, setTemplate] = useState<Record<string, unknown> | null>(widget?.template || null);\n\n // Data Source state\n const [dataSourceType, setDataSourceType] = useState<DataSourceType>(\n ((widget?.config as Record<string, unknown>)?.dataSourceType as DataSourceType) || 'MOCK'\n );\n const [dataSinkKey, setDataSinkKey] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.dataSinkKey as string) || ''\n );\n const [parserId, setParserId] = useState<string>((widget as unknown as Record<string, string>)?.parserId || '');\n\n // REST API config state\n const [endpointUrl, setEndpointUrl] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.endpointUrl as string) || ''\n );\n const [httpMethod, setHttpMethod] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.httpMethod as string) || 'GET'\n );\n const [headers, setHeaders] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.headers as string) || ''\n );\n const [authType, setAuthType] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.authType as string) || 'none'\n );\n const [authValue, setAuthValue] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.authValue as string) || ''\n );\n const [requestBody, setRequestBody] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.requestBody as string) || ''\n );\n const [dataPath, setDataPath] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.dataPath as string) || ''\n );\n\n // GraphQL config state\n const [graphqlEndpoint, setGraphqlEndpoint] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.graphqlEndpoint as string) || ''\n );\n const [graphqlQuery, setGraphqlQuery] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.graphqlQuery as string) || ''\n );\n const [graphqlVariables, setGraphqlVariables] = useState<string>(\n ((widget?.config as Record<string, unknown>)?.graphqlVariables as string) || ''\n );\n\n const availableRenderers = useMemo(() => {\n if (!selectedType) return RENDERER_OPTIONS;\n return RENDERER_OPTIONS.filter((r) => r.supportedTypes.includes(selectedType));\n }, [selectedType]);\n\n useEffect(() => {\n if (selectedType && !availableRenderers.find((r) => r.type === selectedRenderer)) {\n setSelectedRenderer('BIGCONSOLE');\n setTemplate(null);\n }\n }, [selectedType, availableRenderers, selectedRenderer]);\n\n useEffect(() => {\n if (widget) {\n setSelectedType(widget.type);\n setTitle(widget.title);\n setDescription(widget.description || '');\n setRefreshInterval(widget.refreshInterval ?? undefined);\n setConfig((widget.config as Record<string, unknown>) || {});\n setSelectedRenderer(widget.renderer || 'BIGCONSOLE');\n setTemplate(widget.template || null);\n // Load data source settings\n const widgetConfig = (widget.config as Record<string, unknown>) || {};\n setDataSourceType((widgetConfig.dataSourceType as DataSourceType) || 'MOCK');\n setDataSinkKey((widgetConfig.dataSinkKey as string) || '');\n setParserId((widget as unknown as Record<string, string>).parserId || '');\n // Load REST API config\n setEndpointUrl((widgetConfig.endpointUrl as string) || '');\n setHttpMethod((widgetConfig.httpMethod as string) || 'GET');\n setHeaders((widgetConfig.headers as string) || '');\n setAuthType((widgetConfig.authType as string) || 'none');\n setAuthValue((widgetConfig.authValue as string) || '');\n setRequestBody((widgetConfig.requestBody as string) || '');\n setDataPath((widgetConfig.dataPath as string) || '');\n // Load GraphQL config\n setGraphqlEndpoint((widgetConfig.graphqlEndpoint as string) || '');\n setGraphqlQuery((widgetConfig.graphqlQuery as string) || '');\n setGraphqlVariables((widgetConfig.graphqlVariables as string) || '');\n setStep('general');\n } else if (preselectedType) {\n // Handle preselected type from drag & drop.\n // Use preselectedDefinition (if provided) to preserve installed/store widget metadata\n // instead of looking up the generic 'custom' definition from the registry.\n const definition = preselectedDefinition || getWidgetDefinition(preselectedType);\n setSelectedType(preselectedType);\n setTitle(definition.name);\n setDescription('');\n setRefreshInterval(undefined);\n setConfig(definition.defaultConfig || {});\n // Installed/store widgets use CUSTOM renderer; built-in widgets use BIGCONSOLE.\n setSelectedRenderer(definition.category === 'Installed' ? 'CUSTOM' : 'BIGCONSOLE');\n setTemplate(null);\n setDataSourceType('MOCK');\n setDataSinkKey('');\n setParserId('');\n // Reset REST API config\n setEndpointUrl('');\n setHttpMethod('GET');\n setHeaders('');\n setAuthType('none');\n setAuthValue('');\n setRequestBody('');\n setDataPath('');\n // Reset GraphQL config\n setGraphqlEndpoint('');\n setGraphqlQuery('');\n setGraphqlVariables('');\n // Skip type selection, go to general\n setStep('general');\n } else {\n setSelectedType(undefined);\n setTitle('');\n setDescription('');\n setRefreshInterval(undefined);\n setConfig({});\n setSelectedRenderer('BIGCONSOLE');\n setTemplate(null);\n setDataSourceType('MOCK');\n setDataSinkKey('');\n setParserId('');\n // Reset REST API config\n setEndpointUrl('');\n setHttpMethod('GET');\n setHeaders('');\n setAuthType('none');\n setAuthValue('');\n setRequestBody('');\n setDataPath('');\n // Reset GraphQL config\n setGraphqlEndpoint('');\n setGraphqlQuery('');\n setGraphqlVariables('');\n setStep('type');\n }\n }, [widget, preselectedType, preselectedDefinition, isOpen]);\n\n const handleTypeSelect = useCallback((type: WidgetType, installedDefinition?: WidgetDefinition) => {\n setSelectedType(type);\n const definition = installedDefinition || getWidgetDefinition(type);\n if (definition) {\n setTitle(definition.name);\n setConfig(definition.defaultConfig || {});\n // Installed/store widgets must use the CUSTOM renderer so the store\n // component is resolved via WidgetRendererFactory instead of the\n // native BigConsole path.\n if (definition.category === 'Installed') {\n setSelectedRenderer('CUSTOM');\n }\n }\n }, []);\n\n const handleRendererSelect = useCallback((renderer: RendererType) => {\n setSelectedRenderer(renderer);\n if (renderer === 'BIGCONSOLE') {\n setTemplate(null);\n } else {\n const preset = TEMPLATE_PRESETS.find((p) => p.renderer === renderer);\n setTemplate(preset ? preset.template : {});\n }\n }, []);\n\n const handleSave = useCallback(() => {\n if (!selectedType) return;\n\n // Look up the actual DataSink ID (UUID) from the key\n // dataSinks is a Map, so we need to iterate to find by key\n let actualDataSinkId: string | undefined;\n if (dataSinkKey) {\n for (const ds of dataSinks.values()) {\n if (ds.key === dataSinkKey) {\n actualDataSinkId = ds.id;\n break;\n }\n }\n }\n\n const widgetData: Partial<Widget> & { dataSinkId?: string; parserId?: string } = {\n type: selectedType,\n title,\n description: description || undefined,\n refreshInterval,\n config: {\n ...config,\n dataSourceType,\n dataSinkKey: dataSinkKey || undefined,\n // REST API config\n ...(dataSourceType === 'REST_API'\n ? {\n endpointUrl: endpointUrl || undefined,\n httpMethod: httpMethod || 'GET',\n headers: headers || undefined,\n authType: authType || 'none',\n authValue: authValue || undefined,\n requestBody: requestBody || undefined,\n dataPath: dataPath || undefined,\n }\n : {}),\n // GraphQL config\n ...(dataSourceType === 'GRAPHQL'\n ? {\n graphqlEndpoint: graphqlEndpoint || undefined,\n graphqlQuery: graphqlQuery || undefined,\n graphqlVariables: graphqlVariables || undefined,\n headers: headers || undefined,\n dataPath: dataPath || undefined,\n }\n : {}),\n },\n renderer: selectedRenderer,\n template: selectedRenderer !== 'BIGCONSOLE' ? template : undefined,\n // v2.0: Data source fields at top level for API (DataSink → Parser → Widget)\n // Use actual dataSink ID (UUID) looked up from the key\n dataSinkId: actualDataSinkId || undefined,\n parserId: parserId || undefined,\n };\n onSave(widgetData);\n onClose();\n }, [\n selectedType,\n title,\n description,\n refreshInterval,\n config,\n selectedRenderer,\n template,\n dataSourceType,\n dataSinkKey,\n dataSinks,\n parserId,\n endpointUrl,\n httpMethod,\n headers,\n authType,\n authValue,\n requestBody,\n dataPath,\n graphqlEndpoint,\n graphqlQuery,\n graphqlVariables,\n onSave,\n onClose,\n ]);\n\n const definition = selectedType ? getWidgetDefinition(selectedType) : null;\n const ConfigComponent = selectedType ? getConfigComponent(selectedType) : null;\n const showRendererStep = selectedRenderer !== 'BIGCONSOLE';\n // Always show config step - it has Data Source settings needed for all renderers\n const showConfigStep = true;\n const hasWidgetSpecificConfig = selectedRenderer === 'BIGCONSOLE' && ConfigComponent;\n\n const steps = useMemo(() => {\n const allSteps = [\n { id: 'type' as Step, label: 'Type', show: isNew },\n { id: 'general' as Step, label: 'General', show: true },\n { id: 'renderer' as Step, label: 'Template', show: showRendererStep },\n { id: 'config' as Step, label: 'Data Source', show: showConfigStep },\n ];\n return allSteps.filter((s) => s.show);\n }, [isNew, showRendererStep, showConfigStep]);\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent\n style={{\n maxWidth: '520px',\n width: '95vw',\n maxHeight: '85vh',\n display: 'flex',\n flexDirection: 'column',\n padding: 0,\n overflow: 'hidden',\n }}\n >\n <DialogHeader style={{ padding: '16px 16px 8px 16px', flexShrink: 0 }}>\n <DialogTitle style={{ fontSize: '16px' }}>{isNew ? 'Add Widget' : 'Edit Widget'}</DialogTitle>\n {/* Always render DialogDescription (Radix requires it for a11y).\n When no definition is resolved yet, fall back to a generic label. */}\n <DialogDescription className=\"text-xs\">\n {definition ? (\n <>\n {definition.name}\n {selectedRenderer !== 'BIGCONSOLE' && (\n <span className=\"ml-2 px-1.5 py-0.5 text-xs bg-action-primary-bg/10 text-primary rounded\">\n {RENDERER_OPTIONS.find((r) => r.type === selectedRenderer)?.name}\n </span>\n )}\n </>\n ) : (\n 'Configure the widget data source, appearance, and actions.'\n )}\n </DialogDescription>\n </DialogHeader>\n\n {/* Steps */}\n <Tabs\n value={step}\n onValueChange={(v) => setStep(v as Step)}\n style={{ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0, overflow: 'hidden' }}\n >\n <TabsList\n style={{ margin: '0 16px', width: 'auto', justifyContent: 'flex-start', height: '32px', flexShrink: 0 }}\n >\n {steps.map((s) => (\n <TabsTrigger\n key={s.id}\n value={s.id}\n disabled={s.id !== 'type' && !selectedType}\n style={{ fontSize: '12px', padding: '4px 12px' }}\n >\n {s.label}\n </TabsTrigger>\n ))}\n </TabsList>\n\n <div\n style={{\n flex: 1,\n minHeight: 0,\n marginTop: '8px',\n overflow: 'auto',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <TabsContent value=\"type\" style={{ marginTop: 0, height: '100%', overflow: 'hidden' }}>\n <WidgetTypeSelector\n selectedType={selectedType}\n selectedRenderer={selectedRenderer}\n onSelect={(type, definition) => {\n handleTypeSelect(type, definition);\n setStep('general');\n }}\n onRendererSelect={handleRendererSelect}\n installedWidgets={installedWidgets}\n />\n </TabsContent>\n\n <TabsContent\n value=\"general\"\n style={{\n marginTop: 0,\n flex: 1,\n overflowY: 'auto',\n padding: '12px 16px',\n minHeight: '300px',\n }}\n >\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"title\">Title</Label>\n <Input\n id=\"title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"Widget title\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"description\">Description (optional)</Label>\n <Textarea\n id=\"description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Brief description of this widget\"\n rows={2}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Renderer</Label>\n <div className=\"grid grid-cols-2 gap-2\">\n {availableRenderers.map((renderer) => {\n const Icon = renderer.icon;\n return (\n <Card\n key={renderer.type}\n className={`cursor-pointer transition-colors ${\n selectedRenderer === renderer.type\n ? 'border-primary bg-action-primary-bg/5'\n : 'hover:border-primary/50'\n }`}\n onClick={() => handleRendererSelect(renderer.type)}\n >\n <CardHeader className=\"p-3\">\n <div className=\"flex items-start gap-3\">\n <div\n className={`p-2 rounded-md ${\n selectedRenderer === renderer.type\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken'\n }`}\n >\n <Icon className=\"size-4\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <CardTitle className=\"text-sm\">{renderer.name}</CardTitle>\n <CardDescription className=\"text-xs line-clamp-2\">\n {renderer.description}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n </Card>\n );\n })}\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"refresh\">Auto-Refresh Interval (seconds)</Label>\n <Input\n id=\"refresh\"\n type=\"number\"\n value={refreshInterval || ''}\n onChange={(e) => setRefreshInterval(e.target.value ? Number(e.target.value) : undefined)}\n min={0}\n placeholder=\"No auto-refresh\"\n />\n </div>\n </div>\n </TabsContent>\n\n <TabsContent\n value=\"renderer\"\n style={{\n marginTop: 0,\n flex: 1,\n overflowY: 'auto',\n padding: '12px 16px',\n minHeight: '300px',\n }}\n >\n <TemplateEditor renderer={selectedRenderer} template={template} onChange={setTemplate} />\n </TabsContent>\n\n <TabsContent\n value=\"config\"\n style={{\n marginTop: 0,\n flex: 1,\n overflowY: 'auto',\n padding: '12px 16px',\n minHeight: '300px',\n }}\n >\n <div className=\"space-y-4\">\n {/* Data Source Section */}\n <div className=\"space-y-3 p-3 border border-border-subtle rounded-lg bg-bg-sunken/30\">\n <h4 className=\"text-sm font-medium\">Data Source</h4>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"dataSourceType\">Source Type</Label>\n <select\n id=\"dataSourceType\"\n value={dataSourceType}\n onChange={(e) => setDataSourceType(e.target.value as DataSourceType)}\n className=\"w-full h-9 px-3 rounded-md border border-border-default bg-bg-surface text-sm\"\n >\n <option value=\"MOCK\">Sample JSON</option>\n <option value=\"DATASINK\">DataSink</option>\n <option value=\"REST_API\">REST API</option>\n <option value=\"GRAPHQL\">GraphQL</option>\n </select>\n </div>\n\n {dataSourceType === 'DATASINK' && (\n <div className=\"space-y-2\">\n <Label htmlFor=\"dataSinkKey\">DataSink</Label>\n <DataSinkSelector\n value={dataSinkKey || null}\n onChange={(key) => setDataSinkKey(key || '')}\n placeholder=\"Select a DataSink...\"\n />\n </div>\n )}\n\n {/* REST API Configuration */}\n {dataSourceType === 'REST_API' && (\n <div className=\"space-y-3\">\n <div className=\"space-y-2\">\n <Label>Endpoint URL *</Label>\n <Input\n value={endpointUrl}\n onChange={(e) => setEndpointUrl(e.target.value)}\n placeholder=\"https://api.example.com/data\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>HTTP Method</Label>\n <select\n value={httpMethod}\n onChange={(e) => setHttpMethod(e.target.value)}\n className=\"w-full h-9 px-3 rounded-md border border-border-default bg-bg-surface text-sm\"\n >\n <option value=\"GET\">GET</option>\n <option value=\"POST\">POST</option>\n <option value=\"PUT\">PUT</option>\n <option value=\"DELETE\">DELETE</option>\n </select>\n </div>\n\n <div className=\"space-y-2\">\n <Label>Headers (JSON)</Label>\n <Textarea\n value={headers}\n onChange={(e) => setHeaders(e.target.value)}\n rows={2}\n placeholder='{\"Content-Type\": \"application/json\"}'\n className=\"font-mono text-xs\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Authentication</Label>\n <select\n value={authType}\n onChange={(e) => setAuthType(e.target.value)}\n className=\"w-full h-9 px-3 rounded-md border border-border-default bg-bg-surface text-sm\"\n >\n <option value=\"none\">None</option>\n <option value=\"api_key\">API Key</option>\n <option value=\"bearer\">Bearer Token</option>\n <option value=\"basic\">Basic Auth</option>\n </select>\n </div>\n\n {authType !== 'none' && (\n <div className=\"space-y-2\">\n <Label>{authType === 'basic' ? 'Credentials (user:pass)' : 'Token/Key'}</Label>\n <Input\n type=\"password\"\n value={authValue}\n onChange={(e) => setAuthValue(e.target.value)}\n placeholder={authType === 'basic' ? 'username:password' : 'Enter token or API key'}\n />\n </div>\n )}\n\n {(httpMethod === 'POST' || httpMethod === 'PUT') && (\n <div className=\"space-y-2\">\n <Label>Request Body (JSON)</Label>\n <Textarea\n value={requestBody}\n onChange={(e) => setRequestBody(e.target.value)}\n rows={3}\n placeholder='{\"key\": \"value\"}'\n className=\"font-mono text-xs\"\n />\n </div>\n )}\n\n <div className=\"space-y-2\">\n <Label>Data Path (optional)</Label>\n <Input\n value={dataPath}\n onChange={(e) => setDataPath(e.target.value)}\n placeholder=\"$.data.items or .data.items\"\n />\n <p className=\"text-xs text-text-secondary\">Extract data from response using JSONPath</p>\n </div>\n </div>\n )}\n\n {/* GraphQL Configuration */}\n {dataSourceType === 'GRAPHQL' && (\n <div className=\"space-y-3\">\n <div className=\"space-y-2\">\n <Label>GraphQL Endpoint *</Label>\n <Input\n value={graphqlEndpoint}\n onChange={(e) => setGraphqlEndpoint(e.target.value)}\n placeholder=\"https://api.example.com/graphql\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Query *</Label>\n <Textarea\n value={graphqlQuery}\n onChange={(e) => setGraphqlQuery(e.target.value)}\n rows={5}\n placeholder={`query GetData($limit: Int) {\n items(limit: $limit) {\n id\n name\n }\n}`}\n className=\"font-mono text-xs\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Variables (JSON, optional)</Label>\n <Textarea\n value={graphqlVariables}\n onChange={(e) => setGraphqlVariables(e.target.value)}\n rows={2}\n placeholder='{\"limit\": 10}'\n className=\"font-mono text-xs\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Headers (JSON, optional)</Label>\n <Textarea\n value={headers}\n onChange={(e) => setHeaders(e.target.value)}\n rows={2}\n placeholder='{\"Authorization\": \"Bearer token\"}'\n className=\"font-mono text-xs\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Data Path (optional)</Label>\n <Input\n value={dataPath}\n onChange={(e) => setDataPath(e.target.value)}\n placeholder=\".data.items\"\n />\n <p className=\"text-xs text-text-secondary\">Extract data from GraphQL response</p>\n </div>\n </div>\n )}\n\n {dashboardId && (\n <div className=\"space-y-2\">\n <Label htmlFor=\"parserId\">Parser (optional)</Label>\n <ParserSelector\n consoleId={dashboardId}\n value={parserId || null}\n onChange={(id) => setParserId(id || '')}\n filterInputSinkKey={dataSourceType === 'DATASINK' ? dataSinkKey : undefined}\n filterStatus={undefined}\n placeholder=\"Select a parser...\"\n />\n <p className=\"text-xs text-text-secondary\">Transform data using a parser before display</p>\n </div>\n )}\n </div>\n\n {/* Widget-specific config (only for BIGCONSOLE renderer) */}\n {hasWidgetSpecificConfig && ConfigComponent && (\n <ConfigComponent config={config as never} onChange={setConfig as never} />\n )}\n </div>\n </TabsContent>\n </div>\n </Tabs>\n\n <DialogFooter\n style={{\n display: 'flex',\n flexDirection: 'row',\n justifyContent: 'space-between',\n padding: '12px 16px',\n borderTop: '1px solid var(--border)',\n flexShrink: 0,\n }}\n >\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n const currentIndex = steps.findIndex((s) => s.id === step);\n if (currentIndex > 0) {\n setStep(steps[currentIndex - 1].id);\n } else {\n onClose();\n }\n }}\n >\n {step === steps[0]?.id ? 'Cancel' : 'Back'}\n </Button>\n\n <div className=\"flex gap-2\">\n {step !== steps[steps.length - 1]?.id && selectedType && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n const currentIndex = steps.findIndex((s) => s.id === step);\n if (currentIndex < steps.length - 1) {\n setStep(steps[currentIndex + 1].id);\n }\n }}\n >\n Next\n </Button>\n )}\n <Button\n size=\"sm\"\n onClick={handleSave}\n disabled={!selectedType || !title.trim() || (selectedRenderer !== 'BIGCONSOLE' && !template)}\n >\n {isNew ? 'Add Widget' : 'Save'}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default WidgetConfigDialog;\n"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isL
|
|
|
9
9
|
flex items-center justify-between
|
|
10
10
|
px-4 py-3
|
|
11
11
|
border-b border-border-default
|
|
12
|
-
bg-bg-
|
|
12
|
+
bg-bg-sunken
|
|
13
13
|
${s && c ? "bg-action-primary-bg/5" : ""}
|
|
14
14
|
`,
|
|
15
15
|
children: [/* @__PURE__ */ i("div", {
|
|
@@ -62,7 +62,7 @@ var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isL
|
|
|
62
62
|
onClick: (e) => {
|
|
63
63
|
e.stopPropagation(), h();
|
|
64
64
|
},
|
|
65
|
-
className: "\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-
|
|
65
|
+
className: "\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n ",
|
|
66
66
|
title: "Drill down to details",
|
|
67
67
|
children: /* @__PURE__ */ r("svg", {
|
|
68
68
|
className: "w-4 h-4",
|
|
@@ -87,7 +87,7 @@ var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isL
|
|
|
87
87
|
p-1.5 rounded-md
|
|
88
88
|
text-text-secondary
|
|
89
89
|
hover:text-text-primary
|
|
90
|
-
hover:bg-bg-
|
|
90
|
+
hover:bg-bg-sunken
|
|
91
91
|
transition-colors
|
|
92
92
|
disabled:opacity-50 disabled:cursor-not-allowed
|
|
93
93
|
${l ? "animate-spin" : ""}
|
|
@@ -116,7 +116,7 @@ var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isL
|
|
|
116
116
|
className: `
|
|
117
117
|
relative p-1.5 rounded-md
|
|
118
118
|
transition-colors
|
|
119
|
-
${_ ? "text-action-primary-text bg-action-primary-bg/10" : "text-text-secondary hover:text-text-primary hover:bg-bg-
|
|
119
|
+
${_ ? "text-action-primary-text bg-action-primary-bg/10" : "text-text-secondary hover:text-text-primary hover:bg-bg-sunken"}
|
|
120
120
|
`,
|
|
121
121
|
title: "Comments",
|
|
122
122
|
children: [/* @__PURE__ */ r(n, { className: "w-4 h-4" }), v > 0 && /* @__PURE__ */ r("span", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetHeader.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetHeader.tsx"],"sourcesContent":["/**\n * WidgetHeader Component\n *\n * Header section of a widget with title, icon, and action menu.\n */\n\nimport { type FC, type ReactElement, memo } from 'react';\nimport { MessageSquare } from 'lucide-react';\nimport type { Widget } from '../../types';\nimport type { WidgetDefinition } from './WidgetRegistry';\nimport { WidgetMenu } from './WidgetMenu';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetHeaderProps {\n /** The widget data */\n widget: Widget;\n /** Widget definition from registry */\n definition: WidgetDefinition;\n /** Whether the widget is selected */\n isSelected?: boolean;\n /** Whether in edit mode */\n isEditMode?: boolean;\n /** Whether the widget is loading */\n isLoading?: boolean;\n /** Time until next refresh (formatted string) */\n timeUntilRefresh?: string;\n /** Whether drilldown is enabled on this widget */\n hasDrilldown?: boolean;\n /** Callback to refresh widget */\n onRefresh?: () => void;\n /** Callback to export as CSV */\n onExportCSV?: () => void;\n /** Callback to export as PNG */\n onExportPNG?: () => void;\n /** Callback for drilldown */\n onDrilldown?: () => void;\n /** Toggle the widget comments panel */\n onToggleComments?: () => void;\n /** Whether the comments panel is currently open */\n commentsOpen?: boolean;\n /** Number of comments on this widget (badge) */\n commentCount?: number;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetHeader: FC<WidgetHeaderProps> = memo(function WidgetHeader({\n widget,\n definition,\n isSelected,\n isEditMode,\n isLoading,\n timeUntilRefresh,\n hasDrilldown,\n onRefresh,\n onExportCSV,\n onExportPNG,\n onDrilldown,\n onToggleComments,\n commentsOpen,\n commentCount = 0,\n}) {\n return (\n <div\n className={`\n flex items-center justify-between\n px-4 py-3\n border-b border-border-default\n bg-bg-muted\n ${isSelected && isEditMode ? 'bg-action-primary-bg/5' : ''}\n `}\n >\n {/* Left side: Icon and Title - THIS IS THE DRAG HANDLE */}\n <div\n className={`\n widget-drag-handle\n flex items-center gap-2 min-w-0 flex-1\n ${isEditMode ? 'cursor-move' : ''}\n `}\n >\n {/* Widget type icon */}\n <div\n className={`\n flex-shrink-0 w-5 h-5\n text-text-secondary\n ${isLoading ? 'animate-pulse' : ''}\n `}\n >\n <WidgetIcon name={definition.icon} />\n </div>\n\n {/* Title */}\n <h3\n className=\"\n text-sm font-medium\n text-text-primary\n truncate\n \"\n title={widget.title}\n >\n {widget.title}\n </h3>\n\n {/* Drilldown indicator badge */}\n {hasDrilldown && (\n <span\n className=\"\n flex-shrink-0 inline-flex items-center gap-0.5\n px-1.5 py-0.5 rounded-md\n bg-action-primary-bg/10 text-action-primary-text\n text-[10px] font-medium\n \"\n title=\"Click data to drill down\"\n >\n <svg className=\"w-2.5 h-2.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2.5}\n d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\"\n />\n </svg>\n Drilldown\n </span>\n )}\n\n {/* Refresh indicator */}\n {timeUntilRefresh && !isEditMode && (\n <span\n className=\"\n flex-shrink-0 text-xs\n text-text-secondary\n tabular-nums\n \"\n title=\"Time until next refresh\"\n >\n {timeUntilRefresh}\n </span>\n )}\n </div>\n\n {/* Right side: Actions */}\n <div className=\"flex items-center gap-1 ml-2\">\n {/* Drilldown button (shown in view mode) */}\n {onDrilldown && !isEditMode && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDrilldown();\n }}\n className=\"\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-muted\n transition-colors\n \"\n title=\"Drill down to details\"\n >\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=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\"\n />\n </svg>\n </button>\n )}\n\n {/* Refresh button */}\n {onRefresh && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onRefresh();\n }}\n disabled={isLoading}\n className={`\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-muted\n transition-colors\n disabled:opacity-50 disabled:cursor-not-allowed\n ${isLoading ? 'animate-spin' : ''}\n `}\n title=\"Refresh data\"\n >\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=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n </button>\n )}\n\n {/* Comments toggle */}\n {onToggleComments && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleComments();\n }}\n aria-label=\"Toggle comments\"\n aria-pressed={commentsOpen}\n className={`\n relative p-1.5 rounded-md\n transition-colors\n ${\n commentsOpen\n ? 'text-action-primary-text bg-action-primary-bg/10'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-muted'\n }\n `}\n title=\"Comments\"\n >\n <MessageSquare className=\"w-4 h-4\" />\n {commentCount > 0 && (\n <span className=\"absolute -top-0.5 -right-0.5 min-w-[14px] h-[14px] px-0.5 rounded-full bg-action-primary-bg text-action-primary-text text-[9px] font-semibold leading-[14px] text-center tabular-nums\">\n {commentCount > 99 ? '99+' : commentCount}\n </span>\n )}\n </button>\n )}\n\n {/* Menu (always visible) */}\n <div className=\"block\">\n <WidgetMenu\n widget={widget}\n isEditMode={isEditMode}\n onRefresh={onRefresh}\n onExportCSV={onExportCSV}\n onExportPNG={onExportPNG}\n />\n </div>\n </div>\n </div>\n );\n});\n\n// ============================================================================\n// Widget Icon Component\n// ============================================================================\n\ninterface WidgetIconProps {\n name: string;\n className?: string;\n}\n\nconst WidgetIcon: FC<WidgetIconProps> = ({ name, className = '' }) => {\n // Simple icon mapping - in production, use lucide-react or similar\n const icons: Record<string, ReactElement> = {\n TrendingUp: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 7h8m0 0v8m0-8l-8 8-4-4-6 6\" />\n </svg>\n ),\n BarChart3: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M18 20V10M12 20V4M6 20v-6\" />\n </svg>\n ),\n LineChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M7 20l4-16m2 16l4-16M6 9h14M4 15h14\" />\n </svg>\n ),\n Table: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n ),\n Gauge: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n ),\n Activity: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M22 12h-4l-3 9L9 3l-3 9H2\" />\n </svg>\n ),\n List: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 6h16M4 10h16M4 14h16M4 18h16\" />\n </svg>\n ),\n PieChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z\"\n />\n </svg>\n ),\n BarChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n ),\n AreaChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M7 12l3-3 3 3 4-4M8 21l4-4 4 4M3 4h18M4 4v16\"\n />\n </svg>\n ),\n };\n\n // Default icon\n const defaultIcon = (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n );\n\n return icons[name] || defaultIcon;\n};\n\nexport default WidgetHeader;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WidgetHeader.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetHeader.tsx"],"sourcesContent":["/**\n * WidgetHeader Component\n *\n * Header section of a widget with title, icon, and action menu.\n */\n\nimport { type FC, type ReactElement, memo } from 'react';\nimport { MessageSquare } from 'lucide-react';\nimport type { Widget } from '../../types';\nimport type { WidgetDefinition } from './WidgetRegistry';\nimport { WidgetMenu } from './WidgetMenu';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetHeaderProps {\n /** The widget data */\n widget: Widget;\n /** Widget definition from registry */\n definition: WidgetDefinition;\n /** Whether the widget is selected */\n isSelected?: boolean;\n /** Whether in edit mode */\n isEditMode?: boolean;\n /** Whether the widget is loading */\n isLoading?: boolean;\n /** Time until next refresh (formatted string) */\n timeUntilRefresh?: string;\n /** Whether drilldown is enabled on this widget */\n hasDrilldown?: boolean;\n /** Callback to refresh widget */\n onRefresh?: () => void;\n /** Callback to export as CSV */\n onExportCSV?: () => void;\n /** Callback to export as PNG */\n onExportPNG?: () => void;\n /** Callback for drilldown */\n onDrilldown?: () => void;\n /** Toggle the widget comments panel */\n onToggleComments?: () => void;\n /** Whether the comments panel is currently open */\n commentsOpen?: boolean;\n /** Number of comments on this widget (badge) */\n commentCount?: number;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetHeader: FC<WidgetHeaderProps> = memo(function WidgetHeader({\n widget,\n definition,\n isSelected,\n isEditMode,\n isLoading,\n timeUntilRefresh,\n hasDrilldown,\n onRefresh,\n onExportCSV,\n onExportPNG,\n onDrilldown,\n onToggleComments,\n commentsOpen,\n commentCount = 0,\n}) {\n return (\n <div\n className={`\n flex items-center justify-between\n px-4 py-3\n border-b border-border-default\n bg-bg-sunken\n ${isSelected && isEditMode ? 'bg-action-primary-bg/5' : ''}\n `}\n >\n {/* Left side: Icon and Title - THIS IS THE DRAG HANDLE */}\n <div\n className={`\n widget-drag-handle\n flex items-center gap-2 min-w-0 flex-1\n ${isEditMode ? 'cursor-move' : ''}\n `}\n >\n {/* Widget type icon */}\n <div\n className={`\n flex-shrink-0 w-5 h-5\n text-text-secondary\n ${isLoading ? 'animate-pulse' : ''}\n `}\n >\n <WidgetIcon name={definition.icon} />\n </div>\n\n {/* Title */}\n <h3\n className=\"\n text-sm font-medium\n text-text-primary\n truncate\n \"\n title={widget.title}\n >\n {widget.title}\n </h3>\n\n {/* Drilldown indicator badge */}\n {hasDrilldown && (\n <span\n className=\"\n flex-shrink-0 inline-flex items-center gap-0.5\n px-1.5 py-0.5 rounded-md\n bg-action-primary-bg/10 text-action-primary-text\n text-[10px] font-medium\n \"\n title=\"Click data to drill down\"\n >\n <svg className=\"w-2.5 h-2.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2.5}\n d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\"\n />\n </svg>\n Drilldown\n </span>\n )}\n\n {/* Refresh indicator */}\n {timeUntilRefresh && !isEditMode && (\n <span\n className=\"\n flex-shrink-0 text-xs\n text-text-secondary\n tabular-nums\n \"\n title=\"Time until next refresh\"\n >\n {timeUntilRefresh}\n </span>\n )}\n </div>\n\n {/* Right side: Actions */}\n <div className=\"flex items-center gap-1 ml-2\">\n {/* Drilldown button (shown in view mode) */}\n {onDrilldown && !isEditMode && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDrilldown();\n }}\n className=\"\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n \"\n title=\"Drill down to details\"\n >\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=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\"\n />\n </svg>\n </button>\n )}\n\n {/* Refresh button */}\n {onRefresh && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onRefresh();\n }}\n disabled={isLoading}\n className={`\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n disabled:opacity-50 disabled:cursor-not-allowed\n ${isLoading ? 'animate-spin' : ''}\n `}\n title=\"Refresh data\"\n >\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=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n </button>\n )}\n\n {/* Comments toggle */}\n {onToggleComments && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleComments();\n }}\n aria-label=\"Toggle comments\"\n aria-pressed={commentsOpen}\n className={`\n relative p-1.5 rounded-md\n transition-colors\n ${\n commentsOpen\n ? 'text-action-primary-text bg-action-primary-bg/10'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-sunken'\n }\n `}\n title=\"Comments\"\n >\n <MessageSquare className=\"w-4 h-4\" />\n {commentCount > 0 && (\n <span className=\"absolute -top-0.5 -right-0.5 min-w-[14px] h-[14px] px-0.5 rounded-full bg-action-primary-bg text-action-primary-text text-[9px] font-semibold leading-[14px] text-center tabular-nums\">\n {commentCount > 99 ? '99+' : commentCount}\n </span>\n )}\n </button>\n )}\n\n {/* Menu (always visible) */}\n <div className=\"block\">\n <WidgetMenu\n widget={widget}\n isEditMode={isEditMode}\n onRefresh={onRefresh}\n onExportCSV={onExportCSV}\n onExportPNG={onExportPNG}\n />\n </div>\n </div>\n </div>\n );\n});\n\n// ============================================================================\n// Widget Icon Component\n// ============================================================================\n\ninterface WidgetIconProps {\n name: string;\n className?: string;\n}\n\nconst WidgetIcon: FC<WidgetIconProps> = ({ name, className = '' }) => {\n // Simple icon mapping - in production, use lucide-react or similar\n const icons: Record<string, ReactElement> = {\n TrendingUp: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 7h8m0 0v8m0-8l-8 8-4-4-6 6\" />\n </svg>\n ),\n BarChart3: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M18 20V10M12 20V4M6 20v-6\" />\n </svg>\n ),\n LineChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M7 20l4-16m2 16l4-16M6 9h14M4 15h14\" />\n </svg>\n ),\n Table: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n ),\n Gauge: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n ),\n Activity: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M22 12h-4l-3 9L9 3l-3 9H2\" />\n </svg>\n ),\n List: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 6h16M4 10h16M4 14h16M4 18h16\" />\n </svg>\n ),\n PieChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z\"\n />\n </svg>\n ),\n BarChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n ),\n AreaChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M7 12l3-3 3 3 4-4M8 21l4-4 4 4M3 4h18M4 4v16\"\n />\n </svg>\n ),\n };\n\n // Default icon\n const defaultIcon = (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n );\n\n return icons[name] || defaultIcon;\n};\n\nexport default WidgetHeader;\n"],"mappings":"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"}
|
|
@@ -231,9 +231,9 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
231
231
|
p-1.5 rounded-md
|
|
232
232
|
text-text-secondary
|
|
233
233
|
hover:text-text-primary
|
|
234
|
-
hover:bg-bg-
|
|
234
|
+
hover:bg-bg-sunken
|
|
235
235
|
transition-colors
|
|
236
|
-
${h ? "bg-bg-
|
|
236
|
+
${h ? "bg-bg-sunken" : ""}
|
|
237
237
|
`,
|
|
238
238
|
title: "Widget options",
|
|
239
239
|
"aria-haspopup": "menu",
|
|
@@ -281,7 +281,7 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
281
281
|
w-full flex items-center gap-2
|
|
282
282
|
px-3 py-2 text-left text-sm
|
|
283
283
|
transition-colors
|
|
284
|
-
${e.disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-bg-
|
|
284
|
+
${e.disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-bg-sunken"}
|
|
285
285
|
${e.destructive ? "text-state-error hover:text-state-error" : "text-text-primary"}
|
|
286
286
|
`,
|
|
287
287
|
role: "menuitem",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetMenu.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetMenu.tsx"],"sourcesContent":["/**\n * WidgetMenu Component\n *\n * Dropdown menu with widget actions (edit, duplicate, delete, etc.)\n */\n\nimport { type FC, type ReactElement, useState, useRef, useEffect, useCallback, memo } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { Widget } from '../../types';\nimport { useWidgetStore } from '../../store';\nimport { useWidgetOperations } from '../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetMenuProps {\n /** The widget data */\n widget: Widget;\n /** Whether in edit mode */\n isEditMode?: boolean;\n /** Callback to refresh widget */\n onRefresh?: () => void;\n /** Callback to export as CSV */\n onExportCSV?: () => void;\n /** Callback to export as PNG */\n onExportPNG?: () => void;\n}\n\ninterface MenuItem {\n id: string;\n label: string;\n icon: ReactElement;\n action: () => void;\n disabled?: boolean;\n destructive?: boolean;\n divider?: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetMenu: FC<WidgetMenuProps> = memo(function WidgetMenu({\n widget,\n isEditMode,\n onRefresh,\n onExportCSV,\n onExportPNG,\n}) {\n const [isOpen, setIsOpen] = useState(false);\n const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });\n const menuRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n // Store actions\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n const copyWidget = useWidgetStore((state) => state.copyWidget);\n const cutWidget = useWidgetStore((state) => state.cutWidget);\n const removeWidget = useWidgetStore((state) => state.removeWidget);\n const openConfigPanel = useWidgetStore((state) => state.openConfigPanel);\n\n // API operations - skipFetch since parent page fetches\n const {\n duplicateWidget: duplicateWidgetApi,\n deleteWidget: deleteWidgetApi,\n refreshWidgetData,\n } = useWidgetOperations(widget.pageId, widget.dashboardId, { skipFetch: true });\n\n // Close menu when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n menuRef.current &&\n !menuRef.current.contains(event.target as Node) &&\n buttonRef.current &&\n !buttonRef.current.contains(event.target as Node)\n ) {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [isOpen]);\n\n // Close menu on escape\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n buttonRef.current?.focus();\n }\n };\n\n if (isOpen) {\n document.addEventListener('keydown', handleEscape);\n }\n\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [isOpen]);\n\n // Toggle menu with collision detection\n const toggleMenu = useCallback((event: React.MouseEvent) => {\n event.stopPropagation();\n setIsOpen((prev) => {\n if (!prev && buttonRef.current) {\n // Calculate position when opening with collision detection\n const rect = buttonRef.current.getBoundingClientRect();\n const menuWidth = 160;\n const menuHeight = 320; // Approximate max height of menu\n const collisionPadding = 10;\n\n // Calculate initial position\n let top = rect.bottom + 4;\n let left = rect.right - menuWidth;\n\n // Check right overflow - ensure menu doesn't go past viewport right edge\n if (left + menuWidth > window.innerWidth - collisionPadding) {\n left = window.innerWidth - menuWidth - collisionPadding;\n }\n\n // Check left overflow - ensure menu doesn't go past viewport left edge\n if (left < collisionPadding) {\n left = collisionPadding;\n }\n\n // Check bottom overflow - flip to top if needed\n if (top + menuHeight > window.innerHeight - collisionPadding) {\n top = rect.top - menuHeight - 4;\n // If still overflows at top, position at top of viewport\n if (top < collisionPadding) {\n top = collisionPadding;\n }\n }\n\n setMenuPosition({\n top: top + window.scrollY,\n left: left + window.scrollX,\n });\n }\n return !prev;\n });\n }, []);\n\n // Menu items\n const menuItems: MenuItem[] = [\n {\n id: 'edit',\n label: 'Edit Widget',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n ),\n action: () => {\n selectWidget(widget.id);\n openConfigPanel();\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'refresh',\n label: 'Refresh Data',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n if (onRefresh) {\n onRefresh();\n } else {\n await refreshWidgetData(widget.id);\n }\n },\n disabled: false,\n },\n {\n id: 'exportCSV',\n label: 'Export CSV',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportCSV?.();\n },\n disabled: !onExportCSV,\n },\n {\n id: 'exportPNG',\n label: 'Export PNG',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportPNG?.();\n },\n disabled: !onExportPNG,\n },\n {\n id: 'divider1',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'copy',\n label: 'Copy',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n copyWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'cut',\n label: 'Cut',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z\"\n />\n </svg>\n ),\n action: () => {\n cutWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'duplicate',\n label: 'Duplicate',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n // Calculate new position (offset from original)\n const newPosition = {\n x: (widget.positionX ?? 0) + 1,\n y: (widget.positionY ?? 0) + 1,\n width: widget.positionWidth ?? 4,\n height: widget.positionHeight ?? 4,\n };\n await duplicateWidgetApi(widget.id, newPosition, `${widget.title} (Copy)`);\n },\n disabled: !isEditMode,\n },\n {\n id: 'divider2',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'delete',\n label: 'Delete',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n ),\n action: async () => {\n if (window.confirm('Are you sure you want to delete this widget?')) {\n setIsOpen(false);\n const success = await deleteWidgetApi(widget.id);\n if (!success) {\n // Fallback to local store removal if API fails\n removeWidget(widget.id);\n }\n } else {\n setIsOpen(false);\n }\n },\n disabled: !isEditMode,\n destructive: true,\n },\n ];\n\n return (\n <div className=\"relative\">\n {/* Menu Button */}\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={toggleMenu}\n className={`\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-muted\n transition-colors\n ${isOpen ? 'bg-bg-muted' : ''}\n `}\n title=\"Widget options\"\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n >\n <svg className=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"5\" r=\"2\" />\n <circle cx=\"12\" cy=\"12\" r=\"2\" />\n <circle cx=\"12\" cy=\"19\" r=\"2\" />\n </svg>\n </button>\n\n {/* Dropdown Menu - Rendered via Portal to avoid overflow clipping */}\n {isOpen &&\n createPortal(\n <div\n ref={menuRef}\n style={{\n position: 'fixed',\n top: menuPosition.top,\n left: menuPosition.left,\n zIndex: 99999,\n }}\n className=\"\n min-w-[160px]\n py-1\n bg-bg-surface\n border border-border-default\n rounded-lg shadow-lg\n \"\n role=\"menu\"\n aria-orientation=\"vertical\"\n >\n {menuItems.map((item) => {\n if (item.divider) {\n return <div key={item.id} className=\"my-1 border-t border-border-default\" />;\n }\n\n return (\n <button\n key={item.id}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n item.action();\n }}\n disabled={item.disabled}\n className={`\n w-full flex items-center gap-2\n px-3 py-2 text-left text-sm\n transition-colors\n ${item.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-bg-muted'}\n ${item.destructive ? 'text-state-error hover:text-state-error' : 'text-text-primary'}\n `}\n role=\"menuitem\"\n >\n {item.icon}\n <span>{item.label}</span>\n </button>\n );\n })}\n </div>,\n document.body\n )}\n </div>\n );\n});\n\nexport default WidgetMenu;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WidgetMenu.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetMenu.tsx"],"sourcesContent":["/**\n * WidgetMenu Component\n *\n * Dropdown menu with widget actions (edit, duplicate, delete, etc.)\n */\n\nimport { type FC, type ReactElement, useState, useRef, useEffect, useCallback, memo } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { Widget } from '../../types';\nimport { useWidgetStore } from '../../store';\nimport { useWidgetOperations } from '../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetMenuProps {\n /** The widget data */\n widget: Widget;\n /** Whether in edit mode */\n isEditMode?: boolean;\n /** Callback to refresh widget */\n onRefresh?: () => void;\n /** Callback to export as CSV */\n onExportCSV?: () => void;\n /** Callback to export as PNG */\n onExportPNG?: () => void;\n}\n\ninterface MenuItem {\n id: string;\n label: string;\n icon: ReactElement;\n action: () => void;\n disabled?: boolean;\n destructive?: boolean;\n divider?: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetMenu: FC<WidgetMenuProps> = memo(function WidgetMenu({\n widget,\n isEditMode,\n onRefresh,\n onExportCSV,\n onExportPNG,\n}) {\n const [isOpen, setIsOpen] = useState(false);\n const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });\n const menuRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n // Store actions\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n const copyWidget = useWidgetStore((state) => state.copyWidget);\n const cutWidget = useWidgetStore((state) => state.cutWidget);\n const removeWidget = useWidgetStore((state) => state.removeWidget);\n const openConfigPanel = useWidgetStore((state) => state.openConfigPanel);\n\n // API operations - skipFetch since parent page fetches\n const {\n duplicateWidget: duplicateWidgetApi,\n deleteWidget: deleteWidgetApi,\n refreshWidgetData,\n } = useWidgetOperations(widget.pageId, widget.dashboardId, { skipFetch: true });\n\n // Close menu when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n menuRef.current &&\n !menuRef.current.contains(event.target as Node) &&\n buttonRef.current &&\n !buttonRef.current.contains(event.target as Node)\n ) {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [isOpen]);\n\n // Close menu on escape\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n buttonRef.current?.focus();\n }\n };\n\n if (isOpen) {\n document.addEventListener('keydown', handleEscape);\n }\n\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [isOpen]);\n\n // Toggle menu with collision detection\n const toggleMenu = useCallback((event: React.MouseEvent) => {\n event.stopPropagation();\n setIsOpen((prev) => {\n if (!prev && buttonRef.current) {\n // Calculate position when opening with collision detection\n const rect = buttonRef.current.getBoundingClientRect();\n const menuWidth = 160;\n const menuHeight = 320; // Approximate max height of menu\n const collisionPadding = 10;\n\n // Calculate initial position\n let top = rect.bottom + 4;\n let left = rect.right - menuWidth;\n\n // Check right overflow - ensure menu doesn't go past viewport right edge\n if (left + menuWidth > window.innerWidth - collisionPadding) {\n left = window.innerWidth - menuWidth - collisionPadding;\n }\n\n // Check left overflow - ensure menu doesn't go past viewport left edge\n if (left < collisionPadding) {\n left = collisionPadding;\n }\n\n // Check bottom overflow - flip to top if needed\n if (top + menuHeight > window.innerHeight - collisionPadding) {\n top = rect.top - menuHeight - 4;\n // If still overflows at top, position at top of viewport\n if (top < collisionPadding) {\n top = collisionPadding;\n }\n }\n\n setMenuPosition({\n top: top + window.scrollY,\n left: left + window.scrollX,\n });\n }\n return !prev;\n });\n }, []);\n\n // Menu items\n const menuItems: MenuItem[] = [\n {\n id: 'edit',\n label: 'Edit Widget',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n ),\n action: () => {\n selectWidget(widget.id);\n openConfigPanel();\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'refresh',\n label: 'Refresh Data',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n if (onRefresh) {\n onRefresh();\n } else {\n await refreshWidgetData(widget.id);\n }\n },\n disabled: false,\n },\n {\n id: 'exportCSV',\n label: 'Export CSV',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportCSV?.();\n },\n disabled: !onExportCSV,\n },\n {\n id: 'exportPNG',\n label: 'Export PNG',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n setIsOpen(false);\n onExportPNG?.();\n },\n disabled: !onExportPNG,\n },\n {\n id: 'divider1',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'copy',\n label: 'Copy',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n ),\n action: () => {\n copyWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'cut',\n label: 'Cut',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z\"\n />\n </svg>\n ),\n action: () => {\n cutWidget(widget.id);\n setIsOpen(false);\n },\n disabled: !isEditMode,\n },\n {\n id: 'duplicate',\n label: 'Duplicate',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2\"\n />\n </svg>\n ),\n action: async () => {\n setIsOpen(false);\n // Calculate new position (offset from original)\n const newPosition = {\n x: (widget.positionX ?? 0) + 1,\n y: (widget.positionY ?? 0) + 1,\n width: widget.positionWidth ?? 4,\n height: widget.positionHeight ?? 4,\n };\n await duplicateWidgetApi(widget.id, newPosition, `${widget.title} (Copy)`);\n },\n disabled: !isEditMode,\n },\n {\n id: 'divider2',\n label: '',\n icon: <></>,\n action: () => {},\n divider: true,\n },\n {\n id: 'delete',\n label: 'Delete',\n icon: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className=\"w-4 h-4\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n ),\n action: async () => {\n if (window.confirm('Are you sure you want to delete this widget?')) {\n setIsOpen(false);\n const success = await deleteWidgetApi(widget.id);\n if (!success) {\n // Fallback to local store removal if API fails\n removeWidget(widget.id);\n }\n } else {\n setIsOpen(false);\n }\n },\n disabled: !isEditMode,\n destructive: true,\n },\n ];\n\n return (\n <div className=\"relative\">\n {/* Menu Button */}\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={toggleMenu}\n className={`\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n ${isOpen ? 'bg-bg-sunken' : ''}\n `}\n title=\"Widget options\"\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n >\n <svg className=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"5\" r=\"2\" />\n <circle cx=\"12\" cy=\"12\" r=\"2\" />\n <circle cx=\"12\" cy=\"19\" r=\"2\" />\n </svg>\n </button>\n\n {/* Dropdown Menu - Rendered via Portal to avoid overflow clipping */}\n {isOpen &&\n createPortal(\n <div\n ref={menuRef}\n style={{\n position: 'fixed',\n top: menuPosition.top,\n left: menuPosition.left,\n zIndex: 99999,\n }}\n className=\"\n min-w-[160px]\n py-1\n bg-bg-surface\n border border-border-default\n rounded-lg shadow-lg\n \"\n role=\"menu\"\n aria-orientation=\"vertical\"\n >\n {menuItems.map((item) => {\n if (item.divider) {\n return <div key={item.id} className=\"my-1 border-t border-border-default\" />;\n }\n\n return (\n <button\n key={item.id}\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n item.action();\n }}\n disabled={item.disabled}\n className={`\n w-full flex items-center gap-2\n px-3 py-2 text-left text-sm\n transition-colors\n ${item.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-bg-sunken'}\n ${item.destructive ? 'text-state-error hover:text-state-error' : 'text-text-primary'}\n `}\n role=\"menuitem\"\n >\n {item.icon}\n <span>{item.label}</span>\n </button>\n );\n })}\n </div>,\n document.body\n )}\n </div>\n );\n});\n\nexport default WidgetMenu;\n"],"mappings":"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"}
|