@burdenoff/microfe-bigconsole 2026.805.2 → 2026.805.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +109 -113
- package/dist/bigconsole/components/builder/inspector/DataBindingSection.js.map +1 -1
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +115 -113
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js +71 -62
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js +90 -89
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +56 -54
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js +126 -125
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +151 -150
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js +77 -76
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +97 -96
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +172 -191
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js +75 -83
- package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +151 -154
- package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +181 -190
- package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js.map +1 -1
- package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js +1 -0
- package/dist/bigconsole/components/dashboard/WidgetPalette.js +110 -108
- package/dist/bigconsole/components/dashboard/WidgetPalette.js.map +1 -1
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js +102 -103
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +36 -41
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +38 -37
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +113 -117
- package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +182 -197
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +166 -168
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/DataSinkSelector.js +87 -86
- package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ImportParserDialog.js +196 -211
- package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserSelector.js +130 -128
- package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +66 -63
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewPanel.js +95 -91
- package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js +222 -215
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js.map +1 -1
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +149 -145
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +504 -470
- package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +44 -47
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/ChartConfig.js +143 -142
- package/dist/bigconsole/components/widgets/chart/ChartConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js +101 -88
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js.map +1 -1
- package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +135 -134
- package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js.map +1 -1
- package/dist/bigconsole/components/widgets/form/FormWidget.js +167 -159
- package/dist/bigconsole/components/widgets/form/FormWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +69 -68
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js +64 -72
- package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +60 -59
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/list/ListConfig.js +57 -56
- package/dist/bigconsole/components/widgets/list/ListConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +70 -69
- package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +35 -34
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +20 -19
- package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js.map +1 -1
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +178 -177
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +76 -75
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableConfig.js +183 -168
- package/dist/bigconsole/components/widgets/table/TableConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +187 -180
- package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js.map +1 -1
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +40 -38
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js.map +1 -1
- package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js +150 -148
- package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js.map +1 -1
- package/dist/bigconsole/pages/DashboardBuilderPage.js +181 -176
- package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardViewPage.js +196 -192
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +223 -219
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/DataParsersPage.js +167 -170
- package/dist/bigconsole/pages/DataParsersPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkBuilderPage.js +204 -189
- package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +175 -169
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSourcesPage.js +8 -7
- package/dist/bigconsole/pages/DataSourcesPage.js.map +1 -1
- package/dist/bigconsole/pages/InsightsPage.js +238 -208
- package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserBuilderPage.js +464 -412
- package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserViewPage.js +92 -90
- package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineBuilderPage.js +221 -194
- package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineViewPage.js +181 -184
- package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelinesPage.js +128 -127
- package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
- package/dist/bigconsole/pages/SettingsPage.js +177 -172
- package/dist/bigconsole/pages/SettingsPage.js.map +1 -1
- package/dist/bigconsole/pages/WidgetDemoPage.js +121 -83
- package/dist/bigconsole/pages/WidgetDemoPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowViewPage.js +199 -193
- package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowsPage.js +129 -128
- package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +111 -109
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +127 -126
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/CustomRenderer.js +103 -103
- package/dist/bigconsole/renderers/adapters/CustomRenderer.js.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlockKitRenderer.js","names":[],"sources":["../../../../src/bigconsole/renderers/adapters/BlockKitRenderer.tsx"],"sourcesContent":["/**\n * Slack Block Kit Renderer (v2.0)\n *\n * Renders widgets using Slack Block Kit format.\n * Supports common block types: section, divider, header, context, image, actions.\n */\n\nimport React, { memo, useCallback, useMemo, type ReactNode } from 'react';\nimport type { RendererProps, BlockKitTemplate, BlockKitElement, BlockKitText } from '../types/renderer';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Render Block Kit text (plain_text or mrkdwn)\n * Uses React elements instead of dangerouslySetInnerHTML for better security\n */\nfunction renderText(text: BlockKitText | string | undefined): React.ReactNode {\n if (!text) return null;\n\n if (typeof text === 'string') {\n return text;\n }\n\n if (text.type === 'mrkdwn') {\n // Parse mrkdwn and convert to React elements\n const content = text.text || '';\n\n // Parse the markdown into React elements\n const elements: React.ReactNode[] = [];\n let remaining = content;\n let key = 0;\n\n // Process text segment by segment\n while (remaining.length > 0) {\n // Try to match bold: *text*\n const boldMatch = remaining.match(/^\\*([^*]+)\\*/);\n if (boldMatch) {\n elements.push(<strong key={key++}>{boldMatch[1]}</strong>);\n remaining = remaining.slice(boldMatch[0].length);\n continue;\n }\n\n // Try to match italic: _text_\n const italicMatch = remaining.match(/^_([^_]+)_/);\n if (italicMatch) {\n elements.push(<em key={key++}>{italicMatch[1]}</em>);\n remaining = remaining.slice(italicMatch[0].length);\n continue;\n }\n\n // Try to match strike: ~text~\n const strikeMatch = remaining.match(/^~([^~]+)~/);\n if (strikeMatch) {\n elements.push(<del key={key++}>{strikeMatch[1]}</del>);\n remaining = remaining.slice(strikeMatch[0].length);\n continue;\n }\n\n // Try to match code: `text`\n const codeMatch = remaining.match(/^`([^`]+)`/);\n if (codeMatch) {\n elements.push(\n <code key={key++} className=\"bg-bg-sunken px-1 rounded text-sm\">\n {codeMatch[1]}\n </code>\n );\n remaining = remaining.slice(codeMatch[0].length);\n continue;\n }\n\n // Try to match link with text: <url|text>\n const linkTextMatch = remaining.match(/^<([^|>]+)\\|([^>]+)>/);\n if (linkTextMatch) {\n elements.push(\n <a\n key={key++}\n href={linkTextMatch[1]}\n className=\"text-blue-600 dark:text-blue-400 underline\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {linkTextMatch[2]}\n </a>\n );\n remaining = remaining.slice(linkTextMatch[0].length);\n continue;\n }\n\n // Try to match plain link: <url>\n const linkMatch = remaining.match(/^<([^|>]+)>/);\n if (linkMatch) {\n elements.push(\n <a\n key={key++}\n href={linkMatch[1]}\n className=\"text-blue-600 dark:text-blue-400 underline\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {linkMatch[1]}\n </a>\n );\n remaining = remaining.slice(linkMatch[0].length);\n continue;\n }\n\n // No special formatting found at start - extract plain text until next special char\n const plainMatch = remaining.match(/^[^*_~`<]+/);\n if (plainMatch) {\n elements.push(<React.Fragment key={key++}>{plainMatch[0]}</React.Fragment>);\n remaining = remaining.slice(plainMatch[0].length);\n continue;\n }\n\n // Single special char that didn't match a pattern - output as-is\n elements.push(<React.Fragment key={key++}>{remaining[0]}</React.Fragment>);\n remaining = remaining.slice(1);\n }\n\n return <span>{elements}</span>;\n }\n\n // Plain text with optional emoji\n return text.text;\n}\n\n/**\n * Get button style class\n */\nfunction getButtonStyleClass(style?: string): string {\n switch (style) {\n case 'primary':\n return 'bg-status-success-bg hover:bg-status-success-bg/90 text-white transition-colors';\n case 'danger':\n return 'bg-status-error-bg hover:bg-status-error-bg/90 text-white transition-colors';\n default:\n return 'bg-bg-sunken hover:bg-bg-sunken/80 text-primary transition-colors';\n }\n}\n\n// ============================================================================\n// Template Binding\n// ============================================================================\n\nfunction bindDataToTemplate<T>(template: T, data: Record<string, unknown>): T {\n if (template === null || template === undefined) {\n return template;\n }\n\n if (typeof template === 'string') {\n // Replace ${path} with actual values\n return template.replace(/\\$\\{([^}]+)\\}/g, (_, path) => {\n const value = getNestedValue(data, path.trim());\n return value !== undefined ? String(value) : '';\n }) as T;\n }\n\n if (Array.isArray(template)) {\n return template.map((item) => bindDataToTemplate(item, data)) as T;\n }\n\n if (typeof template === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(template)) {\n result[key] = bindDataToTemplate(value, data);\n }\n return result as T;\n }\n\n return template;\n}\n\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current && typeof current === 'object' && part in (current as Record<string, unknown>)) {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n\n return current;\n}\n\n// ============================================================================\n// Block Renderers\n// ============================================================================\n\ninterface BlockRendererProps {\n block: BlockKitElement;\n index: number;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst BlockRenderer: React.FC<BlockRendererProps> = memo(function BlockRenderer({ block, index: _index, onAction }) {\n switch (block.type) {\n // Header block\n case 'header':\n return (\n <div className=\"py-2\">\n <h2 className=\"text-lg font-bold text-primary\">{renderText(block.text)}</h2>\n </div>\n );\n\n // Section block\n case 'section':\n return (\n <div className=\"py-2 flex items-start gap-4\">\n <div className=\"flex-1\">\n {block.text && <p className=\"text-primary\">{renderText(block.text)}</p>}\n {block.fields && block.fields.length > 0 && (\n <div className=\"grid grid-cols-2 gap-2 mt-2\">\n {block.fields.map((field, i) => (\n <div key={i} className=\"text-sm\">\n {renderText(field)}\n </div>\n ))}\n </div>\n )}\n </div>\n {block.accessory && (\n <div className=\"flex-shrink-0\">\n <AccessoryRenderer accessory={block.accessory as BlockKitElement} onAction={onAction} />\n </div>\n )}\n </div>\n );\n\n // Divider block\n case 'divider':\n return <hr className=\"my-2 border-default\" />;\n\n // Image block\n case 'image':\n return (\n <div className=\"py-2\">\n {block.title && <p className=\"text-sm text-secondary mb-1\">{renderText(block.title)}</p>}\n <img src={block.image_url} alt={block.alt_text || ''} className=\"max-w-full h-auto rounded\" />\n </div>\n );\n\n // Context block\n case 'context':\n return (\n <div className=\"py-1 flex items-center gap-2 flex-wrap text-sm text-secondary\">\n {block.elements?.map((element, i) => (\n <span key={i}>\n {element.type === 'image' ? (\n <img src={element.image_url} alt={element.alt_text || ''} className=\"w-5 h-5 rounded inline-block\" />\n ) : (\n renderText(element.text as BlockKitText)\n )}\n </span>\n ))}\n </div>\n );\n\n // Actions block\n case 'actions':\n return (\n <div className=\"py-2 flex gap-2 flex-wrap\">\n {block.elements?.map((element, i) => (\n <ActionRenderer key={i} element={element} onAction={onAction} />\n ))}\n </div>\n );\n\n // Input block (simplified)\n case 'input':\n return (\n <div className=\"py-2\">\n {block.label ? (\n <label className=\"block text-sm font-medium text-secondary mb-1\">\n {renderText(block.label as BlockKitText) as ReactNode}\n </label>\n ) : null}\n <input\n type=\"text\"\n className=\"w-full px-3 py-2 border border-default rounded-md bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n placeholder={(() => {\n const element = block.element as Record<string, unknown> | undefined;\n return element?.placeholder ? (renderText(element.placeholder as BlockKitText) as string) : '';\n })()}\n />\n </div>\n );\n\n // Rich text block (simplified)\n case 'rich_text':\n return (\n <div className=\"py-2 text-primary\">\n {block.elements?.map((element, i) => (\n <span key={i}>{renderText(element.text as BlockKitText)}</span>\n ))}\n </div>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Accessory Renderer\n// ============================================================================\n\ninterface AccessoryRendererProps {\n accessory: BlockKitElement;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst AccessoryRenderer: React.FC<AccessoryRendererProps> = memo(function AccessoryRenderer({ accessory, onAction }) {\n switch (accessory.type) {\n case 'image':\n return (\n <img src={accessory.image_url} alt={accessory.alt_text || ''} className=\"w-12 h-12 rounded object-cover\" />\n );\n\n case 'button':\n return (\n <button\n type=\"button\"\n onClick={() => onAction?.(accessory.action_id || '', accessory.value)}\n className={`px-3 py-1.5 text-sm font-medium rounded ${getButtonStyleClass(accessory.style)}`}\n >\n {renderText(accessory.text)}\n </button>\n );\n\n case 'overflow':\n return (\n <button type=\"button\" className=\"p-1 text-secondary hover:text-primary transition-colors\">\n <svg className=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z\" />\n </svg>\n </button>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Action Renderer\n// ============================================================================\n\ninterface ActionRendererProps {\n element: BlockKitElement;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst ActionRenderer: React.FC<ActionRendererProps> = memo(function ActionRenderer({ element, onAction }) {\n const handleClick = useCallback(() => {\n if (element.action_id) {\n onAction?.(element.action_id, element.value);\n }\n if (element.url) {\n window.open(element.url, '_blank');\n }\n }, [element, onAction]);\n\n switch (element.type) {\n case 'button':\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n className={`px-4 py-2 text-sm font-medium rounded ${getButtonStyleClass(element.style)}`}\n >\n {renderText(element.text)}\n </button>\n );\n\n case 'static_select':\n return (\n <select\n className=\"px-3 py-2 text-sm border border-default rounded bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n onChange={(e) => onAction?.(element.action_id || '', e.target.value)}\n >\n {element.placeholder ? (\n <option value=\"\">{renderText(element.placeholder as BlockKitText) as ReactNode}</option>\n ) : null}\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <option key={i} value={option.value}>\n {renderText(option.text)}\n </option>\n ))}\n </select>\n );\n\n case 'datepicker':\n return (\n <input\n type=\"date\"\n className=\"px-3 py-2 text-sm border border-default rounded bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n onChange={(e) => onAction?.(element.action_id || '', e.target.value)}\n />\n );\n\n case 'checkboxes':\n return (\n <div className=\"space-y-1\">\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <label key={i} className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n className=\"rounded border-default transition-colors\"\n onChange={(e) => onAction?.(element.action_id || '', option.value, { checked: e.target.checked })}\n />\n {renderText(option.text)}\n </label>\n ))}\n </div>\n );\n\n case 'radio_buttons':\n return (\n <div className=\"space-y-1\">\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <label key={i} className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name={element.action_id}\n className=\"border-default transition-colors\"\n onChange={() => onAction?.(element.action_id || '', option.value)}\n />\n {renderText(option.text)}\n </label>\n ))}\n </div>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Auto-generate BlockKit blocks from arbitrary data\n// ============================================================================\n\nfunction generateBlocksFromData(data: Record<string, unknown>): BlockKitElement[] {\n const blocks: BlockKitElement[] = [];\n\n for (const [key, value] of Object.entries(data)) {\n if (value === null || value === undefined) continue;\n\n // Handle arrays\n if (Array.isArray(value)) {\n // Add header for the array\n blocks.push({\n type: 'header',\n text: {\n type: 'plain_text',\n text: formatKey(key),\n emoji: true,\n },\n });\n\n // Add each item\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n // Object items - render as section with fields\n const fields: BlockKitText[] = [];\n for (const [itemKey, itemValue] of Object.entries(item)) {\n if (itemValue !== null && itemValue !== undefined) {\n fields.push({\n type: 'mrkdwn',\n text: `*${formatKey(itemKey)}:*\\n${formatValue(itemValue)}`,\n });\n }\n }\n if (fields.length > 0) {\n blocks.push({\n type: 'section',\n fields: fields.slice(0, 10), // BlockKit max 10 fields\n });\n }\n } else {\n // Primitive items - render as simple section\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `${index + 1}. ${formatValue(item)}`,\n },\n });\n }\n\n // Add divider between items (except last)\n if (index < value.length - 1) {\n blocks.push({ type: 'divider' });\n }\n });\n }\n // Handle objects\n else if (typeof value === 'object') {\n blocks.push({\n type: 'header',\n text: {\n type: 'plain_text',\n text: formatKey(key),\n emoji: true,\n },\n });\n\n const fields: BlockKitText[] = [];\n for (const [objKey, objValue] of Object.entries(value as Record<string, unknown>)) {\n if (objValue !== null && objValue !== undefined) {\n fields.push({\n type: 'mrkdwn',\n text: `*${formatKey(objKey)}:*\\n${formatValue(objValue)}`,\n });\n }\n }\n if (fields.length > 0) {\n blocks.push({\n type: 'section',\n fields: fields.slice(0, 10),\n });\n }\n }\n // Handle primitives\n else {\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `*${formatKey(key)}:* ${formatValue(value)}`,\n },\n });\n }\n }\n\n return blocks;\n}\n\nfunction formatKey(key: string): string {\n // Convert camelCase or snake_case to Title Case\n return key\n .replace(/_/g, ' ')\n .replace(/([A-Z])/g, ' $1')\n .replace(/^./, (str) => str.toUpperCase())\n .trim();\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null || value === undefined) return '-';\n if (typeof value === 'boolean') return value ? 'Yes' : 'No';\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n\n// ============================================================================\n// Main Renderer Component\n// ============================================================================\n\nexport const BlockKitRenderer: React.FC<RendererProps> = memo(function BlockKitRenderer({\n widget,\n data,\n template,\n isLoading,\n error,\n onDrilldown: _onDrilldown,\n onAction,\n}) {\n // Debug logging\n\n // Parse and bind template\n const boundTemplate = useMemo(() => {\n // 1. If template has blocks, bind data and use it\n if (template && 'blocks' in (template as Record<string, unknown>)) {\n return bindDataToTemplate(template as unknown as BlockKitTemplate, data);\n }\n\n // 2. If data has blocks, use it directly as BlockKit format\n if (data && 'blocks' in data) {\n return data as unknown as BlockKitTemplate;\n }\n\n // 3. If we have any data, auto-generate blocks from it\n if (data && Object.keys(data).length > 0) {\n const generatedBlocks = generateBlocksFromData(data);\n if (generatedBlocks.length > 0) {\n return { blocks: generatedBlocks } as BlockKitTemplate;\n }\n }\n\n // 4. If template exists but no blocks, try to bind and check again\n if (template) {\n const bound = bindDataToTemplate(template as unknown as BlockKitTemplate, data);\n if (bound?.blocks?.length) {\n return bound;\n }\n }\n\n return null;\n }, [template, data]);\n\n // Handle action clicks\n const handleAction = useCallback(\n (actionId: string, value?: string, actionData?: Record<string, unknown>) => {\n if (onAction) {\n onAction(actionId, { value, ...actionData });\n }\n },\n [onAction]\n );\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <div className=\"animate-pulse space-y-2 w-full\">\n <div className=\"h-4 bg-bg-sunken rounded w-3/4\" />\n <div className=\"h-4 bg-bg-sunken rounded w-1/2\" />\n <div className=\"h-4 bg-bg-sunken rounded w-5/6\" />\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-red-600 dark:text-red-400\">\n <div className=\"text-center\">\n <p className=\"font-medium\">Error rendering blocks</p>\n <p className=\"text-sm mt-1\">{error}</p>\n </div>\n </div>\n );\n }\n\n // Empty state\n if (!boundTemplate?.blocks?.length) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-secondary animate-in fade-in duration-200\">\n <div className=\"text-center\">\n <p>No content to display</p>\n <p className=\"text-sm mt-1\">Configure blocks or provide data</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 bg-surface\">\n {/* Render blocks */}\n {boundTemplate.blocks.map((block, i) => (\n <BlockRenderer key={i} block={block} index={i} onAction={handleAction} />\n ))}\n\n {/* Render attachments */}\n {boundTemplate.attachments?.map((attachment, i) => (\n <div\n key={`attachment-${i}`}\n className=\"mt-4 border-l-4 pl-3\"\n style={{ borderLeftColor: attachment.color || 'var(--color-border)' }}\n >\n {attachment.blocks?.map((block, j) => (\n <BlockRenderer key={j} block={block} index={j} onAction={handleAction} />\n ))}\n {attachment.fallback && !attachment.blocks && <p className=\"text-sm text-secondary\">{attachment.fallback}</p>}\n </div>\n ))}\n </div>\n );\n});\n\nexport default BlockKitRenderer;\n"],"mappings":";;;AAkBA,SAAS,EAAW,GAA0D;AAC5E,KAAI,CAAC,EAAM,QAAO;AAElB,KAAI,OAAO,KAAS,SAClB,QAAO;AAGT,KAAI,EAAK,SAAS,UAAU;EAE1B,IAAM,IAAU,EAAK,QAAQ,IAGvB,IAA8B,EAAE,EAClC,IAAY,GACZ,IAAM;AAGV,SAAO,EAAU,SAAS,IAAG;GAE3B,IAAM,IAAY,EAAU,MAAM,eAAe;AACjD,OAAI,GAAW;AAEb,IADA,EAAS,KAAK,kBAAC,UAAD,EAAA,UAAqB,EAAU,IAAY,EAA9B,IAA8B,CAAC,EAC1D,IAAY,EAAU,MAAM,EAAU,GAAG,OAAO;AAChD;;GAIF,IAAM,IAAc,EAAU,MAAM,aAAa;AACjD,OAAI,GAAa;AAEf,IADA,EAAS,KAAK,kBAAC,MAAD,EAAA,UAAiB,EAAY,IAAQ,EAA5B,IAA4B,CAAC,EACpD,IAAY,EAAU,MAAM,EAAY,GAAG,OAAO;AAClD;;GAIF,IAAM,IAAc,EAAU,MAAM,aAAa;AACjD,OAAI,GAAa;AAEf,IADA,EAAS,KAAK,kBAAC,OAAD,EAAA,UAAkB,EAAY,IAAS,EAA7B,IAA6B,CAAC,EACtD,IAAY,EAAU,MAAM,EAAY,GAAG,OAAO;AAClD;;GAIF,IAAM,IAAY,EAAU,MAAM,aAAa;AAC/C,OAAI,GAAW;AAMb,IALA,EAAS,KACP,kBAAC,QAAD;KAAkB,WAAU;eACzB,EAAU;KACN,EAFI,IAEJ,CACR,EACD,IAAY,EAAU,MAAM,EAAU,GAAG,OAAO;AAChD;;GAIF,IAAM,IAAgB,EAAU,MAAM,uBAAuB;AAC7D,OAAI,GAAe;AAYjB,IAXA,EAAS,KACP,kBAAC,KAAD;KAEE,MAAM,EAAc;KACpB,WAAU;KACV,QAAO;KACP,KAAI;eAEH,EAAc;KACb,EAPG,IAOH,CACL,EACD,IAAY,EAAU,MAAM,EAAc,GAAG,OAAO;AACpD;;GAIF,IAAM,IAAY,EAAU,MAAM,cAAc;AAChD,OAAI,GAAW;AAYb,IAXA,EAAS,KACP,kBAAC,KAAD;KAEE,MAAM,EAAU;KAChB,WAAU;KACV,QAAO;KACP,KAAI;eAEH,EAAU;KACT,EAPG,IAOH,CACL,EACD,IAAY,EAAU,MAAM,EAAU,GAAG,OAAO;AAChD;;GAIF,IAAM,IAAa,EAAU,MAAM,aAAa;AAChD,OAAI,GAAY;AAEd,IADA,EAAS,KAAK,kBAAC,EAAM,UAAP,EAAA,UAA6B,EAAW,IAAoB,EAAvC,IAAuC,CAAC,EAC3E,IAAY,EAAU,MAAM,EAAW,GAAG,OAAO;AACjD;;AAKF,GADA,EAAS,KAAK,kBAAC,EAAM,UAAP,EAAA,UAA6B,EAAU,IAAoB,EAAtC,IAAsC,CAAC,EAC1E,IAAY,EAAU,MAAM,EAAE;;AAGhC,SAAO,kBAAC,QAAD,EAAA,UAAO,GAAgB,CAAA;;AAIhC,QAAO,EAAK;;AAMd,SAAS,EAAoB,GAAwB;AACnD,SAAQ,GAAR;EACE,KAAK,UACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,QACE,QAAO;;;AAQb,SAAS,EAAsB,GAAa,GAAkC;AAC5E,KAAI,KAAa,KACf,QAAO;AAGT,KAAI,OAAO,KAAa,SAEtB,QAAO,EAAS,QAAQ,mBAAmB,GAAG,MAAS;EACrD,IAAM,IAAQ,EAAe,GAAM,EAAK,MAAM,CAAC;AAC/C,SAAO,MAAU,KAAA,IAA4B,KAAhB,OAAO,EAAM;GAC1C;AAGJ,KAAI,MAAM,QAAQ,EAAS,CACzB,QAAO,EAAS,KAAK,MAAS,EAAmB,GAAM,EAAK,CAAC;AAG/D,KAAI,OAAO,KAAa,UAAU;EAChC,IAAM,IAAkC,EAAE;AAC1C,OAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAS,CACjD,GAAO,KAAO,EAAmB,GAAO,EAAK;AAE/C,SAAO;;AAGT,QAAO;;AAGT,SAAS,EAAe,GAA8B,GAAuB;CAC3E,IAAM,IAAQ,EAAK,MAAM,IAAI,EACzB,IAAmB;AAEvB,MAAK,IAAM,KAAQ,EACjB,KAAI,KAAW,OAAO,KAAY,YAAY,KAAS,EACrD,KAAW,EAAoC;KAE/C;AAIJ,QAAO;;AAaT,IAAM,IAA8C,EAAK,SAAuB,EAAE,UAAO,OAAO,GAAQ,eAAY;AAClH,SAAQ,EAAM,MAAd;EAEE,KAAK,SACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,MAAD;IAAI,WAAU;cAAkC,EAAW,EAAM,KAAK;IAAM,CAAA;GACxE,CAAA;EAIV,KAAK,UACH,QACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAM,QAAQ,kBAAC,KAAD;KAAG,WAAU;eAAgB,EAAW,EAAM,KAAK;KAAK,CAAA,EACtE,EAAM,UAAU,EAAM,OAAO,SAAS,KACrC,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAM,OAAO,KAAK,GAAO,MACxB,kBAAC,OAAD;MAAa,WAAU;gBACpB,EAAW,EAAM;MACd,EAFI,EAEJ,CACN;KACE,CAAA,CAEJ;OACL,EAAM,aACL,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAmB,WAAW,EAAM;KAAwC;KAAY,CAAA;IACpF,CAAA,CAEJ;;EAIV,KAAK,UACH,QAAO,kBAAC,MAAD,EAAI,WAAU,uBAAwB,CAAA;EAG/C,KAAK,QACH,QACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,EAAM,SAAS,kBAAC,KAAD;IAAG,WAAU;cAA+B,EAAW,EAAM,MAAM;IAAK,CAAA,EACxF,kBAAC,OAAD;IAAK,KAAK,EAAM;IAAW,KAAK,EAAM,YAAY;IAAI,WAAU;IAA8B,CAAA,CAC1F;;EAIV,KAAK,UACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAM,UAAU,KAAK,GAAS,MAC7B,kBAAC,QAAD,EAAA,UACG,EAAQ,SAAS,UAChB,kBAAC,OAAD;IAAK,KAAK,EAAQ;IAAW,KAAK,EAAQ,YAAY;IAAI,WAAU;IAAiC,CAAA,GAErG,EAAW,EAAQ,KAAqB,EAErC,EANI,EAMJ,CACP;GACE,CAAA;EAIV,KAAK,UACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAM,UAAU,KAAK,GAAS,MAC7B,kBAAC,GAAD;IAAiC;IAAmB;IAAY,EAA3C,EAA2C,CAChE;GACE,CAAA;EAIV,KAAK,QACH,QACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,EAAM,QACL,kBAAC,SAAD;IAAO,WAAU;cACd,EAAW,EAAM,MAAsB;IAClC,CAAA,GACN,MACJ,kBAAC,SAAD;IACE,MAAK;IACL,WAAU;IACV,oBAAoB;KAClB,IAAM,IAAU,EAAM;AACtB,YAAO,GAAS,cAAe,EAAW,EAAQ,YAA4B,GAAc;QAC1F;IACJ,CAAA,CACE;;EAIV,KAAK,YACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAM,UAAU,KAAK,GAAS,MAC7B,kBAAC,QAAD,EAAA,UAAe,EAAW,EAAQ,KAAqB,EAAQ,EAApD,EAAoD,CAC/D;GACE,CAAA;EAGV,QACE,QAAO;;EAEX,EAWI,IAAsD,EAAK,SAA2B,EAAE,cAAW,eAAY;AACnH,SAAQ,EAAU,MAAlB;EACE,KAAK,QACH,QACE,kBAAC,OAAD;GAAK,KAAK,EAAU;GAAW,KAAK,EAAU,YAAY;GAAI,WAAU;GAAmC,CAAA;EAG/G,KAAK,SACH,QACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,IAAW,EAAU,aAAa,IAAI,EAAU,MAAM;GACrE,WAAW,2CAA2C,EAAoB,EAAU,MAAM;aAEzF,EAAW,EAAU,KAAK;GACpB,CAAA;EAGb,KAAK,WACH,QACE,kBAAC,UAAD;GAAQ,MAAK;GAAS,WAAU;aAC9B,kBAAC,OAAD;IAAK,WAAU;IAAU,MAAK;IAAe,SAAQ;cACnD,kBAAC,QAAD,EAAM,GAAE,gGAAiG,CAAA;IACrG,CAAA;GACC,CAAA;EAGb,QACE,QAAO;;EAEX,EAWI,IAAgD,EAAK,SAAwB,EAAE,YAAS,eAAY;CACxG,IAAM,IAAc,QAAkB;AAIpC,EAHI,EAAQ,aACV,IAAW,EAAQ,WAAW,EAAQ,MAAM,EAE1C,EAAQ,OACV,OAAO,KAAK,EAAQ,KAAK,SAAS;IAEnC,CAAC,GAAS,EAAS,CAAC;AAEvB,SAAQ,EAAQ,MAAhB;EACE,KAAK,SACH,QACE,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAW,yCAAyC,EAAoB,EAAQ,MAAM;aAErF,EAAW,EAAQ,KAAK;GAClB,CAAA;EAGb,KAAK,gBACH,QACE,kBAAC,UAAD;GACE,WAAU;GACV,WAAW,MAAM,IAAW,EAAQ,aAAa,IAAI,EAAE,OAAO,MAAM;aAFtE,CAIG,EAAQ,cACP,kBAAC,UAAD;IAAQ,OAAM;cAAI,EAAW,EAAQ,YAA4B;IAAuB,CAAA,GACtF,MACF,EAAQ,SAA0D,KAAK,GAAQ,MAC/E,kBAAC,UAAD;IAAgB,OAAO,EAAO;cAC3B,EAAW,EAAO,KAAK;IACjB,EAFI,EAEJ,CACT,CACK;;EAGb,KAAK,aACH,QACE,kBAAC,SAAD;GACE,MAAK;GACL,WAAU;GACV,WAAW,MAAM,IAAW,EAAQ,aAAa,IAAI,EAAE,OAAO,MAAM;GACpE,CAAA;EAGN,KAAK,aACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACX,EAAQ,SAA0D,KAAK,GAAQ,MAC/E,kBAAC,SAAD;IAAe,WAAU;cAAzB,CACE,kBAAC,SAAD;KACE,MAAK;KACL,WAAU;KACV,WAAW,MAAM,IAAW,EAAQ,aAAa,IAAI,EAAO,OAAO,EAAE,SAAS,EAAE,OAAO,SAAS,CAAC;KACjG,CAAA,EACD,EAAW,EAAO,KAAK,CAClB;MAPI,EAOJ,CACR;GACE,CAAA;EAGV,KAAK,gBACH,QACE,kBAAC,OAAD;GAAK,WAAU;aACX,EAAQ,SAA0D,KAAK,GAAQ,MAC/E,kBAAC,SAAD;IAAe,WAAU;cAAzB,CACE,kBAAC,SAAD;KACE,MAAK;KACL,MAAM,EAAQ;KACd,WAAU;KACV,gBAAgB,IAAW,EAAQ,aAAa,IAAI,EAAO,MAAM;KACjE,CAAA,EACD,EAAW,EAAO,KAAK,CAClB;MARI,EAQJ,CACR;GACE,CAAA;EAGV,QACE,QAAO;;EAEX;AAMF,SAAS,EAAuB,GAAkD;CAChF,IAAM,IAA4B,EAAE;AAEpC,MAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAK,CACzC,UAAU,KAGd,KAAI,MAAM,QAAQ,EAAM,CAYtB,CAVA,EAAO,KAAK;EACV,MAAM;EACN,MAAM;GACJ,MAAM;GACN,MAAM,EAAU,EAAI;GACpB,OAAO;GACR;EACF,CAAC,EAGF,EAAM,SAAS,GAAM,MAAU;AAC7B,MAAI,OAAO,KAAS,YAAY,GAAe;GAE7C,IAAM,IAAyB,EAAE;AACjC,QAAK,IAAM,CAAC,GAAS,MAAc,OAAO,QAAQ,EAAK,CACrD,CAAI,KAAc,QAChB,EAAO,KAAK;IACV,MAAM;IACN,MAAM,IAAI,EAAU,EAAQ,CAAC,MAAM,EAAY,EAAU;IAC1D,CAAC;AAGN,GAAI,EAAO,SAAS,KAClB,EAAO,KAAK;IACV,MAAM;IACN,QAAQ,EAAO,MAAM,GAAG,GAAG;IAC5B,CAAC;QAIJ,GAAO,KAAK;GACV,MAAM;GACN,MAAM;IACJ,MAAM;IACN,MAAM,GAAG,IAAQ,EAAE,IAAI,EAAY,EAAK;IACzC;GACF,CAAC;AAIJ,EAAI,IAAQ,EAAM,SAAS,KACzB,EAAO,KAAK,EAAE,MAAM,WAAW,CAAC;GAElC;UAGK,OAAO,KAAU,UAAU;AAClC,IAAO,KAAK;GACV,MAAM;GACN,MAAM;IACJ,MAAM;IACN,MAAM,EAAU,EAAI;IACpB,OAAO;IACR;GACF,CAAC;EAEF,IAAM,IAAyB,EAAE;AACjC,OAAK,IAAM,CAAC,GAAQ,MAAa,OAAO,QAAQ,EAAiC,CAC/E,CAAI,KAAa,QACf,EAAO,KAAK;GACV,MAAM;GACN,MAAM,IAAI,EAAU,EAAO,CAAC,MAAM,EAAY,EAAS;GACxD,CAAC;AAGN,EAAI,EAAO,SAAS,KAClB,EAAO,KAAK;GACV,MAAM;GACN,QAAQ,EAAO,MAAM,GAAG,GAAG;GAC5B,CAAC;OAKJ,GAAO,KAAK;EACV,MAAM;EACN,MAAM;GACJ,MAAM;GACN,MAAM,IAAI,EAAU,EAAI,CAAC,KAAK,EAAY,EAAM;GACjD;EACF,CAAC;AAIN,QAAO;;AAGT,SAAS,EAAU,GAAqB;AAEtC,QAAO,EACJ,QAAQ,MAAM,IAAI,CAClB,QAAQ,YAAY,MAAM,CAC1B,QAAQ,OAAO,MAAQ,EAAI,aAAa,CAAC,CACzC,MAAM;;AAGX,SAAS,EAAY,GAAwB;AAI3C,QAHI,KAAU,OAAoC,MAC9C,OAAO,KAAU,YAAkB,IAAQ,QAAQ,OACnD,OAAO,KAAU,WAAiB,KAAK,UAAU,EAAM,GACpD,OAAO,EAAM;;AAOtB,IAAa,IAA4C,EAAK,SAA0B,EACtF,WACA,SACA,aACA,cACA,UACA,aAAa,GACb,eACC;CAID,IAAM,IAAgB,QAAc;AAElC,MAAI,KAAY,YAAa,EAC3B,QAAO,EAAmB,GAAyC,EAAK;AAI1E,MAAI,KAAQ,YAAY,EACtB,QAAO;AAIT,MAAI,KAAQ,OAAO,KAAK,EAAK,CAAC,SAAS,GAAG;GACxC,IAAM,IAAkB,EAAuB,EAAK;AACpD,OAAI,EAAgB,SAAS,EAC3B,QAAO,EAAE,QAAQ,GAAiB;;AAKtC,MAAI,GAAU;GACZ,IAAM,IAAQ,EAAmB,GAAyC,EAAK;AAC/E,OAAI,GAAO,QAAQ,OACjB,QAAO;;AAIX,SAAO;IACN,CAAC,GAAU,EAAK,CAAC,EAGd,IAAe,GAClB,GAAkB,GAAgB,MAAyC;AAC1E,EAAI,KACF,EAAS,GAAU;GAAE;GAAO,GAAG;GAAY,CAAC;IAGhD,CAAC,EAAS,CACX;AAuCD,QApCI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA;IAClD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA;IAClD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA;IAC9C;;EACF,CAAA,GAKN,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAAc;IAA0B,CAAA,EACrD,kBAAC,KAAD;IAAG,WAAU;cAAgB;IAAU,CAAA,CACnC;;EACF,CAAA,GAKL,GAAe,QAAQ,SAY1B,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEG,EAAc,OAAO,KAAK,GAAO,MAChC,kBAAC,GAAD;GAA8B;GAAO,OAAO;GAAG,UAAU;GAAgB,EAArD,EAAqD,CACzE,EAGD,EAAc,aAAa,KAAK,GAAY,MAC3C,kBAAC,OAAD;GAEE,WAAU;GACV,OAAO,EAAE,iBAAiB,EAAW,SAAS,uBAAuB;aAHvE,CAKG,EAAW,QAAQ,KAAK,GAAO,MAC9B,kBAAC,GAAD;IAA8B;IAAO,OAAO;IAAG,UAAU;IAAgB,EAArD,EAAqD,CACzE,EACD,EAAW,YAAY,CAAC,EAAW,UAAU,kBAAC,KAAD;IAAG,WAAU;cAA0B,EAAW;IAAa,CAAA,CACzG;KARC,cAAc,IAQf,CACN,CACE;MA7BJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD,EAAA,UAAG,yBAAyB,CAAA,EAC5B,kBAAC,KAAD;IAAG,WAAU;cAAe;IAAoC,CAAA,CAC5D;;EACF,CAAA;EA0BV"}
|
|
1
|
+
{"version":3,"file":"BlockKitRenderer.js","names":[],"sources":["../../../../src/bigconsole/renderers/adapters/BlockKitRenderer.tsx"],"sourcesContent":["/**\n * Slack Block Kit Renderer (v2.0)\n *\n * Renders widgets using Slack Block Kit format.\n * Supports common block types: section, divider, header, context, image, actions.\n */\n\nimport React, { memo, useCallback, useMemo, type ReactNode } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { RendererProps, BlockKitTemplate, BlockKitElement, BlockKitText } from '../types/renderer';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Render Block Kit text (plain_text or mrkdwn)\n * Uses React elements instead of dangerouslySetInnerHTML for better security\n */\nfunction renderText(text: BlockKitText | string | undefined): React.ReactNode {\n if (!text) return null;\n\n if (typeof text === 'string') {\n return text;\n }\n\n if (text.type === 'mrkdwn') {\n // Parse mrkdwn and convert to React elements\n const content = text.text || '';\n\n // Parse the markdown into React elements\n const elements: React.ReactNode[] = [];\n let remaining = content;\n let key = 0;\n\n // Process text segment by segment\n while (remaining.length > 0) {\n // Try to match bold: *text*\n const boldMatch = remaining.match(/^\\*([^*]+)\\*/);\n if (boldMatch) {\n elements.push(<strong key={key++}>{boldMatch[1]}</strong>);\n remaining = remaining.slice(boldMatch[0].length);\n continue;\n }\n\n // Try to match italic: _text_\n const italicMatch = remaining.match(/^_([^_]+)_/);\n if (italicMatch) {\n elements.push(<em key={key++}>{italicMatch[1]}</em>);\n remaining = remaining.slice(italicMatch[0].length);\n continue;\n }\n\n // Try to match strike: ~text~\n const strikeMatch = remaining.match(/^~([^~]+)~/);\n if (strikeMatch) {\n elements.push(<del key={key++}>{strikeMatch[1]}</del>);\n remaining = remaining.slice(strikeMatch[0].length);\n continue;\n }\n\n // Try to match code: `text`\n const codeMatch = remaining.match(/^`([^`]+)`/);\n if (codeMatch) {\n elements.push(\n <code key={key++} className=\"bg-bg-sunken px-1 rounded text-sm\">\n {codeMatch[1]}\n </code>\n );\n remaining = remaining.slice(codeMatch[0].length);\n continue;\n }\n\n // Try to match link with text: <url|text>\n const linkTextMatch = remaining.match(/^<([^|>]+)\\|([^>]+)>/);\n if (linkTextMatch) {\n elements.push(\n <a\n key={key++}\n href={linkTextMatch[1]}\n className=\"text-blue-600 dark:text-blue-400 underline\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {linkTextMatch[2]}\n </a>\n );\n remaining = remaining.slice(linkTextMatch[0].length);\n continue;\n }\n\n // Try to match plain link: <url>\n const linkMatch = remaining.match(/^<([^|>]+)>/);\n if (linkMatch) {\n elements.push(\n <a\n key={key++}\n href={linkMatch[1]}\n className=\"text-blue-600 dark:text-blue-400 underline\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {linkMatch[1]}\n </a>\n );\n remaining = remaining.slice(linkMatch[0].length);\n continue;\n }\n\n // No special formatting found at start - extract plain text until next special char\n const plainMatch = remaining.match(/^[^*_~`<]+/);\n if (plainMatch) {\n elements.push(<React.Fragment key={key++}>{plainMatch[0]}</React.Fragment>);\n remaining = remaining.slice(plainMatch[0].length);\n continue;\n }\n\n // Single special char that didn't match a pattern - output as-is\n elements.push(<React.Fragment key={key++}>{remaining[0]}</React.Fragment>);\n remaining = remaining.slice(1);\n }\n\n return <span>{elements}</span>;\n }\n\n // Plain text with optional emoji\n return text.text;\n}\n\n/**\n * Get button style class\n */\nfunction getButtonStyleClass(style?: string): string {\n switch (style) {\n case 'primary':\n return 'bg-status-success-bg hover:bg-status-success-bg/90 text-white transition-colors';\n case 'danger':\n return 'bg-status-error-bg hover:bg-status-error-bg/90 text-white transition-colors';\n default:\n return 'bg-bg-sunken hover:bg-bg-sunken/80 text-primary transition-colors';\n }\n}\n\n// ============================================================================\n// Template Binding\n// ============================================================================\n\nfunction bindDataToTemplate<T>(template: T, data: Record<string, unknown>): T {\n if (template === null || template === undefined) {\n return template;\n }\n\n if (typeof template === 'string') {\n // Replace ${path} with actual values\n return template.replace(/\\$\\{([^}]+)\\}/g, (_, path) => {\n const value = getNestedValue(data, path.trim());\n return value !== undefined ? String(value) : '';\n }) as T;\n }\n\n if (Array.isArray(template)) {\n return template.map((item) => bindDataToTemplate(item, data)) as T;\n }\n\n if (typeof template === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(template)) {\n result[key] = bindDataToTemplate(value, data);\n }\n return result as T;\n }\n\n return template;\n}\n\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\n const parts = path.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current && typeof current === 'object' && part in (current as Record<string, unknown>)) {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n\n return current;\n}\n\n// ============================================================================\n// Block Renderers\n// ============================================================================\n\ninterface BlockRendererProps {\n block: BlockKitElement;\n index: number;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst BlockRenderer: React.FC<BlockRendererProps> = memo(function BlockRenderer({ block, index: _index, onAction }) {\n switch (block.type) {\n // Header block\n case 'header':\n return (\n <div className=\"py-2\">\n <h2 className=\"text-lg font-bold text-primary\">{renderText(block.text)}</h2>\n </div>\n );\n\n // Section block\n case 'section':\n return (\n <div className=\"py-2 flex items-start gap-4\">\n <div className=\"flex-1\">\n {block.text && <p className=\"text-primary\">{renderText(block.text)}</p>}\n {block.fields && block.fields.length > 0 && (\n <div className=\"grid grid-cols-2 gap-2 mt-2\">\n {block.fields.map((field, i) => (\n <div key={i} className=\"text-sm\">\n {renderText(field)}\n </div>\n ))}\n </div>\n )}\n </div>\n {block.accessory && (\n <div className=\"flex-shrink-0\">\n <AccessoryRenderer accessory={block.accessory as BlockKitElement} onAction={onAction} />\n </div>\n )}\n </div>\n );\n\n // Divider block\n case 'divider':\n return <hr className=\"my-2 border-default\" />;\n\n // Image block\n case 'image':\n return (\n <div className=\"py-2\">\n {block.title && <p className=\"text-sm text-secondary mb-1\">{renderText(block.title)}</p>}\n <img src={block.image_url} alt={block.alt_text || ''} className=\"max-w-full h-auto rounded\" />\n </div>\n );\n\n // Context block\n case 'context':\n return (\n <div className=\"py-1 flex items-center gap-2 flex-wrap text-sm text-secondary\">\n {block.elements?.map((element, i) => (\n <span key={i}>\n {element.type === 'image' ? (\n <img src={element.image_url} alt={element.alt_text || ''} className=\"w-5 h-5 rounded inline-block\" />\n ) : (\n renderText(element.text as BlockKitText)\n )}\n </span>\n ))}\n </div>\n );\n\n // Actions block\n case 'actions':\n return (\n <div className=\"py-2 flex gap-2 flex-wrap\">\n {block.elements?.map((element, i) => (\n <ActionRenderer key={i} element={element} onAction={onAction} />\n ))}\n </div>\n );\n\n // Input block (simplified)\n case 'input':\n return (\n <div className=\"py-2\">\n {block.label ? (\n <label className=\"block text-sm font-medium text-secondary mb-1\">\n {renderText(block.label as BlockKitText) as ReactNode}\n </label>\n ) : null}\n <input\n type=\"text\"\n className=\"w-full px-3 py-2 border border-default rounded-md bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n placeholder={(() => {\n const element = block.element as Record<string, unknown> | undefined;\n return element?.placeholder ? (renderText(element.placeholder as BlockKitText) as string) : '';\n })()}\n />\n </div>\n );\n\n // Rich text block (simplified)\n case 'rich_text':\n return (\n <div className=\"py-2 text-primary\">\n {block.elements?.map((element, i) => (\n <span key={i}>{renderText(element.text as BlockKitText)}</span>\n ))}\n </div>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Accessory Renderer\n// ============================================================================\n\ninterface AccessoryRendererProps {\n accessory: BlockKitElement;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst AccessoryRenderer: React.FC<AccessoryRendererProps> = memo(function AccessoryRenderer({ accessory, onAction }) {\n switch (accessory.type) {\n case 'image':\n return (\n <img src={accessory.image_url} alt={accessory.alt_text || ''} className=\"w-12 h-12 rounded object-cover\" />\n );\n\n case 'button':\n return (\n <button\n type=\"button\"\n onClick={() => onAction?.(accessory.action_id || '', accessory.value)}\n className={`px-3 py-1.5 text-sm font-medium rounded ${getButtonStyleClass(accessory.style)}`}\n >\n {renderText(accessory.text)}\n </button>\n );\n\n case 'overflow':\n return (\n <button type=\"button\" className=\"p-1 text-secondary hover:text-primary transition-colors\">\n <svg className=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z\" />\n </svg>\n </button>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Action Renderer\n// ============================================================================\n\ninterface ActionRendererProps {\n element: BlockKitElement;\n onAction?: (actionId: string, value?: string, data?: Record<string, unknown>) => void;\n}\n\nconst ActionRenderer: React.FC<ActionRendererProps> = memo(function ActionRenderer({ element, onAction }) {\n const handleClick = useCallback(() => {\n if (element.action_id) {\n onAction?.(element.action_id, element.value);\n }\n if (element.url) {\n window.open(element.url, '_blank');\n }\n }, [element, onAction]);\n\n switch (element.type) {\n case 'button':\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n className={`px-4 py-2 text-sm font-medium rounded ${getButtonStyleClass(element.style)}`}\n >\n {renderText(element.text)}\n </button>\n );\n\n case 'static_select':\n return (\n <select\n className=\"px-3 py-2 text-sm border border-default rounded bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n onChange={(e) => onAction?.(element.action_id || '', e.target.value)}\n >\n {element.placeholder ? (\n <option value=\"\">{renderText(element.placeholder as BlockKitText) as ReactNode}</option>\n ) : null}\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <option key={i} value={option.value}>\n {renderText(option.text)}\n </option>\n ))}\n </select>\n );\n\n case 'datepicker':\n return (\n <input\n type=\"date\"\n className=\"px-3 py-2 text-sm border border-default rounded bg-surface text-primary transition-colors focus:ring-2 focus:ring-primary/20\"\n onChange={(e) => onAction?.(element.action_id || '', e.target.value)}\n />\n );\n\n case 'checkboxes':\n return (\n <div className=\"space-y-1\">\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <label key={i} className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n className=\"rounded border-default transition-colors\"\n onChange={(e) => onAction?.(element.action_id || '', option.value, { checked: e.target.checked })}\n />\n {renderText(option.text)}\n </label>\n ))}\n </div>\n );\n\n case 'radio_buttons':\n return (\n <div className=\"space-y-1\">\n {(element.options as Array<{ text: BlockKitText; value: string }>)?.map((option, i) => (\n <label key={i} className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name={element.action_id}\n className=\"border-default transition-colors\"\n onChange={() => onAction?.(element.action_id || '', option.value)}\n />\n {renderText(option.text)}\n </label>\n ))}\n </div>\n );\n\n default:\n return null;\n }\n});\n\n// ============================================================================\n// Auto-generate BlockKit blocks from arbitrary data\n// ============================================================================\n\nfunction generateBlocksFromData(data: Record<string, unknown>): BlockKitElement[] {\n const blocks: BlockKitElement[] = [];\n\n for (const [key, value] of Object.entries(data)) {\n if (value === null || value === undefined) continue;\n\n // Handle arrays\n if (Array.isArray(value)) {\n // Add header for the array\n blocks.push({\n type: 'header',\n text: {\n type: 'plain_text',\n text: formatKey(key),\n emoji: true,\n },\n });\n\n // Add each item\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n // Object items - render as section with fields\n const fields: BlockKitText[] = [];\n for (const [itemKey, itemValue] of Object.entries(item)) {\n if (itemValue !== null && itemValue !== undefined) {\n fields.push({\n type: 'mrkdwn',\n text: `*${formatKey(itemKey)}:*\\n${formatValue(itemValue)}`,\n });\n }\n }\n if (fields.length > 0) {\n blocks.push({\n type: 'section',\n fields: fields.slice(0, 10), // BlockKit max 10 fields\n });\n }\n } else {\n // Primitive items - render as simple section\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `${index + 1}. ${formatValue(item)}`,\n },\n });\n }\n\n // Add divider between items (except last)\n if (index < value.length - 1) {\n blocks.push({ type: 'divider' });\n }\n });\n }\n // Handle objects\n else if (typeof value === 'object') {\n blocks.push({\n type: 'header',\n text: {\n type: 'plain_text',\n text: formatKey(key),\n emoji: true,\n },\n });\n\n const fields: BlockKitText[] = [];\n for (const [objKey, objValue] of Object.entries(value as Record<string, unknown>)) {\n if (objValue !== null && objValue !== undefined) {\n fields.push({\n type: 'mrkdwn',\n text: `*${formatKey(objKey)}:*\\n${formatValue(objValue)}`,\n });\n }\n }\n if (fields.length > 0) {\n blocks.push({\n type: 'section',\n fields: fields.slice(0, 10),\n });\n }\n }\n // Handle primitives\n else {\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `*${formatKey(key)}:* ${formatValue(value)}`,\n },\n });\n }\n }\n\n return blocks;\n}\n\nfunction formatKey(key: string): string {\n // Convert camelCase or snake_case to Title Case\n return key\n .replace(/_/g, ' ')\n .replace(/([A-Z])/g, ' $1')\n .replace(/^./, (str) => str.toUpperCase())\n .trim();\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null || value === undefined) return '-';\n if (typeof value === 'boolean') return value ? 'Yes' : 'No';\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n\n// ============================================================================\n// Main Renderer Component\n// ============================================================================\n\nexport const BlockKitRenderer: React.FC<RendererProps> = memo(function BlockKitRenderer({\n widget,\n data,\n template,\n isLoading,\n error,\n onDrilldown: _onDrilldown,\n onAction,\n}) {\n const { t } = useI18n();\n\n // Debug logging\n\n // Parse and bind template\n const boundTemplate = useMemo(() => {\n // 1. If template has blocks, bind data and use it\n if (template && 'blocks' in (template as Record<string, unknown>)) {\n return bindDataToTemplate(template as unknown as BlockKitTemplate, data);\n }\n\n // 2. If data has blocks, use it directly as BlockKit format\n if (data && 'blocks' in data) {\n return data as unknown as BlockKitTemplate;\n }\n\n // 3. If we have any data, auto-generate blocks from it\n if (data && Object.keys(data).length > 0) {\n const generatedBlocks = generateBlocksFromData(data);\n if (generatedBlocks.length > 0) {\n return { blocks: generatedBlocks } as BlockKitTemplate;\n }\n }\n\n // 4. If template exists but no blocks, try to bind and check again\n if (template) {\n const bound = bindDataToTemplate(template as unknown as BlockKitTemplate, data);\n if (bound?.blocks?.length) {\n return bound;\n }\n }\n\n return null;\n }, [template, data]);\n\n // Handle action clicks\n const handleAction = useCallback(\n (actionId: string, value?: string, actionData?: Record<string, unknown>) => {\n if (onAction) {\n onAction(actionId, { value, ...actionData });\n }\n },\n [onAction]\n );\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <div className=\"animate-pulse space-y-2 w-full\">\n <div className=\"h-4 bg-bg-sunken rounded w-3/4\" />\n <div className=\"h-4 bg-bg-sunken rounded w-1/2\" />\n <div className=\"h-4 bg-bg-sunken rounded w-5/6\" />\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-red-600 dark:text-red-400\">\n <div className=\"text-center\">\n <p className=\"font-medium\">\n {t('bigconsole.widget.renderer.blockKit.errorRendering', 'Error rendering blocks')}\n </p>\n <p className=\"text-sm mt-1\">{error}</p>\n </div>\n </div>\n );\n }\n\n // Empty state\n if (!boundTemplate?.blocks?.length) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-secondary animate-in fade-in duration-200\">\n <div className=\"text-center\">\n <p>{t('bigconsole.widget.renderer.blockKit.noContent', 'No content to display')}</p>\n <p className=\"text-sm mt-1\">\n {t('bigconsole.widget.renderer.blockKit.configureBlocks', 'Configure blocks or provide data')}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 bg-surface\">\n {/* Render blocks */}\n {boundTemplate.blocks.map((block, i) => (\n <BlockRenderer key={i} block={block} index={i} onAction={handleAction} />\n ))}\n\n {/* Render attachments */}\n {boundTemplate.attachments?.map((attachment, i) => (\n <div\n key={`attachment-${i}`}\n className=\"mt-4 border-l-4 pl-3\"\n style={{ borderLeftColor: attachment.color || 'var(--color-border)' }}\n >\n {attachment.blocks?.map((block, j) => (\n <BlockRenderer key={j} block={block} index={j} onAction={handleAction} />\n ))}\n {attachment.fallback && !attachment.blocks && <p className=\"text-sm text-secondary\">{attachment.fallback}</p>}\n </div>\n ))}\n </div>\n );\n});\n\nexport default BlockKitRenderer;\n"],"mappings":"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"}
|
|
@@ -1,172 +1,172 @@
|
|
|
1
1
|
import { memo as e, useMemo as t } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useI18n as n } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
3
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
3
4
|
//#region src/bigconsole/renderers/adapters/CustomRenderer.tsx
|
|
4
|
-
var
|
|
5
|
-
function
|
|
6
|
-
|
|
5
|
+
var a = /* @__PURE__ */ new Map();
|
|
6
|
+
function o(e, t) {
|
|
7
|
+
a.set(e, t);
|
|
7
8
|
}
|
|
8
|
-
function
|
|
9
|
-
return
|
|
9
|
+
function s(e) {
|
|
10
|
+
return a.get(e);
|
|
10
11
|
}
|
|
11
|
-
function
|
|
12
|
-
return Array.from(
|
|
12
|
+
function c() {
|
|
13
|
+
return Array.from(a.keys());
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
let
|
|
16
|
-
return
|
|
17
|
-
className: `p-4 text-${
|
|
18
|
-
children: /* @__PURE__ */
|
|
15
|
+
o("SimpleText", ({ data: e, props: t }) => {
|
|
16
|
+
let { t: i } = n(), a = e.text || t?.text, o = t?.align || "left";
|
|
17
|
+
return a ? /* @__PURE__ */ r("div", {
|
|
18
|
+
className: `p-4 text-${o}`,
|
|
19
|
+
children: /* @__PURE__ */ r("p", {
|
|
19
20
|
className: "text-primary",
|
|
20
|
-
children:
|
|
21
|
+
children: a
|
|
21
22
|
})
|
|
22
|
-
}) : /* @__PURE__ */
|
|
23
|
+
}) : /* @__PURE__ */ r("div", {
|
|
23
24
|
className: "flex items-center justify-center h-full p-4 text-secondary",
|
|
24
|
-
children: /* @__PURE__ */
|
|
25
|
+
children: /* @__PURE__ */ r("p", {
|
|
25
26
|
className: "text-sm",
|
|
26
|
-
children: "No data available"
|
|
27
|
+
children: i("bigconsole.widget.renderer.custom.noData", "No data available")
|
|
27
28
|
})
|
|
28
29
|
});
|
|
29
|
-
}),
|
|
30
|
-
let
|
|
31
|
-
if (
|
|
30
|
+
}), o("Counter", ({ data: e, props: t }) => {
|
|
31
|
+
let { t: a } = n(), o = e.value ?? t?.value, s = e.label || t?.label, c = t?.format || "number";
|
|
32
|
+
if (o == null) return /* @__PURE__ */ r("div", {
|
|
32
33
|
className: "flex flex-col items-center justify-center h-full p-4 text-secondary",
|
|
33
|
-
children: /* @__PURE__ */
|
|
34
|
+
children: /* @__PURE__ */ r("p", {
|
|
34
35
|
className: "text-sm",
|
|
35
|
-
children: "No data available"
|
|
36
|
+
children: a("bigconsole.widget.renderer.custom.noData", "No data available")
|
|
36
37
|
})
|
|
37
38
|
});
|
|
38
|
-
let
|
|
39
|
-
return /* @__PURE__ */
|
|
39
|
+
let l = o;
|
|
40
|
+
return /* @__PURE__ */ i("div", {
|
|
40
41
|
className: "flex flex-col items-center justify-center h-full p-4 animate-in fade-in duration-200",
|
|
41
|
-
children: [/* @__PURE__ */
|
|
42
|
+
children: [/* @__PURE__ */ r("span", {
|
|
42
43
|
className: "text-3xl font-bold text-primary",
|
|
43
|
-
children:
|
|
44
|
+
children: c === "currency" ? new Intl.NumberFormat("en-US", {
|
|
44
45
|
style: "currency",
|
|
45
46
|
currency: "USD"
|
|
46
|
-
}).format(
|
|
47
|
-
}),
|
|
47
|
+
}).format(l) : c === "percent" ? `${(l * 100).toFixed(1)}%` : new Intl.NumberFormat().format(l)
|
|
48
|
+
}), s && /* @__PURE__ */ r("span", {
|
|
48
49
|
className: "text-sm text-secondary mt-1",
|
|
49
|
-
children:
|
|
50
|
+
children: s
|
|
50
51
|
})]
|
|
51
52
|
});
|
|
52
|
-
}),
|
|
53
|
-
let
|
|
54
|
-
if (!
|
|
53
|
+
}), o("Card", ({ data: e, props: t, onAction: a }) => {
|
|
54
|
+
let { t: o } = n(), s = e.title || t?.title, c = e.description || t?.description || "";
|
|
55
|
+
if (!s && !c) return /* @__PURE__ */ r("div", {
|
|
55
56
|
className: "flex items-center justify-center h-full p-4 text-secondary",
|
|
56
|
-
children: /* @__PURE__ */
|
|
57
|
+
children: /* @__PURE__ */ r("p", {
|
|
57
58
|
className: "text-sm",
|
|
58
|
-
children: "No data available"
|
|
59
|
+
children: o("bigconsole.widget.renderer.custom.noData", "No data available")
|
|
59
60
|
})
|
|
60
61
|
});
|
|
61
|
-
let
|
|
62
|
-
return /* @__PURE__ */
|
|
62
|
+
let l = e.image || t?.image, u = t?.actionLabel || o("bigconsole.widget.renderer.custom.viewDetails", "View Details"), d = t?.showAction !== !1;
|
|
63
|
+
return /* @__PURE__ */ i("div", {
|
|
63
64
|
className: "flex flex-col h-full bg-surface rounded-lg overflow-hidden animate-in fade-in duration-200",
|
|
64
65
|
children: [
|
|
65
|
-
|
|
66
|
+
l && /* @__PURE__ */ r("div", {
|
|
66
67
|
className: "aspect-video bg-bg-sunken",
|
|
67
|
-
children: /* @__PURE__ */
|
|
68
|
-
src:
|
|
68
|
+
children: /* @__PURE__ */ r("img", {
|
|
69
|
+
src: l,
|
|
69
70
|
alt: "",
|
|
70
71
|
className: "w-full h-full object-cover"
|
|
71
72
|
})
|
|
72
73
|
}),
|
|
73
|
-
/* @__PURE__ */
|
|
74
|
+
/* @__PURE__ */ i("div", {
|
|
74
75
|
className: "flex-1 p-4",
|
|
75
|
-
children: [
|
|
76
|
+
children: [s && /* @__PURE__ */ r("h3", {
|
|
76
77
|
className: "text-lg font-semibold text-primary",
|
|
77
|
-
children:
|
|
78
|
-
}),
|
|
78
|
+
children: s
|
|
79
|
+
}), c && /* @__PURE__ */ r("p", {
|
|
79
80
|
className: "text-sm text-secondary mt-1",
|
|
80
|
-
children:
|
|
81
|
+
children: c
|
|
81
82
|
})]
|
|
82
83
|
}),
|
|
83
|
-
|
|
84
|
+
d && /* @__PURE__ */ r("div", {
|
|
84
85
|
className: "p-4 pt-0",
|
|
85
|
-
children: /* @__PURE__ */
|
|
86
|
+
children: /* @__PURE__ */ r("button", {
|
|
86
87
|
type: "button",
|
|
87
|
-
onClick: () =>
|
|
88
|
+
onClick: () => a?.("click", { title: s }),
|
|
88
89
|
className: "w-full px-4 py-2 text-sm font-medium text-white bg-action-primary hover:bg-action-primary/90 rounded-md transition-colors",
|
|
89
|
-
children:
|
|
90
|
+
children: u
|
|
90
91
|
})
|
|
91
92
|
})
|
|
92
93
|
]
|
|
93
94
|
});
|
|
94
95
|
});
|
|
95
|
-
var
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
className: "text-
|
|
99
|
-
children:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
"
|
|
108
|
-
e
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (a && typeof a == "object" && "componentName" in a) return a.componentName;
|
|
96
|
+
var l = ({ componentName: e }) => {
|
|
97
|
+
let { t } = n();
|
|
98
|
+
return /* @__PURE__ */ r("div", {
|
|
99
|
+
className: "flex flex-col items-center justify-center h-full p-4 text-secondary animate-in fade-in duration-200",
|
|
100
|
+
children: /* @__PURE__ */ i("div", {
|
|
101
|
+
className: "text-center",
|
|
102
|
+
children: [
|
|
103
|
+
/* @__PURE__ */ r("p", {
|
|
104
|
+
className: "font-medium",
|
|
105
|
+
children: t("bigconsole.widget.renderer.custom.componentNotFound", "Component Not Found")
|
|
106
|
+
}),
|
|
107
|
+
/* @__PURE__ */ r("p", {
|
|
108
|
+
className: "text-sm mt-1",
|
|
109
|
+
children: t("bigconsole.widget.renderer.custom.componentNotRegistered", "Component \"{{name}}\" is not registered", { name: e ?? "" })
|
|
110
|
+
}),
|
|
111
|
+
/* @__PURE__ */ i("div", {
|
|
112
|
+
className: "mt-4 text-xs",
|
|
113
|
+
children: [/* @__PURE__ */ r("p", { children: t("bigconsole.widget.renderer.custom.availableComponents", "Available components:") }), /* @__PURE__ */ r("ul", {
|
|
114
|
+
className: "mt-1",
|
|
115
|
+
children: c().map((e) => /* @__PURE__ */ r("li", {
|
|
116
|
+
className: "text-link",
|
|
117
|
+
children: e
|
|
118
|
+
}, e))
|
|
119
|
+
})]
|
|
120
|
+
})
|
|
121
|
+
]
|
|
122
|
+
})
|
|
123
|
+
});
|
|
124
|
+
}, u = e(function({ widget: e, data: a, template: o, isLoading: c, error: u, onDrilldown: d, onAction: f }) {
|
|
125
|
+
let { t: p } = n(), m = t(() => {
|
|
126
|
+
if (o && typeof o == "object" && "componentName" in o) return o.componentName;
|
|
127
127
|
let t = e.config;
|
|
128
128
|
return t?.componentName || t?.componentId || "";
|
|
129
|
-
}, [
|
|
130
|
-
return
|
|
129
|
+
}, [o, e.config]), h = t(() => o && typeof o == "object" && "props" in o ? o.props : {}, [o]), g = t(() => m ? s(m) : null, [m]);
|
|
130
|
+
return c ? /* @__PURE__ */ r("div", {
|
|
131
131
|
className: "flex items-center justify-center h-full p-4 animate-in fade-in duration-200",
|
|
132
|
-
children: /* @__PURE__ */
|
|
132
|
+
children: /* @__PURE__ */ i("div", {
|
|
133
133
|
className: "animate-pulse space-y-2 w-full",
|
|
134
|
-
children: [/* @__PURE__ */
|
|
134
|
+
children: [/* @__PURE__ */ r("div", { className: "h-4 bg-bg-sunken rounded w-3/4" }), /* @__PURE__ */ r("div", { className: "h-4 bg-bg-sunken rounded w-1/2" })]
|
|
135
135
|
})
|
|
136
|
-
}) :
|
|
136
|
+
}) : u ? /* @__PURE__ */ r("div", {
|
|
137
137
|
className: "flex items-center justify-center h-full p-4 text-red-600 dark:text-red-400",
|
|
138
|
-
children: /* @__PURE__ */
|
|
138
|
+
children: /* @__PURE__ */ i("div", {
|
|
139
139
|
className: "text-center",
|
|
140
|
-
children: [/* @__PURE__ */
|
|
140
|
+
children: [/* @__PURE__ */ r("p", {
|
|
141
141
|
className: "font-medium",
|
|
142
|
-
children: "Error rendering component"
|
|
143
|
-
}), /* @__PURE__ */
|
|
142
|
+
children: p("bigconsole.widget.renderer.custom.errorRendering", "Error rendering component")
|
|
143
|
+
}), /* @__PURE__ */ r("p", {
|
|
144
144
|
className: "text-sm mt-1",
|
|
145
|
-
children:
|
|
145
|
+
children: u
|
|
146
146
|
})]
|
|
147
147
|
})
|
|
148
|
-
}) :
|
|
148
|
+
}) : m ? g ? /* @__PURE__ */ r(g, {
|
|
149
149
|
widget: e,
|
|
150
|
-
data:
|
|
151
|
-
template:
|
|
152
|
-
props:
|
|
153
|
-
onAction:
|
|
154
|
-
onDrilldown:
|
|
155
|
-
}) : /* @__PURE__ */
|
|
150
|
+
data: a,
|
|
151
|
+
template: o,
|
|
152
|
+
props: h,
|
|
153
|
+
onAction: f,
|
|
154
|
+
onDrilldown: d
|
|
155
|
+
}) : /* @__PURE__ */ r(l, { componentName: m }) : /* @__PURE__ */ r("div", {
|
|
156
156
|
className: "flex items-center justify-center h-full p-4 text-secondary animate-in fade-in duration-200",
|
|
157
|
-
children: /* @__PURE__ */
|
|
157
|
+
children: /* @__PURE__ */ i("div", {
|
|
158
158
|
className: "text-center",
|
|
159
|
-
children: [/* @__PURE__ */
|
|
159
|
+
children: [/* @__PURE__ */ r("p", {
|
|
160
160
|
className: "font-medium",
|
|
161
|
-
children: "No Component Specified"
|
|
162
|
-
}), /* @__PURE__ */
|
|
161
|
+
children: p("bigconsole.widget.renderer.custom.noComponentSpecified", "No Component Specified")
|
|
162
|
+
}), /* @__PURE__ */ r("p", {
|
|
163
163
|
className: "text-sm mt-1",
|
|
164
|
-
children: "Set componentName in template or widget config"
|
|
164
|
+
children: p("bigconsole.widget.renderer.custom.setComponentName", "Set componentName in template or widget config")
|
|
165
165
|
})]
|
|
166
166
|
})
|
|
167
167
|
});
|
|
168
168
|
});
|
|
169
169
|
//#endregion
|
|
170
|
-
export {
|
|
170
|
+
export { u as default };
|
|
171
171
|
|
|
172
172
|
//# sourceMappingURL=CustomRenderer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CustomRenderer.js","names":[],"sources":["../../../../src/bigconsole/renderers/adapters/CustomRenderer.tsx"],"sourcesContent":["/**\n * Custom Renderer (v2.0)\n *\n * Renders widgets using custom React components registered by the application.\n * Supports dynamic component resolution and prop mapping.\n */\n\nimport React, { memo, useMemo, type ComponentType } from 'react';\nimport type { RendererProps } from '../types/renderer';\n\n// ============================================================================\n// Custom Component Registry\n// ============================================================================\n\n// Registry of custom components\nconst customComponentRegistry = new Map<string, ComponentType<CustomComponentProps>>();\n\nexport interface CustomComponentProps {\n widget: RendererProps['widget'];\n data: Record<string, unknown>;\n template?: Record<string, unknown> | null;\n props?: Record<string, unknown>;\n onAction?: (actionName: string, payload: Record<string, unknown>) => void;\n onDrilldown?: (params: Record<string, unknown>) => void;\n}\n\n/**\n * Register a custom component\n */\nexport function registerCustomComponent(name: string, component: ComponentType<CustomComponentProps>): void {\n customComponentRegistry.set(name, component);\n}\n\n/**\n * Unregister a custom component\n */\nexport function unregisterCustomComponent(name: string): boolean {\n return customComponentRegistry.delete(name);\n}\n\n/**\n * Check if a custom component is registered\n */\nexport function hasCustomComponent(name: string): boolean {\n return customComponentRegistry.has(name);\n}\n\n/**\n * Get a registered custom component\n */\nexport function getCustomComponent(name: string): ComponentType<CustomComponentProps> | undefined {\n return customComponentRegistry.get(name);\n}\n\n/**\n * Get all registered component names\n */\nexport function getRegisteredComponentNames(): string[] {\n return Array.from(customComponentRegistry.keys());\n}\n\n// ============================================================================\n// Default Custom Components\n// ============================================================================\n\n/**\n * Simple Text Display Component\n */\nexport const SimpleTextComponent: React.FC<CustomComponentProps> = ({ data, props }) => {\n const text = (data.text || props?.text) as string | undefined;\n const align = (props?.align || 'left') as string;\n\n if (!text) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-secondary\">\n <p className=\"text-sm\">No data available</p>\n </div>\n );\n }\n\n return (\n <div className={`p-4 text-${align}`}>\n <p className=\"text-primary\">{text}</p>\n </div>\n );\n};\n\n/**\n * Simple Counter Component\n */\nexport const CounterComponent: React.FC<CustomComponentProps> = ({ data, props }) => {\n const rawValue = data.value ?? props?.value;\n const label = (data.label || props?.label) as string | undefined;\n const format = (props?.format || 'number') as string;\n\n if (rawValue === undefined || rawValue === null) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full p-4 text-secondary\">\n <p className=\"text-sm\">No data available</p>\n </div>\n );\n }\n\n const value = rawValue as number;\n\n const formattedValue =\n format === 'currency'\n ? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value)\n : format === 'percent'\n ? `${(value * 100).toFixed(1)}%`\n : new Intl.NumberFormat().format(value);\n\n return (\n <div className=\"flex flex-col items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <span className=\"text-3xl font-bold text-primary\">{formattedValue}</span>\n {label && <span className=\"text-sm text-secondary mt-1\">{label}</span>}\n </div>\n );\n};\n\n/**\n * Simple Card Component\n */\nexport const CardComponent: React.FC<CustomComponentProps> = ({ data, props, onAction }) => {\n const title = (data.title || props?.title) as string | undefined;\n const description = (data.description || props?.description || '') as string;\n\n if (!title && !description) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-secondary\">\n <p className=\"text-sm\">No data available</p>\n </div>\n );\n }\n const image = (data.image || props?.image) as string | undefined;\n const actionLabel = (props?.actionLabel || 'View Details') as string;\n const showAction = props?.showAction !== false;\n\n return (\n <div className=\"flex flex-col h-full bg-surface rounded-lg overflow-hidden animate-in fade-in duration-200\">\n {image && (\n <div className=\"aspect-video bg-bg-sunken\">\n <img src={image} alt=\"\" className=\"w-full h-full object-cover\" />\n </div>\n )}\n <div className=\"flex-1 p-4\">\n {title && <h3 className=\"text-lg font-semibold text-primary\">{title}</h3>}\n {description && <p className=\"text-sm text-secondary mt-1\">{description}</p>}\n </div>\n {showAction && (\n <div className=\"p-4 pt-0\">\n <button\n type=\"button\"\n onClick={() => onAction?.('click', { title })}\n className=\"w-full px-4 py-2 text-sm font-medium text-white bg-action-primary hover:bg-action-primary/90 rounded-md transition-colors\"\n >\n {actionLabel}\n </button>\n </div>\n )}\n </div>\n );\n};\n\n// Register default components\nregisterCustomComponent('SimpleText', SimpleTextComponent);\nregisterCustomComponent('Counter', CounterComponent);\nregisterCustomComponent('Card', CardComponent);\n\n// ============================================================================\n// Fallback Component\n// ============================================================================\n\nconst FallbackComponent: React.FC<{ componentName: string }> = ({ componentName }) => (\n <div className=\"flex flex-col items-center justify-center h-full p-4 text-secondary animate-in fade-in duration-200\">\n <div className=\"text-center\">\n <p className=\"font-medium\">Component Not Found</p>\n <p className=\"text-sm mt-1\">Component "{componentName}" is not registered</p>\n <div className=\"mt-4 text-xs\">\n <p>Available components:</p>\n <ul className=\"mt-1\">\n {getRegisteredComponentNames().map((name) => (\n <li key={name} className=\"text-link\">\n {name}\n </li>\n ))}\n </ul>\n </div>\n </div>\n </div>\n);\n\n// ============================================================================\n// Main Renderer Component\n// ============================================================================\n\nexport const CustomRenderer: React.FC<RendererProps> = memo(function CustomRenderer({\n widget,\n data,\n template,\n isLoading,\n error,\n onDrilldown,\n onAction,\n}) {\n // Get component name from template\n const componentName = useMemo(() => {\n if (template && typeof template === 'object' && 'componentName' in template) {\n return template.componentName as string;\n }\n // Fallback to widget config\n const config = widget.config as Record<string, unknown>;\n return (config?.componentName || config?.componentId || '') as string;\n }, [template, widget.config]);\n\n // Get additional props from template\n const componentProps = useMemo(() => {\n if (template && typeof template === 'object' && 'props' in template) {\n return template.props as Record<string, unknown>;\n }\n return {};\n }, [template]);\n\n // Get the component\n const Component = useMemo(() => {\n if (!componentName) return null;\n return getCustomComponent(componentName);\n }, [componentName]);\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <div className=\"animate-pulse space-y-2 w-full\">\n <div className=\"h-4 bg-bg-sunken rounded w-3/4\" />\n <div className=\"h-4 bg-bg-sunken rounded w-1/2\" />\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-red-600 dark:text-red-400\">\n <div className=\"text-center\">\n <p className=\"font-medium\">Error rendering component</p>\n <p className=\"text-sm mt-1\">{error}</p>\n </div>\n </div>\n );\n }\n\n // No component name\n if (!componentName) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-secondary animate-in fade-in duration-200\">\n <div className=\"text-center\">\n <p className=\"font-medium\">No Component Specified</p>\n <p className=\"text-sm mt-1\">Set componentName in template or widget config</p>\n </div>\n </div>\n );\n }\n\n // Component not found\n if (!Component) {\n return <FallbackComponent componentName={componentName} />;\n }\n\n // Render the custom component\n return (\n <Component\n widget={widget}\n data={data}\n template={template}\n props={componentProps}\n onAction={onAction}\n onDrilldown={onDrilldown}\n />\n );\n});\n\nexport default CustomRenderer;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CustomRenderer.js","names":[],"sources":["../../../../src/bigconsole/renderers/adapters/CustomRenderer.tsx"],"sourcesContent":["/**\n * Custom Renderer (v2.0)\n *\n * Renders widgets using custom React components registered by the application.\n * Supports dynamic component resolution and prop mapping.\n */\n\nimport React, { memo, useMemo, type ComponentType } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { RendererProps } from '../types/renderer';\n\n// ============================================================================\n// Custom Component Registry\n// ============================================================================\n\n// Registry of custom components\nconst customComponentRegistry = new Map<string, ComponentType<CustomComponentProps>>();\n\nexport interface CustomComponentProps {\n widget: RendererProps['widget'];\n data: Record<string, unknown>;\n template?: Record<string, unknown> | null;\n props?: Record<string, unknown>;\n onAction?: (actionName: string, payload: Record<string, unknown>) => void;\n onDrilldown?: (params: Record<string, unknown>) => void;\n}\n\n/**\n * Register a custom component\n */\nexport function registerCustomComponent(name: string, component: ComponentType<CustomComponentProps>): void {\n customComponentRegistry.set(name, component);\n}\n\n/**\n * Unregister a custom component\n */\nexport function unregisterCustomComponent(name: string): boolean {\n return customComponentRegistry.delete(name);\n}\n\n/**\n * Check if a custom component is registered\n */\nexport function hasCustomComponent(name: string): boolean {\n return customComponentRegistry.has(name);\n}\n\n/**\n * Get a registered custom component\n */\nexport function getCustomComponent(name: string): ComponentType<CustomComponentProps> | undefined {\n return customComponentRegistry.get(name);\n}\n\n/**\n * Get all registered component names\n */\nexport function getRegisteredComponentNames(): string[] {\n return Array.from(customComponentRegistry.keys());\n}\n\n// ============================================================================\n// Default Custom Components\n// ============================================================================\n\n/**\n * Simple Text Display Component\n */\nexport const SimpleTextComponent: React.FC<CustomComponentProps> = ({ data, props }) => {\n const { t } = useI18n();\n const text = (data.text || props?.text) as string | undefined;\n const align = (props?.align || 'left') as string;\n\n if (!text) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-secondary\">\n <p className=\"text-sm\">{t('bigconsole.widget.renderer.custom.noData', 'No data available')}</p>\n </div>\n );\n }\n\n return (\n <div className={`p-4 text-${align}`}>\n <p className=\"text-primary\">{text}</p>\n </div>\n );\n};\n\n/**\n * Simple Counter Component\n */\nexport const CounterComponent: React.FC<CustomComponentProps> = ({ data, props }) => {\n const { t } = useI18n();\n const rawValue = data.value ?? props?.value;\n const label = (data.label || props?.label) as string | undefined;\n const format = (props?.format || 'number') as string;\n\n if (rawValue === undefined || rawValue === null) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full p-4 text-secondary\">\n <p className=\"text-sm\">{t('bigconsole.widget.renderer.custom.noData', 'No data available')}</p>\n </div>\n );\n }\n\n const value = rawValue as number;\n\n const formattedValue =\n format === 'currency'\n ? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value)\n : format === 'percent'\n ? `${(value * 100).toFixed(1)}%`\n : new Intl.NumberFormat().format(value);\n\n return (\n <div className=\"flex flex-col items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <span className=\"text-3xl font-bold text-primary\">{formattedValue}</span>\n {label && <span className=\"text-sm text-secondary mt-1\">{label}</span>}\n </div>\n );\n};\n\n/**\n * Simple Card Component\n */\nexport const CardComponent: React.FC<CustomComponentProps> = ({ data, props, onAction }) => {\n const { t } = useI18n();\n const title = (data.title || props?.title) as string | undefined;\n const description = (data.description || props?.description || '') as string;\n\n if (!title && !description) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-secondary\">\n <p className=\"text-sm\">{t('bigconsole.widget.renderer.custom.noData', 'No data available')}</p>\n </div>\n );\n }\n const image = (data.image || props?.image) as string | undefined;\n const actionLabel = (props?.actionLabel ||\n t('bigconsole.widget.renderer.custom.viewDetails', 'View Details')) as string;\n const showAction = props?.showAction !== false;\n\n return (\n <div className=\"flex flex-col h-full bg-surface rounded-lg overflow-hidden animate-in fade-in duration-200\">\n {image && (\n <div className=\"aspect-video bg-bg-sunken\">\n <img src={image} alt=\"\" className=\"w-full h-full object-cover\" />\n </div>\n )}\n <div className=\"flex-1 p-4\">\n {title && <h3 className=\"text-lg font-semibold text-primary\">{title}</h3>}\n {description && <p className=\"text-sm text-secondary mt-1\">{description}</p>}\n </div>\n {showAction && (\n <div className=\"p-4 pt-0\">\n <button\n type=\"button\"\n onClick={() => onAction?.('click', { title })}\n className=\"w-full px-4 py-2 text-sm font-medium text-white bg-action-primary hover:bg-action-primary/90 rounded-md transition-colors\"\n >\n {actionLabel}\n </button>\n </div>\n )}\n </div>\n );\n};\n\n// Register default components\nregisterCustomComponent('SimpleText', SimpleTextComponent);\nregisterCustomComponent('Counter', CounterComponent);\nregisterCustomComponent('Card', CardComponent);\n\n// ============================================================================\n// Fallback Component\n// ============================================================================\n\nconst FallbackComponent: React.FC<{ componentName: string }> = ({ componentName }) => {\n const { t } = useI18n();\n return (\n <div className=\"flex flex-col items-center justify-center h-full p-4 text-secondary animate-in fade-in duration-200\">\n <div className=\"text-center\">\n <p className=\"font-medium\">{t('bigconsole.widget.renderer.custom.componentNotFound', 'Component Not Found')}</p>\n <p className=\"text-sm mt-1\">\n {t('bigconsole.widget.renderer.custom.componentNotRegistered', 'Component \"{{name}}\" is not registered', {\n name: componentName ?? '',\n })}\n </p>\n <div className=\"mt-4 text-xs\">\n <p>{t('bigconsole.widget.renderer.custom.availableComponents', 'Available components:')}</p>\n <ul className=\"mt-1\">\n {getRegisteredComponentNames().map((name) => (\n <li key={name} className=\"text-link\">\n {name}\n </li>\n ))}\n </ul>\n </div>\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Main Renderer Component\n// ============================================================================\n\nexport const CustomRenderer: React.FC<RendererProps> = memo(function CustomRenderer({\n widget,\n data,\n template,\n isLoading,\n error,\n onDrilldown,\n onAction,\n}) {\n const { t } = useI18n();\n\n // Get component name from template\n const componentName = useMemo(() => {\n if (template && typeof template === 'object' && 'componentName' in template) {\n return template.componentName as string;\n }\n // Fallback to widget config\n const config = widget.config as Record<string, unknown>;\n return (config?.componentName || config?.componentId || '') as string;\n }, [template, widget.config]);\n\n // Get additional props from template\n const componentProps = useMemo(() => {\n if (template && typeof template === 'object' && 'props' in template) {\n return template.props as Record<string, unknown>;\n }\n return {};\n }, [template]);\n\n // Get the component\n const Component = useMemo(() => {\n if (!componentName) return null;\n return getCustomComponent(componentName);\n }, [componentName]);\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 animate-in fade-in duration-200\">\n <div className=\"animate-pulse space-y-2 w-full\">\n <div className=\"h-4 bg-bg-sunken rounded w-3/4\" />\n <div className=\"h-4 bg-bg-sunken rounded w-1/2\" />\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-red-600 dark:text-red-400\">\n <div className=\"text-center\">\n <p className=\"font-medium\">\n {t('bigconsole.widget.renderer.custom.errorRendering', 'Error rendering component')}\n </p>\n <p className=\"text-sm mt-1\">{error}</p>\n </div>\n </div>\n );\n }\n\n // No component name\n if (!componentName) {\n return (\n <div className=\"flex items-center justify-center h-full p-4 text-secondary animate-in fade-in duration-200\">\n <div className=\"text-center\">\n <p className=\"font-medium\">\n {t('bigconsole.widget.renderer.custom.noComponentSpecified', 'No Component Specified')}\n </p>\n <p className=\"text-sm mt-1\">\n {t('bigconsole.widget.renderer.custom.setComponentName', 'Set componentName in template or widget config')}\n </p>\n </div>\n </div>\n );\n }\n\n // Component not found\n if (!Component) {\n return <FallbackComponent componentName={componentName} />;\n }\n\n // Render the custom component\n return (\n <Component\n widget={widget}\n data={data}\n template={template}\n props={componentProps}\n onAction={onAction}\n onDrilldown={onDrilldown}\n />\n );\n});\n\nexport default CustomRenderer;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-bigconsole",
|
|
3
|
-
"version": "2026.805.
|
|
3
|
+
"version": "2026.805.3",
|
|
4
4
|
"description": "BigConsole - AI-powered analytics and dashboard platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"dev": "vite",
|
|
29
29
|
"build": "tsc && bash scripts/build-limits.sh --gate vite build",
|
|
30
30
|
"codegen": "graphql-codegen --project wspace && graphql-codegen --project global",
|
|
31
|
+
"i18n:bundle": "bun run scripts/build-i18n-bundle.ts",
|
|
31
32
|
"lint": "eslint .",
|
|
32
33
|
"lint:sanity": "if [ \"${CI:-}\" = \"true\" ]; then bun run lint; else bun run lint -- --cache --cache-location .eslintcache; fi",
|
|
33
34
|
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|