@burdenoff/microfe-bigconsole 2026.710.3 → 2026.711.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-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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"}
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 * Backend widget catalog (from `getWidgetTypes` via useWidgetCatalog). Carries\n * registry-driven store types under the \"Installed\" category (BOFF-2858);\n * forwarded to the type selector so the Add Widget modal surfaces installed\n * store widgets like the sidebar palette does.\n */\n definitions?: 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 definitions,\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 definitions={definitions}\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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"}
@@ -84,7 +84,22 @@ var P = [
84
84
  }, [f]), v = a(() => {
85
85
  let e = c || I, t = {}, n = F[h];
86
86
  for (let r of e) {
87
- let e = r === "Installed" ? d : _[r] ?? [];
87
+ let e;
88
+ if (r === "Installed") {
89
+ let t = /* @__PURE__ */ new Map();
90
+ for (let e of d) t.set(e.name.toLowerCase(), e);
91
+ for (let e of _.Installed ?? []) {
92
+ let n = e.name.toLowerCase(), r = t.get(n);
93
+ t.set(n, r ? {
94
+ ...e,
95
+ defaultConfig: {
96
+ ...r.defaultConfig,
97
+ ...e.defaultConfig
98
+ }
99
+ } : e);
100
+ }
101
+ e = Array.from(t.values());
102
+ } else e = _[r] ?? [];
88
103
  if (p.trim()) {
89
104
  let t = p.toLowerCase();
90
105
  e = e.filter((e) => e.name.toLowerCase().includes(t) || e.description.toLowerCase().includes(t));
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetTypeSelector.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetTypeSelector.tsx"],"sourcesContent":["/**\n * WidgetTypeSelector Component\n *\n * Grid of available widget types for selection.\n * v2.0: Added renderer filter tabs for filtering by renderer type.\n * Uses fe-libs shared components for consistent styling.\n */\n\nimport { type FC, memo, useState, useMemo, useCallback } from 'react';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport type { WidgetType, WidgetCategory, RendererType } from '../../types';\nimport { getAllWidgetDefinitions, getWidgetKey, WIDGET_CATEGORY_LABELS, type WidgetDefinition } from './WidgetRegistry';\nimport {\n TrendingUp,\n BarChart3,\n Filter,\n Table,\n Grid3x3,\n Gauge,\n Activity,\n List,\n FileText,\n Type,\n ExternalLink,\n MapPin,\n Calendar,\n Columns,\n GitBranch,\n Puzzle,\n LayoutDashboard,\n Search,\n Layers,\n CreditCard,\n MessageSquare,\n Code,\n type LucideIcon,\n} from 'lucide-react';\n\n// ============================================================================\n// Icon Mapping - Maps string icon names to Lucide components\n// ============================================================================\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n TrendingUp,\n BarChart3,\n Filter,\n Table,\n Grid3x3,\n Gauge,\n Activity,\n List,\n FileText,\n Type,\n ExternalLink,\n MapPin,\n Calendar,\n Columns,\n GitBranch,\n Puzzle,\n LayoutDashboard,\n};\n\nfunction getIconComponent(iconName: string): LucideIcon {\n return ICON_MAP[iconName] || Puzzle;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetTypeSelectorProps {\n selectedType?: WidgetType;\n onSelect: (type: WidgetType, definition?: WidgetDefinition) => void;\n categories?: WidgetCategory[];\n onRendererSelect?: (renderer: RendererType) => void;\n selectedRenderer?: RendererType;\n /** Installed widgets from the store (fetched by parent) */\n installedWidgets?: WidgetDefinition[];\n /**\n * Built-in widget catalog (from the backend `getWidgetTypes`, fetched by the\n * parent via useWidgetCatalog). When omitted, falls back to the bundled\n * static registry.\n */\n definitions?: WidgetDefinition[];\n}\n\ntype RendererFilter = RendererType | 'ALL';\n\ninterface RendererFilterOption {\n id: RendererFilter;\n label: string;\n icon: LucideIcon;\n}\n\nconst RENDERER_FILTERS: RendererFilterOption[] = [\n { id: 'ALL', label: 'All', icon: Layers },\n { id: 'ADAPTIVE_CARD', label: 'Adaptive Card', icon: CreditCard },\n { id: 'BLOCKKIT', label: 'Block Kit', icon: MessageSquare },\n { id: 'CUSTOM', label: 'Custom', icon: Code },\n];\n\n// Widget types that support each renderer (for filtering)\nconst RENDERER_WIDGET_TYPES: Record<RendererFilter, WidgetType[] | 'all'> = {\n ALL: 'all',\n BIGCONSOLE: 'all',\n ADAPTIVE_CARD: ['metric_card', 'kpi_card_comparison', 'text', 'list', 'table', 'form', 'custom'],\n BLOCKKIT: ['metric_card', 'kpi_card_comparison', 'text', 'list', 'table', 'custom'],\n CUSTOM: ['custom'],\n};\n\nconst ALL_CATEGORIES: WidgetCategory[] = ['KPI', 'Visualization', 'Data', 'Content', 'Temporal', 'Custom', 'Installed'];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetTypeSelector: FC<WidgetTypeSelectorProps> = memo(function WidgetTypeSelector({\n selectedType,\n onSelect,\n categories,\n onRendererSelect,\n selectedRenderer,\n installedWidgets = [],\n definitions,\n}) {\n const [searchQuery, setSearchQuery] = useState('');\n const [rendererFilter, setRendererFilter] = useState<RendererFilter>(selectedRenderer || 'ALL');\n\n // Group the provided catalog by category; fall back to the bundled registry\n // when the parent doesn't supply a backend-sourced catalog.\n const definitionsByCategory = useMemo(() => {\n const source = definitions ?? getAllWidgetDefinitions();\n const grouped: Partial<Record<WidgetCategory, WidgetDefinition[]>> = {};\n for (const def of source) {\n (grouped[def.category] ??= []).push(def);\n }\n return grouped;\n }, [definitions]);\n\n const widgetsByCategory = useMemo(() => {\n const cats = categories || ALL_CATEGORIES;\n const grouped: Record<string, WidgetDefinition[]> = {};\n\n // Get allowed widget types for current renderer filter\n const allowedTypes = RENDERER_WIDGET_TYPES[rendererFilter];\n\n for (const cat of cats) {\n let widgets = cat === 'Installed' ? installedWidgets : (definitionsByCategory[cat] ?? []);\n\n // Filter by search query\n if (searchQuery.trim()) {\n const query = searchQuery.toLowerCase();\n widgets = widgets.filter(\n (w) => w.name.toLowerCase().includes(query) || w.description.toLowerCase().includes(query)\n );\n }\n\n // Filter by renderer\n if (allowedTypes !== 'all') {\n widgets = widgets.filter((w) => allowedTypes.includes(w.type));\n }\n\n if (widgets.length > 0) {\n grouped[cat] = widgets;\n }\n }\n\n return grouped;\n }, [searchQuery, categories, rendererFilter, installedWidgets, definitionsByCategory]);\n\n const handleRendererFilterChange = useCallback(\n (filter: RendererFilter) => {\n setRendererFilter(filter);\n if (onRendererSelect && filter !== 'ALL') {\n onRendererSelect(filter as RendererType);\n }\n },\n [onRendererSelect]\n );\n\n const handleSelect = useCallback(\n (type: WidgetType, definition?: WidgetDefinition) => {\n onSelect(type, definition);\n // If a specific renderer is selected, notify parent\n if (rendererFilter !== 'ALL' && onRendererSelect) {\n onRendererSelect(rendererFilter as RendererType);\n }\n },\n [onSelect, rendererFilter, onRendererSelect]\n );\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', height: '100%', maxHeight: '500px', maxWidth: '500px' }}>\n {/* Renderer Filter Tabs */}\n <div style={{ padding: '8px 12px', borderBottom: '1px solid var(--color-border)' }}>\n <div\n style={{\n display: 'flex',\n gap: '4px',\n backgroundColor: 'var(--color-bg-muted)',\n padding: '3px',\n borderRadius: '6px',\n }}\n >\n {RENDERER_FILTERS.map((filter) => {\n const Icon = filter.icon;\n const isActive = rendererFilter === filter.id;\n return (\n <button\n key={filter.id}\n type=\"button\"\n onClick={() => handleRendererFilterChange(filter.id)}\n style={{\n flex: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '4px',\n padding: '6px 8px',\n borderRadius: '4px',\n border: 'none',\n fontSize: '11px',\n fontWeight: 500,\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n backgroundColor: isActive ? 'var(--color-bg-surface, white)' : 'transparent',\n color: isActive ? 'var(--color-primary)' : 'var(--color-text-secondary)',\n boxShadow: isActive ? '0 1px 2px rgba(0,0,0,0.05)' : 'none',\n }}\n >\n <Icon style={{ width: '12px', height: '12px' }} />\n <span>{filter.label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Search */}\n <div style={{ padding: '8px 12px', borderBottom: '1px solid var(--color-border)' }}>\n <div style={{ position: 'relative' }}>\n <Search\n style={{\n position: 'absolute',\n left: '10px',\n top: '50%',\n transform: 'translateY(-50%)',\n width: '14px',\n height: '14px',\n color: 'var(--color-text-secondary)',\n }}\n />\n <Input\n type=\"text\"\n placeholder=\"Search widgets...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n style={{ paddingLeft: '32px', height: '32px', fontSize: '13px' }}\n />\n </div>\n </div>\n\n {/* Categories and widgets - with visible scrollbar */}\n <div\n style={{\n flex: 1,\n overflowY: 'auto',\n padding: '12px',\n backgroundColor: 'var(--color-bg-sunken)',\n maxHeight: '350px',\n minHeight: '200px',\n }}\n >\n {Object.entries(widgetsByCategory).map(([category, widgets]) => (\n <div key={category} style={{ marginBottom: '16px' }}>\n <h3\n style={{\n fontSize: '11px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--color-text-secondary)',\n marginBottom: '8px',\n paddingLeft: '4px',\n }}\n >\n {category === 'search'\n ? `Search Results (${widgets.length})`\n : WIDGET_CATEGORY_LABELS[category as WidgetCategory]}\n </h3>\n\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(2, 1fr)',\n gap: '8px',\n }}\n >\n {widgets.map((widget) => {\n const Icon = getIconComponent(widget.icon);\n const widgetKey = getWidgetKey(widget);\n // For installed widgets (all typed as 'custom'), avoid highlighting\n // every installed widget when one is selected.\n const isSelected = widget.category !== 'Installed' && selectedType === widget.type;\n\n return (\n <div\n key={widgetKey}\n onClick={() => handleSelect(widget.type, widget)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '10px',\n padding: '10px 12px',\n borderRadius: '8px',\n border: isSelected ? '2px solid var(--color-action-primary-bg)' : '1px solid var(--color-border)',\n backgroundColor: isSelected ? 'var(--color-action-primary-bg)' : 'var(--color-bg-surface)',\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n boxShadow: isSelected ? 'var(--shadow-elevation-2)' : 'var(--shadow-elevation-1)',\n }}\n onMouseEnter={(e) => {\n if (!isSelected) {\n e.currentTarget.style.borderColor = 'var(--color-action-primary-bg)';\n e.currentTarget.style.backgroundColor = 'var(--color-bg-sunken)';\n e.currentTarget.style.boxShadow = 'var(--shadow-elevation-2)';\n }\n }}\n onMouseLeave={(e) => {\n if (!isSelected) {\n e.currentTarget.style.borderColor = 'var(--color-border)';\n e.currentTarget.style.backgroundColor = 'var(--color-bg-surface)';\n e.currentTarget.style.boxShadow = 'var(--shadow-elevation-1)';\n }\n }}\n >\n <div\n style={{\n width: '32px',\n height: '32px',\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n backgroundColor: isSelected ? 'var(--color-action-primary-bg)' : 'var(--color-bg-muted)',\n color: isSelected ? 'var(--color-action-primary-text)' : 'var(--color-text-secondary)',\n }}\n >\n <Icon style={{ width: '16px', height: '16px' }} />\n </div>\n <span\n style={{\n fontSize: '13px',\n fontWeight: 500,\n color: isSelected ? 'var(--color-action-primary-text)' : 'var(--color-text-primary)',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {widget.name}\n </span>\n </div>\n );\n })}\n </div>\n </div>\n ))}\n\n {Object.keys(widgetsByCategory).length === 0 && (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '32px',\n color: 'var(--color-text-secondary)',\n }}\n >\n <Search style={{ width: '32px', height: '32px', marginBottom: '12px', opacity: 0.5 }} />\n <p style={{ fontSize: '13px' }}>No widgets found</p>\n </div>\n )}\n </div>\n </div>\n );\n});\n\nexport default WidgetTypeSelector;\n"],"mappings":"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"}
1
+ {"version":3,"file":"WidgetTypeSelector.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetTypeSelector.tsx"],"sourcesContent":["/**\n * WidgetTypeSelector Component\n *\n * Grid of available widget types for selection.\n * v2.0: Added renderer filter tabs for filtering by renderer type.\n * Uses fe-libs shared components for consistent styling.\n */\n\nimport { type FC, memo, useState, useMemo, useCallback } from 'react';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport type { WidgetType, WidgetCategory, RendererType } from '../../types';\nimport { getAllWidgetDefinitions, getWidgetKey, WIDGET_CATEGORY_LABELS, type WidgetDefinition } from './WidgetRegistry';\nimport {\n TrendingUp,\n BarChart3,\n Filter,\n Table,\n Grid3x3,\n Gauge,\n Activity,\n List,\n FileText,\n Type,\n ExternalLink,\n MapPin,\n Calendar,\n Columns,\n GitBranch,\n Puzzle,\n LayoutDashboard,\n Search,\n Layers,\n CreditCard,\n MessageSquare,\n Code,\n type LucideIcon,\n} from 'lucide-react';\n\n// ============================================================================\n// Icon Mapping - Maps string icon names to Lucide components\n// ============================================================================\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n TrendingUp,\n BarChart3,\n Filter,\n Table,\n Grid3x3,\n Gauge,\n Activity,\n List,\n FileText,\n Type,\n ExternalLink,\n MapPin,\n Calendar,\n Columns,\n GitBranch,\n Puzzle,\n LayoutDashboard,\n};\n\nfunction getIconComponent(iconName: string): LucideIcon {\n return ICON_MAP[iconName] || Puzzle;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetTypeSelectorProps {\n selectedType?: WidgetType;\n onSelect: (type: WidgetType, definition?: WidgetDefinition) => void;\n categories?: WidgetCategory[];\n onRendererSelect?: (renderer: RendererType) => void;\n selectedRenderer?: RendererType;\n /** Installed widgets from the store (fetched by parent) */\n installedWidgets?: WidgetDefinition[];\n /**\n * Built-in widget catalog (from the backend `getWidgetTypes`, fetched by the\n * parent via useWidgetCatalog). When omitted, falls back to the bundled\n * static registry.\n */\n definitions?: WidgetDefinition[];\n}\n\ntype RendererFilter = RendererType | 'ALL';\n\ninterface RendererFilterOption {\n id: RendererFilter;\n label: string;\n icon: LucideIcon;\n}\n\nconst RENDERER_FILTERS: RendererFilterOption[] = [\n { id: 'ALL', label: 'All', icon: Layers },\n { id: 'ADAPTIVE_CARD', label: 'Adaptive Card', icon: CreditCard },\n { id: 'BLOCKKIT', label: 'Block Kit', icon: MessageSquare },\n { id: 'CUSTOM', label: 'Custom', icon: Code },\n];\n\n// Widget types that support each renderer (for filtering)\nconst RENDERER_WIDGET_TYPES: Record<RendererFilter, WidgetType[] | 'all'> = {\n ALL: 'all',\n BIGCONSOLE: 'all',\n ADAPTIVE_CARD: ['metric_card', 'kpi_card_comparison', 'text', 'list', 'table', 'form', 'custom'],\n BLOCKKIT: ['metric_card', 'kpi_card_comparison', 'text', 'list', 'table', 'custom'],\n CUSTOM: ['custom'],\n};\n\nconst ALL_CATEGORIES: WidgetCategory[] = ['KPI', 'Visualization', 'Data', 'Content', 'Temporal', 'Custom', 'Installed'];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetTypeSelector: FC<WidgetTypeSelectorProps> = memo(function WidgetTypeSelector({\n selectedType,\n onSelect,\n categories,\n onRendererSelect,\n selectedRenderer,\n installedWidgets = [],\n definitions,\n}) {\n const [searchQuery, setSearchQuery] = useState('');\n const [rendererFilter, setRendererFilter] = useState<RendererFilter>(selectedRenderer || 'ALL');\n\n // Group the provided catalog by category; fall back to the bundled registry\n // when the parent doesn't supply a backend-sourced catalog.\n const definitionsByCategory = useMemo(() => {\n const source = definitions ?? getAllWidgetDefinitions();\n const grouped: Partial<Record<WidgetCategory, WidgetDefinition[]>> = {};\n for (const def of source) {\n (grouped[def.category] ??= []).push(def);\n }\n return grouped;\n }, [definitions]);\n\n const widgetsByCategory = useMemo(() => {\n const cats = categories || ALL_CATEGORIES;\n const grouped: Record<string, WidgetDefinition[]> = {};\n\n // Get allowed widget types for current renderer filter\n const allowedTypes = RENDERER_WIDGET_TYPES[rendererFilter];\n\n for (const cat of cats) {\n let widgets: WidgetDefinition[];\n if (cat === 'Installed') {\n // Mirror WidgetPalette: the \"Installed\" group merges the registry-driven\n // backend catalog store types (getWidgetTypes → `definitions` under this\n // category, BOFF-2858) with the legacy workspaceInstalledApps listing\n // (`installedWidgets`), deduped by name so a widget present in both shows\n // once with the richer catalog definition.\n const byName = new Map<string, WidgetDefinition>();\n for (const w of installedWidgets) byName.set(w.name.toLowerCase(), w);\n for (const w of definitionsByCategory['Installed'] ?? []) {\n const key = w.name.toLowerCase();\n const legacy = byName.get(key);\n // Catalog entry wins for metadata (label/description/renderer/widgetTypeKey),\n // but MERGE defaultConfig so the legacy workspaceInstalledApps fields survive —\n // notably `componentId`, which the current custom render path resolves against\n // and the getWidgetTypes store entry lacks (widgetTypeKey rendering isn't wired\n // yet). A store widget present in both sources thus stays renderable.\n byName.set(key, legacy ? { ...w, defaultConfig: { ...legacy.defaultConfig, ...w.defaultConfig } } : w);\n }\n widgets = Array.from(byName.values());\n } else {\n widgets = definitionsByCategory[cat] ?? [];\n }\n\n // Filter by search query\n if (searchQuery.trim()) {\n const query = searchQuery.toLowerCase();\n widgets = widgets.filter(\n (w) => w.name.toLowerCase().includes(query) || w.description.toLowerCase().includes(query)\n );\n }\n\n // Filter by renderer\n if (allowedTypes !== 'all') {\n widgets = widgets.filter((w) => allowedTypes.includes(w.type));\n }\n\n if (widgets.length > 0) {\n grouped[cat] = widgets;\n }\n }\n\n return grouped;\n }, [searchQuery, categories, rendererFilter, installedWidgets, definitionsByCategory]);\n\n const handleRendererFilterChange = useCallback(\n (filter: RendererFilter) => {\n setRendererFilter(filter);\n if (onRendererSelect && filter !== 'ALL') {\n onRendererSelect(filter as RendererType);\n }\n },\n [onRendererSelect]\n );\n\n const handleSelect = useCallback(\n (type: WidgetType, definition?: WidgetDefinition) => {\n onSelect(type, definition);\n // If a specific renderer is selected, notify parent\n if (rendererFilter !== 'ALL' && onRendererSelect) {\n onRendererSelect(rendererFilter as RendererType);\n }\n },\n [onSelect, rendererFilter, onRendererSelect]\n );\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', height: '100%', maxHeight: '500px', maxWidth: '500px' }}>\n {/* Renderer Filter Tabs */}\n <div style={{ padding: '8px 12px', borderBottom: '1px solid var(--color-border)' }}>\n <div\n style={{\n display: 'flex',\n gap: '4px',\n backgroundColor: 'var(--color-bg-muted)',\n padding: '3px',\n borderRadius: '6px',\n }}\n >\n {RENDERER_FILTERS.map((filter) => {\n const Icon = filter.icon;\n const isActive = rendererFilter === filter.id;\n return (\n <button\n key={filter.id}\n type=\"button\"\n onClick={() => handleRendererFilterChange(filter.id)}\n style={{\n flex: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '4px',\n padding: '6px 8px',\n borderRadius: '4px',\n border: 'none',\n fontSize: '11px',\n fontWeight: 500,\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n backgroundColor: isActive ? 'var(--color-bg-surface, white)' : 'transparent',\n color: isActive ? 'var(--color-primary)' : 'var(--color-text-secondary)',\n boxShadow: isActive ? '0 1px 2px rgba(0,0,0,0.05)' : 'none',\n }}\n >\n <Icon style={{ width: '12px', height: '12px' }} />\n <span>{filter.label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Search */}\n <div style={{ padding: '8px 12px', borderBottom: '1px solid var(--color-border)' }}>\n <div style={{ position: 'relative' }}>\n <Search\n style={{\n position: 'absolute',\n left: '10px',\n top: '50%',\n transform: 'translateY(-50%)',\n width: '14px',\n height: '14px',\n color: 'var(--color-text-secondary)',\n }}\n />\n <Input\n type=\"text\"\n placeholder=\"Search widgets...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n style={{ paddingLeft: '32px', height: '32px', fontSize: '13px' }}\n />\n </div>\n </div>\n\n {/* Categories and widgets - with visible scrollbar */}\n <div\n style={{\n flex: 1,\n overflowY: 'auto',\n padding: '12px',\n backgroundColor: 'var(--color-bg-sunken)',\n maxHeight: '350px',\n minHeight: '200px',\n }}\n >\n {Object.entries(widgetsByCategory).map(([category, widgets]) => (\n <div key={category} style={{ marginBottom: '16px' }}>\n <h3\n style={{\n fontSize: '11px',\n fontWeight: 600,\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n color: 'var(--color-text-secondary)',\n marginBottom: '8px',\n paddingLeft: '4px',\n }}\n >\n {category === 'search'\n ? `Search Results (${widgets.length})`\n : WIDGET_CATEGORY_LABELS[category as WidgetCategory]}\n </h3>\n\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(2, 1fr)',\n gap: '8px',\n }}\n >\n {widgets.map((widget) => {\n const Icon = getIconComponent(widget.icon);\n const widgetKey = getWidgetKey(widget);\n // For installed widgets (all typed as 'custom'), avoid highlighting\n // every installed widget when one is selected.\n const isSelected = widget.category !== 'Installed' && selectedType === widget.type;\n\n return (\n <div\n key={widgetKey}\n onClick={() => handleSelect(widget.type, widget)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '10px',\n padding: '10px 12px',\n borderRadius: '8px',\n border: isSelected ? '2px solid var(--color-action-primary-bg)' : '1px solid var(--color-border)',\n backgroundColor: isSelected ? 'var(--color-action-primary-bg)' : 'var(--color-bg-surface)',\n cursor: 'pointer',\n transition: 'all 0.15s ease',\n boxShadow: isSelected ? 'var(--shadow-elevation-2)' : 'var(--shadow-elevation-1)',\n }}\n onMouseEnter={(e) => {\n if (!isSelected) {\n e.currentTarget.style.borderColor = 'var(--color-action-primary-bg)';\n e.currentTarget.style.backgroundColor = 'var(--color-bg-sunken)';\n e.currentTarget.style.boxShadow = 'var(--shadow-elevation-2)';\n }\n }}\n onMouseLeave={(e) => {\n if (!isSelected) {\n e.currentTarget.style.borderColor = 'var(--color-border)';\n e.currentTarget.style.backgroundColor = 'var(--color-bg-surface)';\n e.currentTarget.style.boxShadow = 'var(--shadow-elevation-1)';\n }\n }}\n >\n <div\n style={{\n width: '32px',\n height: '32px',\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n backgroundColor: isSelected ? 'var(--color-action-primary-bg)' : 'var(--color-bg-muted)',\n color: isSelected ? 'var(--color-action-primary-text)' : 'var(--color-text-secondary)',\n }}\n >\n <Icon style={{ width: '16px', height: '16px' }} />\n </div>\n <span\n style={{\n fontSize: '13px',\n fontWeight: 500,\n color: isSelected ? 'var(--color-action-primary-text)' : 'var(--color-text-primary)',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {widget.name}\n </span>\n </div>\n );\n })}\n </div>\n </div>\n ))}\n\n {Object.keys(widgetsByCategory).length === 0 && (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '32px',\n color: 'var(--color-text-secondary)',\n }}\n >\n <Search style={{ width: '32px', height: '32px', marginBottom: '12px', opacity: 0.5 }} />\n <p style={{ fontSize: '13px' }}>No widgets found</p>\n </div>\n )}\n </div>\n </div>\n );\n});\n\nexport default WidgetTypeSelector;\n"],"mappings":"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"}
@@ -1,17 +1,59 @@
1
1
  import { GetWidgetTypesDocument as e } from "../../generated/wspace-operations.js";
2
2
  import t, { WIDGET_CATEGORY_LABELS as n, getAllWidgetDefinitions as r, getWidgetDefinition as i } from "../components/widgets/WidgetRegistry.js";
3
- import { mapFromFederationWidgetType as a } from "../utils/widgetTypeMapping.js";
4
- import { useEffect as o, useMemo as s } from "react";
5
- import { useQuery as c } from "@tanstack/react-query";
6
- import { print as l } from "graphql";
3
+ import { isFederationAccessible as a, mapFromFederationWidgetType as o } from "../utils/widgetTypeMapping.js";
4
+ import { useEffect as s, useMemo as c } from "react";
5
+ import { useQuery as l } from "@tanstack/react-query";
6
+ import { print as u } from "graphql";
7
7
  //#region src/bigconsole/hooks/useWidgetCatalog.ts
8
- var u = new Set(Object.keys(n));
9
- function d(e) {
8
+ var d = new Set(Object.keys(n)), f = "Puzzle", p = "Installed";
9
+ function m(e) {
10
10
  let n = String(e).toLowerCase();
11
- return Object.prototype.hasOwnProperty.call(t, n) ? n : a(e);
11
+ return Object.prototype.hasOwnProperty.call(t, n) || a(e);
12
12
  }
13
- function f(e) {
14
- let t = d(e.type), n = i(t), r = u.has(e.category) ? e.category : n.category;
13
+ function h(e) {
14
+ let n = String(e).toLowerCase();
15
+ return Object.prototype.hasOwnProperty.call(t, n) ? n : o(e);
16
+ }
17
+ function g(e) {
18
+ return {
19
+ type: "custom",
20
+ name: e.label,
21
+ description: e.description,
22
+ icon: e.icon || f,
23
+ category: p,
24
+ component: null,
25
+ defaultConfig: {
26
+ ...e.defaultConfig ?? {},
27
+ widgetTypeKey: e.type,
28
+ isStoreWidget: !0
29
+ },
30
+ defaultSize: {
31
+ width: e.defaultSize.width,
32
+ height: e.defaultSize.height
33
+ },
34
+ minSize: {
35
+ width: e.minSize.width,
36
+ height: e.minSize.height
37
+ },
38
+ maxSize: {
39
+ width: e.maxSize.width,
40
+ height: e.maxSize.height
41
+ },
42
+ defaultResponsive: {
43
+ xs: 12,
44
+ sm: 6,
45
+ md: 4,
46
+ lg: 4
47
+ },
48
+ supportsDataBinding: !0,
49
+ supportsDrilldown: !0,
50
+ supportsAutoRefresh: !0,
51
+ tags: ["store", "installed"]
52
+ };
53
+ }
54
+ function _(e) {
55
+ if (!m(e.type)) return g(e);
56
+ let t = h(e.type), n = i(t), r = d.has(e.category) ? e.category : n.category;
15
57
  return {
16
58
  type: t,
17
59
  name: e.label,
@@ -39,8 +81,8 @@ function f(e) {
39
81
  tags: n.tags
40
82
  };
41
83
  }
42
- function p(t = {}) {
43
- let { workspaceId: n, apiGatewayUrl: i, authToken: a, enabled: u = !0 } = t, { data: d, isLoading: p, error: m } = c({
84
+ function v(t = {}) {
85
+ let { workspaceId: n, apiGatewayUrl: i, authToken: a, enabled: o = !0 } = t, { data: d, isLoading: f, error: p } = l({
44
86
  queryKey: ["widgetCatalog", n],
45
87
  queryFn: async () => {
46
88
  let t = { "Content-Type": "application/json" };
@@ -48,35 +90,35 @@ function p(t = {}) {
48
90
  let r = i ? `${i}/workspaces/graphql` : "/workspaces/graphql", o = await fetch(r, {
49
91
  method: "POST",
50
92
  headers: t,
51
- body: JSON.stringify({ query: l(e) })
93
+ body: JSON.stringify({ query: u(e) })
52
94
  });
53
95
  if (!o.ok) throw Error(`Failed to fetch widget catalog: ${o.status}`);
54
96
  let s = await o.json();
55
97
  if (s.errors?.length) throw Error(s.errors[0].message);
56
98
  return s.data?.getWidgetTypes ?? [];
57
99
  },
58
- enabled: u && !!n,
100
+ enabled: o && !!n,
59
101
  staleTime: 600 * 1e3
60
102
  });
61
- return o(() => {
62
- m && console.warn("Falling back to bundled widget registry:", m.message);
63
- }, [m]), s(() => d && d.length > 0 ? {
64
- definitions: d.map(f),
103
+ return s(() => {
104
+ p && console.warn("Falling back to bundled widget registry:", p.message);
105
+ }, [p]), c(() => d && d.length > 0 ? {
106
+ definitions: d.map(_),
65
107
  source: "backend",
66
- loading: p,
67
- error: m ?? void 0
108
+ loading: f,
109
+ error: p ?? void 0
68
110
  } : {
69
111
  definitions: r(),
70
112
  source: "static",
71
- loading: p,
72
- error: m ?? void 0
113
+ loading: f,
114
+ error: p ?? void 0
73
115
  }, [
74
116
  d,
75
- p,
76
- m
117
+ f,
118
+ p
77
119
  ]);
78
120
  }
79
121
  //#endregion
80
- export { p as default };
122
+ export { v as default };
81
123
 
82
124
  //# sourceMappingURL=useWidgetCatalog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useWidgetCatalog.js","names":[],"sources":["../../../src/bigconsole/hooks/useWidgetCatalog.ts"],"sourcesContent":["/**\n * Hook to fetch the widget-type catalog from the backend (`getWidgetTypes`) and\n * merge it over the bundled static registry.\n *\n * The backend is the source of truth for which widget types exist and their\n * palette metadata (label, description, icon, category, defaultConfig, sizes).\n * A few presentational fields the backend doesn't model — defaultResponsive,\n * supports* capability flags, tags, and the (always-null) `component` — are\n * filled in from the matching static `WIDGET_DEFINITIONS` entry.\n *\n * Resilient by design: while the query is loading, or if it errors / returns\n * nothing (e.g. an older supergraph that hasn't picked up the schema yet), the\n * hook returns the full bundled registry so the palette always works.\n *\n * Follows the same fetch/auth convention as {@link useInstalledWidgets}:\n * workspaceId, apiGatewayUrl, and authToken come from BigConsoleProvider context.\n */\n\nimport { useEffect, useMemo } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { print } from 'graphql';\nimport { GetWidgetTypesDocument } from '../../generated/wspace-operations';\nimport type { GetWidgetTypesQuery } from '../../generated/wspace-operations';\nimport {\n WIDGET_CATEGORY_LABELS,\n WIDGET_DEFINITIONS,\n getAllWidgetDefinitions,\n getWidgetDefinition,\n type WidgetDefinition,\n} from '../components/widgets/WidgetRegistry';\nimport { mapFromFederationWidgetType } from '../utils/widgetTypeMapping';\nimport type { WidgetType, WidgetCategory } from '../types';\n\ntype BackendWidgetType = GetWidgetTypesQuery['getWidgetTypes'][number];\n\nconst KNOWN_CATEGORIES = new Set<string>(Object.keys(WIDGET_CATEGORY_LABELS));\n\n/**\n * Resolve a backend `type` value to a valid internal widget-type key.\n *\n * Handles both shapes the field can take:\n * - canonical front-end keys (`metric_card`, `chart`, `map`, …) once the\n * supergraph serves `WidgetTypeInfo.type` as String — used as-is; and\n * - federation `WidgetType` enum values (`KPI`, `LINE_CHART`, `FUNNEL`, …)\n * if an older supergraph still serves the enum — reverse-mapped to the\n * internal key via the shared widgetTypeMapping util.\n *\n * Naive lowercasing alone is wrong: `KPI` -> `kpi`, `LINE_CHART` -> `line_chart`\n * are not internal types and would render as the \"unknown\" definition.\n */\nfunction resolveInternalType(rawType: string): WidgetType {\n const lower = String(rawType).toLowerCase();\n if (Object.prototype.hasOwnProperty.call(WIDGET_DEFINITIONS, lower)) {\n return lower as WidgetType;\n }\n return mapFromFederationWidgetType(rawType);\n}\n\nexport interface UseWidgetCatalogParams {\n workspaceId?: string;\n apiGatewayUrl?: string;\n authToken?: string;\n /** When false, the query is skipped and the static registry is returned. */\n enabled?: boolean;\n}\n\nexport interface UseWidgetCatalogResult {\n /** Merged catalog (backend metadata over static), or the static registry as fallback. */\n definitions: WidgetDefinition[];\n /** Whether the catalog came from the backend (`backend`) or the bundled registry (`static`). */\n source: 'backend' | 'static';\n loading: boolean;\n error: Error | undefined;\n}\n\n/**\n * Build a WidgetDefinition from one backend catalog entry, filling presentational\n * gaps from the matching static definition.\n */\nfunction mergeBackendType(entry: BackendWidgetType): WidgetDefinition {\n // Resolve to a valid internal widget-type key, whether the backend returns a\n // canonical key (\"metric_card\") or a federation enum value (\"KPI\").\n const typeKey = resolveInternalType(entry.type);\n const staticDef = getWidgetDefinition(typeKey);\n\n const category: WidgetCategory = KNOWN_CATEGORIES.has(entry.category)\n ? (entry.category as WidgetCategory)\n : staticDef.category;\n\n return {\n type: typeKey,\n name: entry.label,\n description: entry.description,\n icon: entry.icon,\n category,\n component: null,\n defaultConfig: (entry.defaultConfig as Record<string, unknown>) ?? staticDef.defaultConfig,\n defaultSize: { width: entry.defaultSize.width, height: entry.defaultSize.height },\n minSize: { width: entry.minSize.width, height: entry.minSize.height },\n maxSize: { width: entry.maxSize.width, height: entry.maxSize.height },\n // Fields the backend WidgetTypeInfo does not model — keep the static values.\n defaultResponsive: staticDef.defaultResponsive,\n supportsDataBinding: staticDef.supportsDataBinding,\n supportsDrilldown: staticDef.supportsDrilldown,\n supportsAutoRefresh: staticDef.supportsAutoRefresh,\n tags: staticDef.tags,\n };\n}\n\nexport function useWidgetCatalog(params: UseWidgetCatalogParams = {}): UseWidgetCatalogResult {\n const { workspaceId, apiGatewayUrl, authToken, enabled = true } = params;\n\n const {\n data: backendTypes,\n isLoading: loading,\n error,\n } = useQuery<BackendWidgetType[], Error>({\n // Only workspaceId is part of the cache key; token/url are used inside queryFn\n // but should not invalidate the cache on rotation.\n queryKey: ['widgetCatalog', workspaceId],\n queryFn: async () => {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (workspaceId) headers['x-workspace-id'] = workspaceId;\n if (authToken) headers['Authorization'] = `Bearer ${authToken}`;\n\n const url = apiGatewayUrl ? `${apiGatewayUrl}/workspaces/graphql` : '/workspaces/graphql';\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query: print(GetWidgetTypesDocument) }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch widget catalog: ${response.status}`);\n }\n\n const result: { data?: GetWidgetTypesQuery; errors?: { message: string }[] } = await response.json();\n\n if (result.errors?.length) {\n throw new Error(result.errors[0].message);\n }\n\n return result.data?.getWidgetTypes ?? [];\n },\n enabled: enabled && !!workspaceId,\n // The catalog is effectively static config; avoid refetching on every remount.\n staleTime: 10 * 60 * 1000,\n });\n\n useEffect(() => {\n if (error) {\n // Non-fatal: the palette falls back to the bundled registry.\n console.warn('Falling back to bundled widget registry:', error.message);\n }\n }, [error]);\n\n return useMemo<UseWidgetCatalogResult>(() => {\n if (backendTypes && backendTypes.length > 0) {\n return {\n definitions: backendTypes.map(mergeBackendType),\n source: 'backend',\n loading,\n error: error ?? undefined,\n };\n }\n return {\n definitions: getAllWidgetDefinitions(),\n source: 'static',\n loading,\n error: error ?? undefined,\n };\n }, [backendTypes, loading, error]);\n}\n\nexport default useWidgetCatalog;\n"],"mappings":";;;;;;;AAmCA,IAAM,IAAmB,IAAI,IAAY,OAAO,KAAK,EAAuB,CAAC;AAe7E,SAAS,EAAoB,GAA6B;CACxD,IAAM,IAAQ,OAAO,EAAQ,CAAC,aAAa;AAI3C,QAHI,OAAO,UAAU,eAAe,KAAK,GAAoB,EAAM,GAC1D,IAEF,EAA4B,EAAQ;;AAwB7C,SAAS,EAAiB,GAA4C;CAGpE,IAAM,IAAU,EAAoB,EAAM,KAAK,EACzC,IAAY,EAAoB,EAAQ,EAExC,IAA2B,EAAiB,IAAI,EAAM,SAAS,GAChE,EAAM,WACP,EAAU;AAEd,QAAO;EACL,MAAM;EACN,MAAM,EAAM;EACZ,aAAa,EAAM;EACnB,MAAM,EAAM;EACZ;EACA,WAAW;EACX,eAAgB,EAAM,iBAA6C,EAAU;EAC7E,aAAa;GAAE,OAAO,EAAM,YAAY;GAAO,QAAQ,EAAM,YAAY;GAAQ;EACjF,SAAS;GAAE,OAAO,EAAM,QAAQ;GAAO,QAAQ,EAAM,QAAQ;GAAQ;EACrE,SAAS;GAAE,OAAO,EAAM,QAAQ;GAAO,QAAQ,EAAM,QAAQ;GAAQ;EAErE,mBAAmB,EAAU;EAC7B,qBAAqB,EAAU;EAC/B,mBAAmB,EAAU;EAC7B,qBAAqB,EAAU;EAC/B,MAAM,EAAU;EACjB;;AAGH,SAAgB,EAAiB,IAAiC,EAAE,EAA0B;CAC5F,IAAM,EAAE,gBAAa,kBAAe,cAAW,aAAU,OAAS,GAE5D,EACJ,MAAM,GACN,WAAW,GACX,aACE,EAAqC;EAGvC,UAAU,CAAC,iBAAiB,EAAY;EACxC,SAAS,YAAY;GACnB,IAAM,IAAkC,EAAE,gBAAgB,oBAAoB;AAE9E,GADI,MAAa,EAAQ,oBAAoB,IACzC,MAAW,EAAQ,gBAAmB,UAAU;GAEpD,IAAM,IAAM,IAAgB,GAAG,EAAc,uBAAuB,uBAE9D,IAAW,MAAM,MAAM,GAAK;IAChC,QAAQ;IACR;IACA,MAAM,KAAK,UAAU,EAAE,OAAO,EAAM,EAAuB,EAAE,CAAC;IAC/D,CAAC;AAEF,OAAI,CAAC,EAAS,GACZ,OAAU,MAAM,mCAAmC,EAAS,SAAS;GAGvE,IAAM,IAAyE,MAAM,EAAS,MAAM;AAEpG,OAAI,EAAO,QAAQ,OACjB,OAAU,MAAM,EAAO,OAAO,GAAG,QAAQ;AAG3C,UAAO,EAAO,MAAM,kBAAkB,EAAE;;EAE1C,SAAS,KAAW,CAAC,CAAC;EAEtB,WAAW,MAAU;EACtB,CAAC;AASF,QAPA,QAAgB;AACd,EAAI,KAEF,QAAQ,KAAK,4CAA4C,EAAM,QAAQ;IAExE,CAAC,EAAM,CAAC,EAEJ,QACD,KAAgB,EAAa,SAAS,IACjC;EACL,aAAa,EAAa,IAAI,EAAiB;EAC/C,QAAQ;EACR;EACA,OAAO,KAAS,KAAA;EACjB,GAEI;EACL,aAAa,GAAyB;EACtC,QAAQ;EACR;EACA,OAAO,KAAS,KAAA;EACjB,EACA;EAAC;EAAc;EAAS;EAAM,CAAC"}
1
+ {"version":3,"file":"useWidgetCatalog.js","names":[],"sources":["../../../src/bigconsole/hooks/useWidgetCatalog.ts"],"sourcesContent":["/**\n * Hook to fetch the widget-type catalog from the backend (`getWidgetTypes`) and\n * merge it over the bundled static registry.\n *\n * The backend is the source of truth for which widget types exist and their\n * palette metadata (label, description, icon, category, defaultConfig, sizes).\n * A few presentational fields the backend doesn't model — defaultResponsive,\n * supports* capability flags, tags, and the (always-null) `component` — are\n * filled in from the matching static `WIDGET_DEFINITIONS` entry.\n *\n * Resilient by design: while the query is loading, or if it errors / returns\n * nothing (e.g. an older supergraph that hasn't picked up the schema yet), the\n * hook returns the full bundled registry so the palette always works.\n *\n * Follows the same fetch/auth convention as {@link useInstalledWidgets}:\n * workspaceId, apiGatewayUrl, and authToken come from BigConsoleProvider context.\n */\n\nimport { useEffect, useMemo } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { print } from 'graphql';\nimport { GetWidgetTypesDocument } from '../../generated/wspace-operations';\nimport type { GetWidgetTypesQuery } from '../../generated/wspace-operations';\nimport {\n WIDGET_CATEGORY_LABELS,\n WIDGET_DEFINITIONS,\n getAllWidgetDefinitions,\n getWidgetDefinition,\n type WidgetDefinition,\n} from '../components/widgets/WidgetRegistry';\nimport { isFederationAccessible, mapFromFederationWidgetType } from '../utils/widgetTypeMapping';\nimport type { WidgetType, WidgetCategory } from '../types';\n\ntype BackendWidgetType = GetWidgetTypesQuery['getWidgetTypes'][number];\n\nconst KNOWN_CATEGORIES = new Set<string>(Object.keys(WIDGET_CATEGORY_LABELS));\n\n/** Fallbacks for store/custom types whose manifest omits presentational metadata. */\nconst STORE_WIDGET_FALLBACK_ICON = 'Puzzle';\nconst STORE_WIDGET_CATEGORY: WidgetCategory = 'Installed';\n\n/**\n * Whether a backend `type` value corresponds to one of the built-in widget types\n * (either a canonical front-end key like \"metric_card\" or a federation enum value\n * like \"KPI\"). Store/custom types installed from the marketplace (BOFF-2858) use\n * namespaced keys such as \"store/stripe-mrr-card\" and match neither.\n */\nfunction isBuiltInType(rawType: string): boolean {\n const lower = String(rawType).toLowerCase();\n return Object.prototype.hasOwnProperty.call(WIDGET_DEFINITIONS, lower) || isFederationAccessible(rawType);\n}\n\n/**\n * Resolve a backend `type` value to a valid internal widget-type key.\n *\n * Handles both shapes the field can take:\n * - canonical front-end keys (`metric_card`, `chart`, `map`, …) once the\n * supergraph serves `WidgetTypeInfo.type` as String — used as-is; and\n * - federation `WidgetType` enum values (`KPI`, `LINE_CHART`, `FUNNEL`, …)\n * if an older supergraph still serves the enum — reverse-mapped to the\n * internal key via the shared widgetTypeMapping util.\n *\n * Naive lowercasing alone is wrong: `KPI` -> `kpi`, `LINE_CHART` -> `line_chart`\n * are not internal types and would render as the \"unknown\" definition.\n */\nfunction resolveInternalType(rawType: string): WidgetType {\n const lower = String(rawType).toLowerCase();\n if (Object.prototype.hasOwnProperty.call(WIDGET_DEFINITIONS, lower)) {\n return lower as WidgetType;\n }\n return mapFromFederationWidgetType(rawType);\n}\n\nexport interface UseWidgetCatalogParams {\n workspaceId?: string;\n apiGatewayUrl?: string;\n authToken?: string;\n /** When false, the query is skipped and the static registry is returned. */\n enabled?: boolean;\n}\n\nexport interface UseWidgetCatalogResult {\n /** Merged catalog (backend metadata over static), or the static registry as fallback. */\n definitions: WidgetDefinition[];\n /** Whether the catalog came from the backend (`backend`) or the bundled registry (`static`). */\n source: 'backend' | 'static';\n loading: boolean;\n error: Error | undefined;\n}\n\n/**\n * Build a WidgetDefinition for a store/custom widget type (BOFF-2858).\n *\n * Store types have no matching static definition, so everything comes from the\n * backend catalog entry. The namespaced key (e.g. \"store/stripe-mrr-card\") is\n * kept verbatim as the definition `type` and the entry is filed under the\n * \"Installed from Store\" category so the palette groups it separately.\n */\nfunction buildStoreType(entry: BackendWidgetType): WidgetDefinition {\n return {\n // Store types persist as `custom` widget instances — the namespaced key is\n // NOT a valid federated WidgetType enum value, so it cannot be sent to\n // createBigConsoleWidget directly. The real key travels in config\n // (widgetTypeKey), matching the convention useInstalledWidgets already uses\n // for store-installed widgets (isStoreWidget + custom type).\n type: 'custom' as WidgetType,\n name: entry.label,\n description: entry.description,\n icon: entry.icon || STORE_WIDGET_FALLBACK_ICON,\n category: STORE_WIDGET_CATEGORY,\n component: null,\n defaultConfig: {\n ...((entry.defaultConfig as Record<string, unknown>) ?? {}),\n // Preserve the namespaced type so the create flow can set Widget.widgetTypeKey\n // and the renderer can resolve the store type's template.\n widgetTypeKey: entry.type,\n isStoreWidget: true,\n },\n defaultSize: { width: entry.defaultSize.width, height: entry.defaultSize.height },\n minSize: { width: entry.minSize.width, height: entry.minSize.height },\n maxSize: { width: entry.maxSize.width, height: entry.maxSize.height },\n // The backend WidgetTypeInfo does not model these; store widgets support the\n // full data-binding feature set by default.\n defaultResponsive: { xs: 12, sm: 6, md: 4, lg: 4 },\n supportsDataBinding: true,\n supportsDrilldown: true,\n supportsAutoRefresh: true,\n tags: ['store', 'installed'],\n };\n}\n\n/**\n * Build a WidgetDefinition from one backend catalog entry, filling presentational\n * gaps from the matching static definition. Store/custom types (no static match)\n * are handled by {@link buildStoreType} instead of being collapsed onto a\n * built-in key.\n */\nfunction mergeBackendType(entry: BackendWidgetType): WidgetDefinition {\n if (!isBuiltInType(entry.type)) {\n return buildStoreType(entry);\n }\n\n // Resolve to a valid internal widget-type key, whether the backend returns a\n // canonical key (\"metric_card\") or a federation enum value (\"KPI\").\n const typeKey = resolveInternalType(entry.type);\n const staticDef = getWidgetDefinition(typeKey);\n\n const category: WidgetCategory = KNOWN_CATEGORIES.has(entry.category)\n ? (entry.category as WidgetCategory)\n : staticDef.category;\n\n return {\n type: typeKey,\n name: entry.label,\n description: entry.description,\n icon: entry.icon,\n category,\n component: null,\n defaultConfig: (entry.defaultConfig as Record<string, unknown>) ?? staticDef.defaultConfig,\n defaultSize: { width: entry.defaultSize.width, height: entry.defaultSize.height },\n minSize: { width: entry.minSize.width, height: entry.minSize.height },\n maxSize: { width: entry.maxSize.width, height: entry.maxSize.height },\n // Fields the backend WidgetTypeInfo does not model — keep the static values.\n defaultResponsive: staticDef.defaultResponsive,\n supportsDataBinding: staticDef.supportsDataBinding,\n supportsDrilldown: staticDef.supportsDrilldown,\n supportsAutoRefresh: staticDef.supportsAutoRefresh,\n tags: staticDef.tags,\n };\n}\n\nexport function useWidgetCatalog(params: UseWidgetCatalogParams = {}): UseWidgetCatalogResult {\n const { workspaceId, apiGatewayUrl, authToken, enabled = true } = params;\n\n const {\n data: backendTypes,\n isLoading: loading,\n error,\n } = useQuery<BackendWidgetType[], Error>({\n // Only workspaceId is part of the cache key; token/url are used inside queryFn\n // but should not invalidate the cache on rotation.\n queryKey: ['widgetCatalog', workspaceId],\n queryFn: async () => {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (workspaceId) headers['x-workspace-id'] = workspaceId;\n if (authToken) headers['Authorization'] = `Bearer ${authToken}`;\n\n const url = apiGatewayUrl ? `${apiGatewayUrl}/workspaces/graphql` : '/workspaces/graphql';\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query: print(GetWidgetTypesDocument) }),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch widget catalog: ${response.status}`);\n }\n\n const result: { data?: GetWidgetTypesQuery; errors?: { message: string }[] } = await response.json();\n\n if (result.errors?.length) {\n throw new Error(result.errors[0].message);\n }\n\n return result.data?.getWidgetTypes ?? [];\n },\n enabled: enabled && !!workspaceId,\n // The catalog is effectively static config; avoid refetching on every remount.\n staleTime: 10 * 60 * 1000,\n });\n\n useEffect(() => {\n if (error) {\n // Non-fatal: the palette falls back to the bundled registry.\n console.warn('Falling back to bundled widget registry:', error.message);\n }\n }, [error]);\n\n return useMemo<UseWidgetCatalogResult>(() => {\n if (backendTypes && backendTypes.length > 0) {\n return {\n definitions: backendTypes.map(mergeBackendType),\n source: 'backend',\n loading,\n error: error ?? undefined,\n };\n }\n return {\n definitions: getAllWidgetDefinitions(),\n source: 'static',\n loading,\n error: error ?? undefined,\n };\n }, [backendTypes, loading, error]);\n}\n\nexport default useWidgetCatalog;\n"],"mappings":"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"}