@burdenoff/microfe-bigconsole 2026.805.1 → 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.
Files changed (150) hide show
  1. package/dist/bigconsole/AdminRoot.js +21 -16
  2. package/dist/bigconsole/AdminRoot.js.map +1 -1
  3. package/dist/bigconsole/BigConsoleRoot.js +44 -39
  4. package/dist/bigconsole/BigConsoleRoot.js.map +1 -1
  5. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +109 -113
  6. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js.map +1 -1
  7. package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +115 -113
  8. package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
  9. package/dist/bigconsole/components/dashboard/DashboardCanvas.js +71 -62
  10. package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
  11. package/dist/bigconsole/components/dashboard/DashboardToolbar.js +116 -100
  12. package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
  13. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +56 -54
  14. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
  15. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js +126 -125
  16. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js.map +1 -1
  17. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +151 -150
  18. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js.map +1 -1
  19. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js +77 -76
  20. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js.map +1 -1
  21. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +97 -96
  22. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
  23. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +172 -191
  24. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
  25. package/dist/bigconsole/components/dashboard/PageTabsManager/PageTabsManager.js +100 -98
  26. package/dist/bigconsole/components/dashboard/PageTabsManager/PageTabsManager.js.map +1 -1
  27. package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js +75 -83
  28. package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js.map +1 -1
  29. package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +151 -154
  30. package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js.map +1 -1
  31. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +187 -193
  32. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js.map +1 -1
  33. package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js +1 -0
  34. package/dist/bigconsole/components/dashboard/WidgetPalette.js +110 -108
  35. package/dist/bigconsole/components/dashboard/WidgetPalette.js.map +1 -1
  36. package/dist/bigconsole/components/datasink/CacheTTLSelector.js +102 -103
  37. package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
  38. package/dist/bigconsole/components/datasink/DataSinkCard.js +20 -20
  39. package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
  40. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +36 -41
  41. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js.map +1 -1
  42. package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +38 -37
  43. package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js.map +1 -1
  44. package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +113 -117
  45. package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js.map +1 -1
  46. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +182 -197
  47. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
  48. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +166 -168
  49. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js.map +1 -1
  50. package/dist/bigconsole/components/parser/DataSinkSelector.js +87 -86
  51. package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
  52. package/dist/bigconsole/components/parser/ImportParserDialog.js +196 -211
  53. package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
  54. package/dist/bigconsole/components/parser/ParserSelector.js +130 -128
  55. package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
  56. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +66 -63
  57. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
  58. package/dist/bigconsole/components/preview/AiPreviewPanel.js +95 -91
  59. package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
  60. package/dist/bigconsole/components/widgets/ActionConfigDialog.js +243 -230
  61. package/dist/bigconsole/components/widgets/ActionConfigDialog.js.map +1 -1
  62. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +149 -145
  63. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
  64. package/dist/bigconsole/components/widgets/PropertiesPanel.js +504 -470
  65. package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
  66. package/dist/bigconsole/components/widgets/WidgetMenu.js +87 -76
  67. package/dist/bigconsole/components/widgets/WidgetMenu.js.map +1 -1
  68. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +44 -47
  69. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
  70. package/dist/bigconsole/components/widgets/chart/ChartConfig.js +143 -142
  71. package/dist/bigconsole/components/widgets/chart/ChartConfig.js.map +1 -1
  72. package/dist/bigconsole/components/widgets/comments/WidgetComments.js +101 -88
  73. package/dist/bigconsole/components/widgets/comments/WidgetComments.js.map +1 -1
  74. package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +135 -134
  75. package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js.map +1 -1
  76. package/dist/bigconsole/components/widgets/form/FormWidget.js +167 -159
  77. package/dist/bigconsole/components/widgets/form/FormWidget.js.map +1 -1
  78. package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +69 -68
  79. package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js.map +1 -1
  80. package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js +64 -72
  81. package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js.map +1 -1
  82. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +60 -59
  83. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
  84. package/dist/bigconsole/components/widgets/list/ListConfig.js +57 -56
  85. package/dist/bigconsole/components/widgets/list/ListConfig.js.map +1 -1
  86. package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +70 -69
  87. package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js.map +1 -1
  88. package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +35 -34
  89. package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js.map +1 -1
  90. package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +20 -19
  91. package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js.map +1 -1
  92. package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +178 -177
  93. package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js.map +1 -1
  94. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +76 -75
  95. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js.map +1 -1
  96. package/dist/bigconsole/components/widgets/table/TableConfig.js +183 -168
  97. package/dist/bigconsole/components/widgets/table/TableConfig.js.map +1 -1
  98. package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +187 -180
  99. package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js.map +1 -1
  100. package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js +120 -118
  101. package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js.map +1 -1
  102. package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +40 -38
  103. package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js.map +1 -1
  104. package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js +150 -148
  105. package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js.map +1 -1
  106. package/dist/bigconsole/pages/DashboardBuilderPage.js +189 -175
  107. package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
  108. package/dist/bigconsole/pages/DashboardViewPage.js +200 -187
  109. package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
  110. package/dist/bigconsole/pages/DashboardsPage.js +379 -324
  111. package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
  112. package/dist/bigconsole/pages/DataParsersPage.js +185 -178
  113. package/dist/bigconsole/pages/DataParsersPage.js.map +1 -1
  114. package/dist/bigconsole/pages/DataSinkBuilderPage.js +195 -170
  115. package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
  116. package/dist/bigconsole/pages/DataSinkViewPage.js +224 -200
  117. package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
  118. package/dist/bigconsole/pages/DataSinksPage.js +281 -268
  119. package/dist/bigconsole/pages/DataSinksPage.js.map +1 -1
  120. package/dist/bigconsole/pages/DataSourcesPage.js +8 -7
  121. package/dist/bigconsole/pages/DataSourcesPage.js.map +1 -1
  122. package/dist/bigconsole/pages/InsightsPage.js +238 -208
  123. package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
  124. package/dist/bigconsole/pages/ParserBuilderPage.js +480 -413
  125. package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
  126. package/dist/bigconsole/pages/ParserViewPage.js +192 -172
  127. package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
  128. package/dist/bigconsole/pages/PipelineBuilderPage.js +198 -163
  129. package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
  130. package/dist/bigconsole/pages/PipelineViewPage.js +138 -133
  131. package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
  132. package/dist/bigconsole/pages/PipelinesPage.js +162 -136
  133. package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
  134. package/dist/bigconsole/pages/SettingsPage.js +177 -172
  135. package/dist/bigconsole/pages/SettingsPage.js.map +1 -1
  136. package/dist/bigconsole/pages/WidgetDemoPage.js +121 -83
  137. package/dist/bigconsole/pages/WidgetDemoPage.js.map +1 -1
  138. package/dist/bigconsole/pages/WorkflowViewPage.js +269 -242
  139. package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
  140. package/dist/bigconsole/pages/WorkflowsPage.js +153 -138
  141. package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
  142. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +111 -109
  143. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js.map +1 -1
  144. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +127 -126
  145. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js.map +1 -1
  146. package/dist/bigconsole/renderers/adapters/CustomRenderer.js +103 -103
  147. package/dist/bigconsole/renderers/adapters/CustomRenderer.js.map +1 -1
  148. package/dist/constants/permissions.js +83 -0
  149. package/dist/constants/permissions.js.map +1 -0
  150. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ParserSelector.js","names":[],"sources":["../../../../src/bigconsole/components/parser/ParserSelector.tsx"],"sourcesContent":["/**\n * Parser Selector\n *\n * Dropdown component for selecting a parser.\n * Shows both dashboard-specific parsers and global parsers.\n */\n\nimport { memo, useState, useEffect, useCallback, Fragment, useMemo } from 'react';\nimport { Plus } from 'lucide-react';\nimport { useParserOperations } from '../../hooks/useParserOperations';\nimport { useBuildPath } from '../../contexts';\nimport { ParserTypeBadge } from './ParserTypeBadge';\nimport { ParserStatusBadge } from './ParserStatusBadge';\nimport type { ParserType, ParserStatus } from '../../types';\n\n// Default console ID for global parsers\nconst DEFAULT_CONSOLE_ID = 'default';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface ParserSelectorProps {\n /** Console ID */\n consoleId: string;\n /** Currently selected parser ID */\n value: string | null;\n /** Callback when selection changes */\n onChange: (id: string | null) => void;\n /** Filter by type */\n filterType?: ParserType;\n /** Filter by status */\n filterStatus?: ParserStatus;\n /** Filter by input DataSink key */\n filterInputSinkKey?: string | null;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Whether to show error state */\n error?: boolean;\n /** Error message to display */\n errorMessage?: string;\n}\n\n// ============================================================================\n// ICONS\n// ============================================================================\n\nconst ChevronDownIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n);\n\nconst GlobeIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 21a9 9 0 100-18 9 9 0 000 18z\" />\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M3.6 9h16.8M3.6 15h16.8\" />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 3a15 15 0 014 9 15 15 0 01-4 9 15 15 0 01-4-9 15 15 0 014-9z\"\n />\n </svg>\n);\n\n// ============================================================================\n// PARSER OPTION BUTTON\n// ============================================================================\n\ninterface ParserOptionButtonProps {\n parser: {\n id: string;\n name: string;\n description: string | null;\n type: 'jq' | 'jsonata';\n inputSinkKey: string;\n outputKey: string;\n status: 'active' | 'inactive';\n };\n isSelected: boolean;\n onSelect: (id: string) => void;\n isGlobal?: boolean;\n}\n\nconst ParserOptionButton = memo(function ParserOptionButton({\n parser,\n isSelected,\n onSelect,\n isGlobal = false,\n}: ParserOptionButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={() => onSelect(parser.id)}\n className={`\n w-full px-3 py-2.5\n text-left\n hover:bg-bg-sunken\n transition-colors\n border-b border-border-default last:border-b-0\n ${isSelected ? 'bg-action-primary-bg/10' : ''}\n `}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex-1 min-w-0\">\n {/* Name and type */}\n <div className=\"flex items-center gap-2 mb-1\">\n <ParserTypeBadge type={parser.type} size=\"sm\" />\n <span className=\"text-sm font-medium text-text-primary truncate\">{parser.name}</span>\n {isGlobal && (\n <span className=\"inline-flex items-center gap-1 px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n <GlobeIcon className=\"w-3 h-3\" />\n Global\n </span>\n )}\n </div>\n\n {/* Description */}\n {parser.description && <p className=\"text-xs text-text-secondary line-clamp-1 mb-1\">{parser.description}</p>}\n\n {/* Data flow */}\n <div className=\"flex items-center gap-2 text-xs text-text-tertiary\">\n <span>\n {parser.inputSinkKey} → {parser.outputKey}\n </span>\n </div>\n </div>\n\n {/* Status badge */}\n <div>\n <ParserStatusBadge status={parser.status} size=\"sm\" showIcon={false} />\n </div>\n </div>\n </button>\n );\n});\n\n// ============================================================================\n// MAIN COMPONENT\n// ============================================================================\n\nexport const ParserSelector = memo(function ParserSelector({\n consoleId,\n value,\n onChange,\n filterType,\n filterStatus = 'active',\n filterInputSinkKey,\n placeholder = 'Select a parser...',\n disabled = false,\n error = false,\n errorMessage,\n}: ParserSelectorProps) {\n const [isOpen, setIsOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n\n const buildPath = useBuildPath();\n\n // Determine if we need to fetch global parsers separately\n const isGlobalConsole = consoleId === DEFAULT_CONSOLE_ID;\n\n // Fetch dashboard-specific parsers\n const {\n parsers: dashboardParsers,\n loading: dashboardLoading,\n fetchParsers: fetchDashboardParsers,\n } = useParserOperations({\n consoleId,\n skipFetch: isGlobalConsole, // Skip if already fetching global\n });\n\n // Fetch global parsers (only if consoleId is not already 'default')\n const {\n parsers: globalParsers,\n loading: globalLoading,\n fetchParsers: fetchGlobalParsers,\n } = useParserOperations({\n consoleId: DEFAULT_CONSOLE_ID,\n skipFetch: isGlobalConsole, // Skip if consoleId is already 'default'\n });\n\n // Combine parsers: dashboard-specific + global (avoiding duplicates)\n const allParsers = useMemo(() => {\n if (isGlobalConsole) {\n // If we're already on global console, just return dashboard parsers\n return dashboardParsers;\n }\n\n // Combine both, with dashboard parsers first\n const parserIds = new Set(dashboardParsers.map((p) => p.id));\n const uniqueGlobalParsers = globalParsers.filter((p) => !parserIds.has(p.id));\n\n return [...dashboardParsers, ...uniqueGlobalParsers];\n }, [dashboardParsers, globalParsers, isGlobalConsole]);\n\n // Combined loading state\n const loading = dashboardLoading || globalLoading;\n\n // Filter parsers\n const filteredParsers = useMemo(() => {\n return allParsers.filter((p) => {\n // Filter by type if specified\n if (filterType && p.type !== filterType) return false;\n\n // Filter by status if specified\n if (filterStatus && p.status !== filterStatus) return false;\n\n // Filter by input DataSink key if specified\n if (filterInputSinkKey && p.inputSinkKey !== filterInputSinkKey) return false;\n\n // Filter by search query\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n return (\n p.name.toLowerCase().includes(query) ||\n p.description?.toLowerCase().includes(query) ||\n p.inputSinkKey.toLowerCase().includes(query) ||\n p.outputKey.toLowerCase().includes(query)\n );\n }\n\n return true;\n });\n }, [allParsers, filterType, filterStatus, filterInputSinkKey, searchQuery]);\n\n // Group parsers by scope for display\n const { dashboardScopedParsers, globalScopedParsers } = useMemo(() => {\n const dashboard = filteredParsers.filter((p) => p.consoleId !== DEFAULT_CONSOLE_ID);\n const global = filteredParsers.filter((p) => p.consoleId === DEFAULT_CONSOLE_ID);\n return { dashboardScopedParsers: dashboard, globalScopedParsers: global };\n }, [filteredParsers]);\n\n // Find selected parser\n const selectedParser = allParsers.find((p) => p.id === value);\n\n // Fetch parsers when console changes or DataSink filter changes\n useEffect(() => {\n if (consoleId) {\n fetchDashboardParsers({ type: filterType, status: filterStatus ?? undefined });\n\n // Also fetch global parsers if not already on global console\n if (!isGlobalConsole) {\n fetchGlobalParsers({ type: filterType, status: filterStatus ?? undefined });\n }\n }\n }, [\n consoleId,\n filterType,\n filterStatus,\n filterInputSinkKey,\n fetchDashboardParsers,\n fetchGlobalParsers,\n isGlobalConsole,\n ]);\n\n // Handle selection\n const handleSelect = useCallback(\n (id: string | null) => {\n onChange(id);\n setIsOpen(false);\n setSearchQuery('');\n },\n [onChange]\n );\n\n // Toggle dropdown\n const handleToggle = useCallback(() => {\n if (disabled) return;\n const next = !isOpen;\n // Refetch on open so a parser created via the \"Create new\" tab shows\n // up here without needing a full page reload.\n if (next) {\n void fetchDashboardParsers({ type: filterType, status: filterStatus ?? undefined });\n if (!isGlobalConsole) {\n void fetchGlobalParsers({ type: filterType, status: filterStatus ?? undefined });\n }\n }\n setIsOpen(next);\n }, [disabled, isOpen, fetchDashboardParsers, fetchGlobalParsers, filterType, filterStatus, isGlobalConsole]);\n\n // Close dropdown\n const handleClose = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n // Open the create-Parser page in a new tab so the current editing\n // context (e.g. an unsaved widget in the dashboard builder) is preserved.\n // Preserve the dashboard scope so the new parser is created for this\n // console rather than as a global parser.\n const handleCreateNew = useCallback(() => {\n setIsOpen(false);\n const path = isGlobalConsole ? '/parsers/new' : `/dashboards/${consoleId}/parsers/new`;\n window.open(buildPath(path), '_blank', 'noopener,noreferrer');\n }, [buildPath, consoleId, isGlobalConsole]);\n\n return (\n <div className=\"relative\">\n {/* Trigger button */}\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={disabled}\n className={`\n w-full\n px-3 py-2\n bg-bg-surface\n border rounded-md\n text-left\n flex items-center justify-between gap-2\n transition-colors\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:border-action-primary-bg cursor-pointer'}\n ${error ? 'border-status-error-border' : 'border-border-default'}\n ${isOpen ? 'border-action-primary-bg ring-2 ring-action-primary-bg/20' : ''}\n `}\n >\n <div className=\"flex-1 min-w-0\">\n {selectedParser ? (\n <div className=\"flex items-center gap-2\">\n <ParserTypeBadge type={selectedParser.type} size=\"sm\" />\n <span className=\"text-sm text-text-primary truncate\">{selectedParser.name}</span>\n </div>\n ) : (\n <span className=\"text-sm text-text-tertiary\">{placeholder}</span>\n )}\n </div>\n\n <ChevronDownIcon\n className={`w-4 h-4 text-text-secondary transition-transform ${isOpen ? 'transform rotate-180' : ''}`}\n />\n </button>\n\n {/* Error message */}\n {error && errorMessage && <p className=\"mt-1 text-xs text-status-error-text\">{errorMessage}</p>}\n\n {/* Dropdown */}\n {isOpen && (\n <Fragment>\n {/* Backdrop */}\n <div className=\"fixed inset-0 z-10\" onClick={handleClose} />\n\n {/* Dropdown content */}\n <div className=\"absolute left-0 right-0 top-full mt-1 z-20 bg-bg-surface border border-border-default rounded-md shadow-lg max-h-80 flex flex-col overflow-hidden\">\n {/* Search input */}\n <div className=\"p-2 border-b border-border-default\">\n <input\n type=\"text\"\n placeholder=\"Search parsers...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className={`\n w-full px-3 py-2\n bg-bg-sunken\n border border-border-default\n rounded-md\n text-sm text-text-primary\n placeholder-text-tertiary\n focus:outline-none\n focus:border-action-primary-bg\n focus:ring-2\n focus:ring-action-primary-bg/20\n `}\n />\n </div>\n\n {/* Options list */}\n <div className=\"overflow-y-auto flex-1\">\n {loading ? (\n <div className=\"p-4 text-center text-sm text-text-secondary\">Loading parsers...</div>\n ) : filteredParsers.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-text-secondary\">No parsers found</div>\n ) : (\n <Fragment>\n {/* None option */}\n <button\n type=\"button\"\n onClick={() => handleSelect(null)}\n className={`\n w-full px-3 py-2\n text-left text-sm\n hover:bg-bg-sunken\n transition-colors\n ${value === null ? 'bg-action-primary-bg/10 text-action-primary-bg' : 'text-text-secondary'}\n `}\n >\n <span className=\"italic\">None</span>\n </button>\n\n {/* Dashboard-scoped parsers section */}\n {dashboardScopedParsers.length > 0 && (\n <Fragment>\n <div className=\"px-3 py-1.5 bg-bg-sunken border-y border-border-default\">\n <span className=\"text-xs font-medium text-text-secondary uppercase tracking-wide\">\n Dashboard Parsers\n </span>\n </div>\n {dashboardScopedParsers.map((parser) => (\n <ParserOptionButton\n key={parser.id}\n parser={parser}\n isSelected={value === parser.id}\n onSelect={handleSelect}\n />\n ))}\n </Fragment>\n )}\n\n {/* Global parsers section */}\n {globalScopedParsers.length > 0 && (\n <Fragment>\n <div className=\"px-3 py-1.5 bg-bg-sunken border-y border-border-default\">\n <span className=\"text-xs font-medium text-text-secondary uppercase tracking-wide\">\n Global Parsers\n </span>\n </div>\n {globalScopedParsers.map((parser) => (\n <ParserOptionButton\n key={parser.id}\n parser={parser}\n isSelected={value === parser.id}\n onSelect={handleSelect}\n isGlobal\n />\n ))}\n </Fragment>\n )}\n </Fragment>\n )}\n </div>\n\n {/* Create new — persistent footer action (opens in a new tab) */}\n <div className=\"p-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={handleCreateNew}\n className=\"\n w-full flex items-center gap-2\n px-3 py-2\n text-sm font-medium text-action-primary-bg\n rounded-md\n hover:bg-action-primary-bg/10\n transition-colors\n \"\n >\n <Plus className=\"w-4 h-4\" />\n Create new parser\n </button>\n </div>\n </div>\n </Fragment>\n )}\n </div>\n );\n});\n\nexport default ParserSelector;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ParserSelector.js","names":[],"sources":["../../../../src/bigconsole/components/parser/ParserSelector.tsx"],"sourcesContent":["/**\n * Parser Selector\n *\n * Dropdown component for selecting a parser.\n * Shows both dashboard-specific parsers and global parsers.\n */\n\nimport { memo, useState, useEffect, useCallback, Fragment, useMemo } from 'react';\nimport { Plus } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useParserOperations } from '../../hooks/useParserOperations';\nimport { useBuildPath } from '../../contexts';\nimport { ParserTypeBadge } from './ParserTypeBadge';\nimport { ParserStatusBadge } from './ParserStatusBadge';\nimport type { ParserType, ParserStatus } from '../../types';\n\n// Default console ID for global parsers\nconst DEFAULT_CONSOLE_ID = 'default';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface ParserSelectorProps {\n /** Console ID */\n consoleId: string;\n /** Currently selected parser ID */\n value: string | null;\n /** Callback when selection changes */\n onChange: (id: string | null) => void;\n /** Filter by type */\n filterType?: ParserType;\n /** Filter by status */\n filterStatus?: ParserStatus;\n /** Filter by input DataSink key */\n filterInputSinkKey?: string | null;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Whether to show error state */\n error?: boolean;\n /** Error message to display */\n errorMessage?: string;\n}\n\n// ============================================================================\n// ICONS\n// ============================================================================\n\nconst ChevronDownIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n);\n\nconst GlobeIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 21a9 9 0 100-18 9 9 0 000 18z\" />\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M3.6 9h16.8M3.6 15h16.8\" />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 3a15 15 0 014 9 15 15 0 01-4 9 15 15 0 01-4-9 15 15 0 014-9z\"\n />\n </svg>\n);\n\n// ============================================================================\n// PARSER OPTION BUTTON\n// ============================================================================\n\ninterface ParserOptionButtonProps {\n parser: {\n id: string;\n name: string;\n description: string | null;\n type: 'jq' | 'jsonata';\n inputSinkKey: string;\n outputKey: string;\n status: 'active' | 'inactive';\n };\n isSelected: boolean;\n onSelect: (id: string) => void;\n isGlobal?: boolean;\n}\n\nconst ParserOptionButton = memo(function ParserOptionButton({\n parser,\n isSelected,\n onSelect,\n isGlobal = false,\n}: ParserOptionButtonProps) {\n const { t } = useI18n();\n return (\n <button\n type=\"button\"\n onClick={() => onSelect(parser.id)}\n className={`\n w-full px-3 py-2.5\n text-left\n hover:bg-bg-sunken\n transition-colors\n border-b border-border-default last:border-b-0\n ${isSelected ? 'bg-action-primary-bg/10' : ''}\n `}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex-1 min-w-0\">\n {/* Name and type */}\n <div className=\"flex items-center gap-2 mb-1\">\n <ParserTypeBadge type={parser.type} size=\"sm\" />\n <span className=\"text-sm font-medium text-text-primary truncate\">{parser.name}</span>\n {isGlobal && (\n <span className=\"inline-flex items-center gap-1 px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\">\n <GlobeIcon className=\"w-3 h-3\" />\n {t('bigconsole.parsers.selector.globalBadge', 'Global')}\n </span>\n )}\n </div>\n\n {/* Description */}\n {parser.description && <p className=\"text-xs text-text-secondary line-clamp-1 mb-1\">{parser.description}</p>}\n\n {/* Data flow */}\n <div className=\"flex items-center gap-2 text-xs text-text-tertiary\">\n <span>\n {parser.inputSinkKey} → {parser.outputKey}\n </span>\n </div>\n </div>\n\n {/* Status badge */}\n <div>\n <ParserStatusBadge status={parser.status} size=\"sm\" showIcon={false} />\n </div>\n </div>\n </button>\n );\n});\n\n// ============================================================================\n// MAIN COMPONENT\n// ============================================================================\n\nexport const ParserSelector = memo(function ParserSelector({\n consoleId,\n value,\n onChange,\n filterType,\n filterStatus = 'active',\n filterInputSinkKey,\n placeholder,\n disabled = false,\n error = false,\n errorMessage,\n}: ParserSelectorProps) {\n const { t } = useI18n();\n const [isOpen, setIsOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n\n const resolvedPlaceholder = placeholder ?? t('bigconsole.parsers.selector.placeholder', 'Select a parser...');\n\n const buildPath = useBuildPath();\n\n // Determine if we need to fetch global parsers separately\n const isGlobalConsole = consoleId === DEFAULT_CONSOLE_ID;\n\n // Fetch dashboard-specific parsers\n const {\n parsers: dashboardParsers,\n loading: dashboardLoading,\n fetchParsers: fetchDashboardParsers,\n } = useParserOperations({\n consoleId,\n skipFetch: isGlobalConsole, // Skip if already fetching global\n });\n\n // Fetch global parsers (only if consoleId is not already 'default')\n const {\n parsers: globalParsers,\n loading: globalLoading,\n fetchParsers: fetchGlobalParsers,\n } = useParserOperations({\n consoleId: DEFAULT_CONSOLE_ID,\n skipFetch: isGlobalConsole, // Skip if consoleId is already 'default'\n });\n\n // Combine parsers: dashboard-specific + global (avoiding duplicates)\n const allParsers = useMemo(() => {\n if (isGlobalConsole) {\n // If we're already on global console, just return dashboard parsers\n return dashboardParsers;\n }\n\n // Combine both, with dashboard parsers first\n const parserIds = new Set(dashboardParsers.map((p) => p.id));\n const uniqueGlobalParsers = globalParsers.filter((p) => !parserIds.has(p.id));\n\n return [...dashboardParsers, ...uniqueGlobalParsers];\n }, [dashboardParsers, globalParsers, isGlobalConsole]);\n\n // Combined loading state\n const loading = dashboardLoading || globalLoading;\n\n // Filter parsers\n const filteredParsers = useMemo(() => {\n return allParsers.filter((p) => {\n // Filter by type if specified\n if (filterType && p.type !== filterType) return false;\n\n // Filter by status if specified\n if (filterStatus && p.status !== filterStatus) return false;\n\n // Filter by input DataSink key if specified\n if (filterInputSinkKey && p.inputSinkKey !== filterInputSinkKey) return false;\n\n // Filter by search query\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n return (\n p.name.toLowerCase().includes(query) ||\n p.description?.toLowerCase().includes(query) ||\n p.inputSinkKey.toLowerCase().includes(query) ||\n p.outputKey.toLowerCase().includes(query)\n );\n }\n\n return true;\n });\n }, [allParsers, filterType, filterStatus, filterInputSinkKey, searchQuery]);\n\n // Group parsers by scope for display\n const { dashboardScopedParsers, globalScopedParsers } = useMemo(() => {\n const dashboard = filteredParsers.filter((p) => p.consoleId !== DEFAULT_CONSOLE_ID);\n const global = filteredParsers.filter((p) => p.consoleId === DEFAULT_CONSOLE_ID);\n return { dashboardScopedParsers: dashboard, globalScopedParsers: global };\n }, [filteredParsers]);\n\n // Find selected parser\n const selectedParser = allParsers.find((p) => p.id === value);\n\n // Fetch parsers when console changes or DataSink filter changes\n useEffect(() => {\n if (consoleId) {\n fetchDashboardParsers({ type: filterType, status: filterStatus ?? undefined });\n\n // Also fetch global parsers if not already on global console\n if (!isGlobalConsole) {\n fetchGlobalParsers({ type: filterType, status: filterStatus ?? undefined });\n }\n }\n }, [\n consoleId,\n filterType,\n filterStatus,\n filterInputSinkKey,\n fetchDashboardParsers,\n fetchGlobalParsers,\n isGlobalConsole,\n ]);\n\n // Handle selection\n const handleSelect = useCallback(\n (id: string | null) => {\n onChange(id);\n setIsOpen(false);\n setSearchQuery('');\n },\n [onChange]\n );\n\n // Toggle dropdown\n const handleToggle = useCallback(() => {\n if (disabled) return;\n const next = !isOpen;\n // Refetch on open so a parser created via the \"Create new\" tab shows\n // up here without needing a full page reload.\n if (next) {\n void fetchDashboardParsers({ type: filterType, status: filterStatus ?? undefined });\n if (!isGlobalConsole) {\n void fetchGlobalParsers({ type: filterType, status: filterStatus ?? undefined });\n }\n }\n setIsOpen(next);\n }, [disabled, isOpen, fetchDashboardParsers, fetchGlobalParsers, filterType, filterStatus, isGlobalConsole]);\n\n // Close dropdown\n const handleClose = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n // Open the create-Parser page in a new tab so the current editing\n // context (e.g. an unsaved widget in the dashboard builder) is preserved.\n // Preserve the dashboard scope so the new parser is created for this\n // console rather than as a global parser.\n const handleCreateNew = useCallback(() => {\n setIsOpen(false);\n const path = isGlobalConsole ? '/parsers/new' : `/dashboards/${consoleId}/parsers/new`;\n window.open(buildPath(path), '_blank', 'noopener,noreferrer');\n }, [buildPath, consoleId, isGlobalConsole]);\n\n return (\n <div className=\"relative\">\n {/* Trigger button */}\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={disabled}\n className={`\n w-full\n px-3 py-2\n bg-bg-surface\n border rounded-md\n text-left\n flex items-center justify-between gap-2\n transition-colors\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:border-action-primary-bg cursor-pointer'}\n ${error ? 'border-status-error-border' : 'border-border-default'}\n ${isOpen ? 'border-action-primary-bg ring-2 ring-action-primary-bg/20' : ''}\n `}\n >\n <div className=\"flex-1 min-w-0\">\n {selectedParser ? (\n <div className=\"flex items-center gap-2\">\n <ParserTypeBadge type={selectedParser.type} size=\"sm\" />\n <span className=\"text-sm text-text-primary truncate\">{selectedParser.name}</span>\n </div>\n ) : (\n <span className=\"text-sm text-text-tertiary\">{resolvedPlaceholder}</span>\n )}\n </div>\n\n <ChevronDownIcon\n className={`w-4 h-4 text-text-secondary transition-transform ${isOpen ? 'transform rotate-180' : ''}`}\n />\n </button>\n\n {/* Error message */}\n {error && errorMessage && <p className=\"mt-1 text-xs text-status-error-text\">{errorMessage}</p>}\n\n {/* Dropdown */}\n {isOpen && (\n <Fragment>\n {/* Backdrop */}\n <div className=\"fixed inset-0 z-10\" onClick={handleClose} />\n\n {/* Dropdown content */}\n <div className=\"absolute left-0 right-0 top-full mt-1 z-20 bg-bg-surface border border-border-default rounded-md shadow-lg max-h-80 flex flex-col overflow-hidden\">\n {/* Search input */}\n <div className=\"p-2 border-b border-border-default\">\n <input\n type=\"text\"\n placeholder={t('bigconsole.parsers.selector.searchPlaceholder', 'Search parsers...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className={`\n w-full px-3 py-2\n bg-bg-sunken\n border border-border-default\n rounded-md\n text-sm text-text-primary\n placeholder-text-tertiary\n focus:outline-none\n focus:border-action-primary-bg\n focus:ring-2\n focus:ring-action-primary-bg/20\n `}\n />\n </div>\n\n {/* Options list */}\n <div className=\"overflow-y-auto flex-1\">\n {loading ? (\n <div className=\"p-4 text-center text-sm text-text-secondary\">\n {t('bigconsole.parsers.selector.loading', 'Loading parsers...')}\n </div>\n ) : filteredParsers.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-text-secondary\">\n {t('bigconsole.parsers.selector.empty', 'No parsers found')}\n </div>\n ) : (\n <Fragment>\n {/* None option */}\n <button\n type=\"button\"\n onClick={() => handleSelect(null)}\n className={`\n w-full px-3 py-2\n text-left text-sm\n hover:bg-bg-sunken\n transition-colors\n ${value === null ? 'bg-action-primary-bg/10 text-action-primary-bg' : 'text-text-secondary'}\n `}\n >\n <span className=\"italic\">{t('bigconsole.parsers.selector.none', 'None')}</span>\n </button>\n\n {/* Dashboard-scoped parsers section */}\n {dashboardScopedParsers.length > 0 && (\n <Fragment>\n <div className=\"px-3 py-1.5 bg-bg-sunken border-y border-border-default\">\n <span className=\"text-xs font-medium text-text-secondary uppercase tracking-wide\">\n {t('bigconsole.parsers.selector.dashboardParsers', 'Dashboard Parsers')}\n </span>\n </div>\n {dashboardScopedParsers.map((parser) => (\n <ParserOptionButton\n key={parser.id}\n parser={parser}\n isSelected={value === parser.id}\n onSelect={handleSelect}\n />\n ))}\n </Fragment>\n )}\n\n {/* Global parsers section */}\n {globalScopedParsers.length > 0 && (\n <Fragment>\n <div className=\"px-3 py-1.5 bg-bg-sunken border-y border-border-default\">\n <span className=\"text-xs font-medium text-text-secondary uppercase tracking-wide\">\n {t('bigconsole.parsers.selector.globalParsers', 'Global Parsers')}\n </span>\n </div>\n {globalScopedParsers.map((parser) => (\n <ParserOptionButton\n key={parser.id}\n parser={parser}\n isSelected={value === parser.id}\n onSelect={handleSelect}\n isGlobal\n />\n ))}\n </Fragment>\n )}\n </Fragment>\n )}\n </div>\n\n {/* Create new — persistent footer action (opens in a new tab) */}\n <div className=\"p-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={handleCreateNew}\n className=\"\n w-full flex items-center gap-2\n px-3 py-2\n text-sm font-medium text-action-primary-bg\n rounded-md\n hover:bg-action-primary-bg/10\n transition-colors\n \"\n >\n <Plus className=\"w-4 h-4\" />\n {t('bigconsole.parsers.selector.createNew', 'Create new parser')}\n </button>\n </div>\n </div>\n </Fragment>\n )}\n </div>\n );\n});\n\nexport default ParserSelector;\n"],"mappings":"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"}
@@ -2,132 +2,135 @@ import { useAssistantRunStore as e } from "../../assistant/assistantRunStore.js"
2
2
  import { StepRail as t, formatAgo as n, useAssistantSidebarWidth as r } from "./previewShared.js";
3
3
  import { useEffect as i, useState as a } from "react";
4
4
  import { ArrowUpRight as o, Loader2 as s, Sparkles as c, X as l } from "lucide-react";
5
- import { Button as u } from "@burdenoff/fe-libs/ui";
6
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
5
+ import { useI18n as u } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
6
+ import { Button as d } from "@burdenoff/fe-libs/ui";
7
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
7
8
  //#region src/bigconsole/components/preview/AiPreviewMiniPanel.tsx
8
- function p({ onNavigate: p }) {
9
- let m = e((e) => e.isRunning), h = e((e) => e.previewOpen), g = e((e) => e.hasRun), _ = e((e) => e.richMounted), v = e((e) => e.steps), y = e((e) => e.entityIds), b = e((e) => e.caption), x = e((e) => e.error), S = e((e) => e.updatedAt), C = e((e) => e.openPreview), w = e((e) => e.closePreview), [T, E] = a(() => Date.now());
9
+ function m({ onNavigate: m }) {
10
+ let { t: h } = u(), g = e((e) => e.isRunning), _ = e((e) => e.previewOpen), v = e((e) => e.hasRun), y = e((e) => e.richMounted), b = e((e) => e.steps), x = e((e) => e.entityIds), S = e((e) => e.caption), C = e((e) => e.error), w = e((e) => e.updatedAt), T = e((e) => e.openPreview), E = e((e) => e.closePreview), [D, O] = a(() => Date.now());
10
11
  i(() => {
11
- if (!m) return;
12
- let e = window.setInterval(() => E(Date.now()), 1e3);
12
+ if (!g) return;
13
+ let e = window.setInterval(() => O(Date.now()), 1e3);
13
14
  return () => window.clearInterval(e);
14
- }, [m]);
15
- let D = r(), O = (e) => {
16
- p ? p(e) : typeof window < "u" && window.location.assign(e);
15
+ }, [g]);
16
+ let k = r(), A = (e) => {
17
+ m ? m(e) : typeof window < "u" && window.location.assign(e);
17
18
  };
18
- if (!g || _) return null;
19
- if (!h) return /* @__PURE__ */ d("div", {
19
+ if (!v || y) return null;
20
+ if (!_) return /* @__PURE__ */ f("div", {
20
21
  className: "flex justify-end px-4 pt-1 md:px-6",
21
- style: { marginRight: D },
22
- children: /* @__PURE__ */ f(u, {
22
+ style: { marginRight: k },
23
+ children: /* @__PURE__ */ p(d, {
23
24
  size: "sm",
24
25
  variant: "outline",
25
- onClick: C,
26
+ onClick: T,
26
27
  "data-testid": "ai-preview-open",
27
28
  children: [
28
- /* @__PURE__ */ d(c, { className: "mr-1.5 size-4" }),
29
- "Preview",
30
- m ? /* @__PURE__ */ d(s, { className: "ml-1.5 size-3.5 animate-spin" }) : null
29
+ /* @__PURE__ */ f(c, { className: "mr-1.5 size-4" }),
30
+ h("bigconsole.widget.aiPreviewMini.previewButton", "Preview"),
31
+ g ? /* @__PURE__ */ f(s, { className: "ml-1.5 size-3.5 animate-spin" }) : null
31
32
  ]
32
33
  })
33
34
  });
34
- let k = S ? Math.max(0, Math.floor((T - S) / 1e3)) : 0, A = [];
35
- return y.datasink && A.push({
35
+ let j = w ? Math.max(0, Math.floor((D - w) / 1e3)) : 0, M = [];
36
+ return x.datasink && M.push({
36
37
  key: "datasink",
37
- label: "Data Sink",
38
- path: `/bigconsole/datasinks/${y.datasink}`
39
- }), y.dashboard && A.push({
38
+ label: h("bigconsole.widget.aiPreviewMini.link.dataSink", "Data Sink"),
39
+ path: `/bigconsole/datasinks/${x.datasink}`
40
+ }), x.dashboard && M.push({
40
41
  key: "dashboard",
41
- label: "Dashboard",
42
- path: `/bigconsole/dashboards/${y.dashboard}`
43
- }), y.parser && A.push({
42
+ label: h("bigconsole.widget.aiPreviewMini.link.dashboard", "Dashboard"),
43
+ path: `/bigconsole/dashboards/${x.dashboard}`
44
+ }), x.parser && M.push({
44
45
  key: "parser",
45
- label: "Parser",
46
- path: `/bigconsole/parsers/${y.parser}`
47
- }), /* @__PURE__ */ f("section", {
46
+ label: h("bigconsole.widget.aiPreviewMini.link.parser", "Parser"),
47
+ path: `/bigconsole/parsers/${x.parser}`
48
+ }), /* @__PURE__ */ p("section", {
48
49
  "data-testid": "ai-preview-panel",
49
50
  "data-variant": "mini",
50
- "aria-label": "AI preview",
51
- style: { marginRight: D ? D + 16 : void 0 },
51
+ "aria-label": h("bigconsole.widget.aiPreviewMini.ariaLabel", "AI preview"),
52
+ style: { marginRight: k ? k + 16 : void 0 },
52
53
  className: "mx-4 mt-1 mb-3 overflow-hidden rounded-lg border border-border-default bg-bg-sunken shadow-sm md:mx-6",
53
54
  children: [
54
- /* @__PURE__ */ f("div", {
55
+ /* @__PURE__ */ p("div", {
55
56
  className: "flex items-center justify-between gap-3 border-b border-border-subtle bg-bg-surface px-3 py-2",
56
- children: [/* @__PURE__ */ f("div", {
57
+ children: [/* @__PURE__ */ p("div", {
57
58
  className: "flex min-w-0 items-center gap-2",
58
59
  children: [
59
- /* @__PURE__ */ d(c, { className: "size-4 shrink-0 text-action-primary-bg" }),
60
- /* @__PURE__ */ d("h2", {
60
+ /* @__PURE__ */ f(c, { className: "size-4 shrink-0 text-action-primary-bg" }),
61
+ /* @__PURE__ */ f("h2", {
61
62
  className: "truncate text-sm font-semibold text-text-primary",
62
- children: "AI Preview"
63
+ children: h("bigconsole.widget.aiPreviewMini.title", "AI Preview")
63
64
  }),
64
- m ? /* @__PURE__ */ f("span", {
65
+ g ? /* @__PURE__ */ p("span", {
65
66
  className: "inline-flex items-center gap-1.5 rounded-full bg-status-success-bg px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-status-success-text",
66
67
  "data-testid": "ai-preview-live",
67
- children: [/* @__PURE__ */ f("span", {
68
+ children: [/* @__PURE__ */ p("span", {
68
69
  className: "relative flex size-2",
69
- children: [/* @__PURE__ */ d("span", { className: "absolute inline-flex size-full animate-ping rounded-full bg-status-success-text opacity-75" }), /* @__PURE__ */ d("span", { className: "relative inline-flex size-2 rounded-full bg-status-success-text" })]
70
- }), "Live"]
70
+ children: [/* @__PURE__ */ f("span", { className: "absolute inline-flex size-full animate-ping rounded-full bg-status-success-text opacity-75" }), /* @__PURE__ */ f("span", { className: "relative inline-flex size-2 rounded-full bg-status-success-text" })]
71
+ }), h("bigconsole.widget.aiPreviewMini.live", "Live")]
71
72
  }) : null,
72
- /* @__PURE__ */ f("span", {
73
+ /* @__PURE__ */ p("span", {
73
74
  className: "truncate text-xs text-text-muted",
74
75
  "data-testid": "ai-preview-ago",
75
76
  children: [
76
- m ? b || "Working…" : x ? "Stopped" : "Done",
77
- " · updated",
77
+ g ? S || h("bigconsole.widget.aiPreviewMini.working", "Working…") : C ? h("bigconsole.widget.aiPreviewMini.stopped", "Stopped") : h("bigconsole.widget.aiPreviewMini.done", "Done"),
78
+ " ",
79
+ "· ",
80
+ h("bigconsole.widget.aiPreviewMini.updated", "updated"),
78
81
  " ",
79
- n(k)
82
+ n(j)
80
83
  ]
81
84
  })
82
85
  ]
83
- }), /* @__PURE__ */ d(u, {
86
+ }), /* @__PURE__ */ f(d, {
84
87
  size: "sm",
85
88
  variant: "ghost",
86
- onClick: w,
87
- "aria-label": "Close preview",
89
+ onClick: E,
90
+ "aria-label": h("bigconsole.widget.aiPreviewMini.closeAriaLabel", "Close preview"),
88
91
  "data-testid": "ai-preview-close",
89
- children: /* @__PURE__ */ d(l, { className: "size-4" })
92
+ children: /* @__PURE__ */ f(l, { className: "size-4" })
90
93
  })]
91
94
  }),
92
- /* @__PURE__ */ d("div", {
95
+ /* @__PURE__ */ f("div", {
93
96
  className: "border-b border-border-subtle px-3 py-2",
94
- children: /* @__PURE__ */ d(t, { steps: v })
97
+ children: /* @__PURE__ */ f(t, { steps: b })
95
98
  }),
96
- /* @__PURE__ */ f("div", {
99
+ /* @__PURE__ */ p("div", {
97
100
  className: "space-y-2 p-3",
98
- children: [A.length === 0 ? /* @__PURE__ */ d("p", {
101
+ children: [M.length === 0 ? /* @__PURE__ */ f("p", {
99
102
  className: "px-1 py-4 text-sm text-text-muted",
100
- children: m ? "Building… links appear here as each piece is created." : "Nothing built yet."
101
- }) : A.map((e) => /* @__PURE__ */ f("div", {
103
+ children: g ? h("bigconsole.widget.aiPreviewMini.building", "Building… links appear here as each piece is created.") : h("bigconsole.widget.aiPreviewMini.nothingBuilt", "Nothing built yet.")
104
+ }) : M.map((e) => /* @__PURE__ */ p("div", {
102
105
  "data-testid": "ai-preview-row",
103
106
  className: "flex items-center justify-between gap-3 rounded-md border border-border-subtle bg-bg-surface px-3 py-2",
104
107
  children: [/* @__PURE__ */ f("p", {
105
108
  className: "truncate text-sm font-medium text-text-primary",
106
- children: [e.label, " ready"]
107
- }), /* @__PURE__ */ f(u, {
109
+ children: h("bigconsole.widget.aiPreviewMini.linkReady", "{{label}} ready", { label: e.label })
110
+ }), /* @__PURE__ */ p(d, {
108
111
  size: "sm",
109
112
  variant: "ghost",
110
- onClick: () => O(e.path),
111
- children: ["Open", /* @__PURE__ */ d(o, { className: "ml-1 size-3.5" })]
113
+ onClick: () => A(e.path),
114
+ children: [h("bigconsole.widget.aiPreviewMini.open", "Open"), /* @__PURE__ */ f(o, { className: "ml-1 size-3.5" })]
112
115
  })]
113
- }, e.key)), y.dashboard ? /* @__PURE__ */ f(u, {
116
+ }, e.key)), x.dashboard ? /* @__PURE__ */ p(d, {
114
117
  size: "sm",
115
118
  variant: "outline",
116
119
  className: "w-full",
117
120
  "data-testid": "ai-preview-open-in-bigconsole",
118
- onClick: () => O(`/bigconsole/dashboards/${y.dashboard ?? ""}`),
119
- children: ["Open in BigConsole for the live view", /* @__PURE__ */ d(o, { className: "ml-1 size-3.5" })]
121
+ onClick: () => A(`/bigconsole/dashboards/${x.dashboard ?? ""}`),
122
+ children: [h("bigconsole.widget.aiPreviewMini.openInBigConsole", "Open in BigConsole for the live view"), /* @__PURE__ */ f(o, { className: "ml-1 size-3.5" })]
120
123
  }) : null]
121
124
  }),
122
- x ? /* @__PURE__ */ d("p", {
125
+ C ? /* @__PURE__ */ f("p", {
123
126
  className: "border-t border-border-subtle px-3 py-2 text-xs text-status-error-text",
124
127
  "data-testid": "ai-preview-error",
125
- children: x
128
+ children: C
126
129
  }) : null
127
130
  ]
128
131
  });
129
132
  }
130
133
  //#endregion
131
- export { p as default };
134
+ export { m as default };
132
135
 
133
136
  //# sourceMappingURL=AiPreviewMiniPanel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiPreviewMiniPanel.js","names":[],"sources":["../../../../src/bigconsole/components/preview/AiPreviewMiniPanel.tsx"],"sourcesContent":["/**\n * AI Preview — the app-shell copy.\n *\n * The full <AiPreviewPanel> lives inside BigConsoleRoot and only exists on\n * /bigconsole pages, but the assistant widget is global: you can start a build\n * from anywhere. This lightweight panel is mounted by the app shell so the build\n * is visible on WHATEVER page you are on — workflows, store, workspaces, search.\n *\n * It is deliberately store-only: no Apollo, no BigConsole context, no data\n * fetching. That is what lets it mount above the router. It shows the same live\n * step rail and caption as the full panel, plus click-through links to each\n * entity as it lands. The rich per-entity tabs stay on the /bigconsole pages,\n * where the data layer actually exists.\n *\n * It renders nothing while the full panel is up (`richMounted`), so the two\n * never stack. Leave a /bigconsole page mid-run and the full panel unmounts,\n * `richMounted` flips false, and this one takes over without a flicker.\n */\nimport { useEffect, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { ArrowUpRight, Loader2, Sparkles, X } from 'lucide-react';\n\nimport { useAssistantRunStore } from '../../assistant/assistantRunStore';\nimport { StepRail, formatAgo, useAssistantSidebarWidth } from './previewShared';\n\ninterface AiPreviewMiniPanelProps {\n /**\n * Route to an in-app path. The links are absolute (`/bigconsole/...`) because\n * this panel renders outside the BigConsole router. Falls back to a full\n * navigation when the host does not supply a handler.\n */\n onNavigate?: (path: string) => void;\n}\n\ninterface QuickLink {\n key: string;\n label: string;\n path: string;\n}\n\nexport function AiPreviewMiniPanel({ onNavigate }: AiPreviewMiniPanelProps) {\n const isRunning = useAssistantRunStore((state) => state.isRunning);\n const previewOpen = useAssistantRunStore((state) => state.previewOpen);\n const hasRun = useAssistantRunStore((state) => state.hasRun);\n const richMounted = useAssistantRunStore((state) => state.richMounted);\n const steps = useAssistantRunStore((state) => state.steps);\n const entityIds = useAssistantRunStore((state) => state.entityIds);\n const caption = useAssistantRunStore((state) => state.caption);\n const error = useAssistantRunStore((state) => state.error);\n const updatedAt = useAssistantRunStore((state) => state.updatedAt);\n const openPreview = useAssistantRunStore((state) => state.openPreview);\n const closePreview = useAssistantRunStore((state) => state.closePreview);\n\n const [now, setNow] = useState(() => Date.now());\n useEffect(() => {\n if (!isRunning) return;\n const id = window.setInterval(() => setNow(Date.now()), 1000);\n return () => window.clearInterval(id);\n }, [isRunning]);\n\n const assistantWidth = useAssistantSidebarWidth();\n\n const go = (path: string) => {\n if (onNavigate) onNavigate(path);\n else if (typeof window !== 'undefined') window.location.assign(path);\n };\n\n // Nothing has run, or the full in-context panel is already on screen.\n if (!hasRun || richMounted) return null;\n\n if (!previewOpen) {\n return (\n <div className=\"flex justify-end px-4 pt-1 md:px-6\" style={{ marginRight: assistantWidth }}>\n <Button size=\"sm\" variant=\"outline\" onClick={openPreview} data-testid=\"ai-preview-open\">\n <Sparkles className=\"mr-1.5 size-4\" />\n Preview\n {isRunning ? <Loader2 className=\"ml-1.5 size-3.5 animate-spin\" /> : null}\n </Button>\n </div>\n );\n }\n\n const agoSeconds = updatedAt ? Math.max(0, Math.floor((now - updatedAt) / 1000)) : 0;\n\n const links: QuickLink[] = [];\n if (entityIds.datasink)\n links.push({ key: 'datasink', label: 'Data Sink', path: `/bigconsole/datasinks/${entityIds.datasink}` });\n if (entityIds.dashboard)\n links.push({ key: 'dashboard', label: 'Dashboard', path: `/bigconsole/dashboards/${entityIds.dashboard}` });\n if (entityIds.parser) links.push({ key: 'parser', label: 'Parser', path: `/bigconsole/parsers/${entityIds.parser}` });\n\n return (\n <section\n data-testid=\"ai-preview-panel\"\n data-variant=\"mini\"\n aria-label=\"AI preview\"\n style={{ marginRight: assistantWidth ? assistantWidth + 16 : undefined }}\n className=\"mx-4 mt-1 mb-3 overflow-hidden rounded-lg border border-border-default bg-bg-sunken shadow-sm md:mx-6\"\n >\n {/* Header + live strip */}\n <div className=\"flex items-center justify-between gap-3 border-b border-border-subtle bg-bg-surface px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Sparkles className=\"size-4 shrink-0 text-action-primary-bg\" />\n <h2 className=\"truncate text-sm font-semibold text-text-primary\">AI Preview</h2>\n {isRunning ? (\n <span\n className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-status-success-text\"\n data-testid=\"ai-preview-live\"\n >\n <span className=\"relative flex size-2\">\n <span className=\"absolute inline-flex size-full animate-ping rounded-full bg-status-success-text opacity-75\" />\n <span className=\"relative inline-flex size-2 rounded-full bg-status-success-text\" />\n </span>\n Live\n </span>\n ) : null}\n <span className=\"truncate text-xs text-text-muted\" data-testid=\"ai-preview-ago\">\n {isRunning ? (caption ? caption : 'Working…') : error ? 'Stopped' : 'Done'} · updated{' '}\n {formatAgo(agoSeconds)}\n </span>\n </div>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={closePreview}\n aria-label=\"Close preview\"\n data-testid=\"ai-preview-close\"\n >\n <X className=\"size-4\" />\n </Button>\n </div>\n\n {/* Step rail */}\n <div className=\"border-b border-border-subtle px-3 py-2\">\n <StepRail steps={steps} />\n </div>\n\n {/* Quick links to what has been built so far. The full per-entity tabs live\n on the /bigconsole pages; here we surface the direct links instead. */}\n <div className=\"space-y-2 p-3\">\n {links.length === 0 ? (\n <p className=\"px-1 py-4 text-sm text-text-muted\">\n {isRunning ? 'Building… links appear here as each piece is created.' : 'Nothing built yet.'}\n </p>\n ) : (\n links.map((link) => (\n <div\n key={link.key}\n data-testid=\"ai-preview-row\"\n className=\"flex items-center justify-between gap-3 rounded-md border border-border-subtle bg-bg-surface px-3 py-2\"\n >\n <p className=\"truncate text-sm font-medium text-text-primary\">{link.label} ready</p>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => go(link.path)}>\n Open\n <ArrowUpRight className=\"ml-1 size-3.5\" />\n </Button>\n </div>\n ))\n )}\n {entityIds.dashboard ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"w-full\"\n data-testid=\"ai-preview-open-in-bigconsole\"\n onClick={() => go(`/bigconsole/dashboards/${entityIds.dashboard ?? ''}`)}\n >\n Open in BigConsole for the live view\n <ArrowUpRight className=\"ml-1 size-3.5\" />\n </Button>\n ) : null}\n </div>\n\n {error ? (\n <p\n className=\"border-t border-border-subtle px-3 py-2 text-xs text-status-error-text\"\n data-testid=\"ai-preview-error\"\n >\n {error}\n </p>\n ) : null}\n </section>\n );\n}\n\nexport default AiPreviewMiniPanel;\n"],"mappings":"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"}
1
+ {"version":3,"file":"AiPreviewMiniPanel.js","names":[],"sources":["../../../../src/bigconsole/components/preview/AiPreviewMiniPanel.tsx"],"sourcesContent":["/**\n * AI Preview — the app-shell copy.\n *\n * The full <AiPreviewPanel> lives inside BigConsoleRoot and only exists on\n * /bigconsole pages, but the assistant widget is global: you can start a build\n * from anywhere. This lightweight panel is mounted by the app shell so the build\n * is visible on WHATEVER page you are on — workflows, store, workspaces, search.\n *\n * It is deliberately store-only: no Apollo, no BigConsole context, no data\n * fetching. That is what lets it mount above the router. It shows the same live\n * step rail and caption as the full panel, plus click-through links to each\n * entity as it lands. The rich per-entity tabs stay on the /bigconsole pages,\n * where the data layer actually exists.\n *\n * It renders nothing while the full panel is up (`richMounted`), so the two\n * never stack. Leave a /bigconsole page mid-run and the full panel unmounts,\n * `richMounted` flips false, and this one takes over without a flicker.\n */\nimport { useEffect, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowUpRight, Loader2, Sparkles, X } from 'lucide-react';\n\nimport { useAssistantRunStore } from '../../assistant/assistantRunStore';\nimport { StepRail, formatAgo, useAssistantSidebarWidth } from './previewShared';\n\ninterface AiPreviewMiniPanelProps {\n /**\n * Route to an in-app path. The links are absolute (`/bigconsole/...`) because\n * this panel renders outside the BigConsole router. Falls back to a full\n * navigation when the host does not supply a handler.\n */\n onNavigate?: (path: string) => void;\n}\n\ninterface QuickLink {\n key: string;\n label: string;\n path: string;\n}\n\nexport function AiPreviewMiniPanel({ onNavigate }: AiPreviewMiniPanelProps) {\n const { t } = useI18n();\n const isRunning = useAssistantRunStore((state) => state.isRunning);\n const previewOpen = useAssistantRunStore((state) => state.previewOpen);\n const hasRun = useAssistantRunStore((state) => state.hasRun);\n const richMounted = useAssistantRunStore((state) => state.richMounted);\n const steps = useAssistantRunStore((state) => state.steps);\n const entityIds = useAssistantRunStore((state) => state.entityIds);\n const caption = useAssistantRunStore((state) => state.caption);\n const error = useAssistantRunStore((state) => state.error);\n const updatedAt = useAssistantRunStore((state) => state.updatedAt);\n const openPreview = useAssistantRunStore((state) => state.openPreview);\n const closePreview = useAssistantRunStore((state) => state.closePreview);\n\n const [now, setNow] = useState(() => Date.now());\n useEffect(() => {\n if (!isRunning) return;\n const id = window.setInterval(() => setNow(Date.now()), 1000);\n return () => window.clearInterval(id);\n }, [isRunning]);\n\n const assistantWidth = useAssistantSidebarWidth();\n\n const go = (path: string) => {\n if (onNavigate) onNavigate(path);\n else if (typeof window !== 'undefined') window.location.assign(path);\n };\n\n // Nothing has run, or the full in-context panel is already on screen.\n if (!hasRun || richMounted) return null;\n\n if (!previewOpen) {\n return (\n <div className=\"flex justify-end px-4 pt-1 md:px-6\" style={{ marginRight: assistantWidth }}>\n <Button size=\"sm\" variant=\"outline\" onClick={openPreview} data-testid=\"ai-preview-open\">\n <Sparkles className=\"mr-1.5 size-4\" />\n {t('bigconsole.widget.aiPreviewMini.previewButton', 'Preview')}\n {isRunning ? <Loader2 className=\"ml-1.5 size-3.5 animate-spin\" /> : null}\n </Button>\n </div>\n );\n }\n\n const agoSeconds = updatedAt ? Math.max(0, Math.floor((now - updatedAt) / 1000)) : 0;\n\n const links: QuickLink[] = [];\n if (entityIds.datasink)\n links.push({\n key: 'datasink',\n label: t('bigconsole.widget.aiPreviewMini.link.dataSink', 'Data Sink'),\n path: `/bigconsole/datasinks/${entityIds.datasink}`,\n });\n if (entityIds.dashboard)\n links.push({\n key: 'dashboard',\n label: t('bigconsole.widget.aiPreviewMini.link.dashboard', 'Dashboard'),\n path: `/bigconsole/dashboards/${entityIds.dashboard}`,\n });\n if (entityIds.parser)\n links.push({\n key: 'parser',\n label: t('bigconsole.widget.aiPreviewMini.link.parser', 'Parser'),\n path: `/bigconsole/parsers/${entityIds.parser}`,\n });\n\n return (\n <section\n data-testid=\"ai-preview-panel\"\n data-variant=\"mini\"\n aria-label={t('bigconsole.widget.aiPreviewMini.ariaLabel', 'AI preview')}\n style={{ marginRight: assistantWidth ? assistantWidth + 16 : undefined }}\n className=\"mx-4 mt-1 mb-3 overflow-hidden rounded-lg border border-border-default bg-bg-sunken shadow-sm md:mx-6\"\n >\n {/* Header + live strip */}\n <div className=\"flex items-center justify-between gap-3 border-b border-border-subtle bg-bg-surface px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Sparkles className=\"size-4 shrink-0 text-action-primary-bg\" />\n <h2 className=\"truncate text-sm font-semibold text-text-primary\">\n {t('bigconsole.widget.aiPreviewMini.title', 'AI Preview')}\n </h2>\n {isRunning ? (\n <span\n className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-status-success-text\"\n data-testid=\"ai-preview-live\"\n >\n <span className=\"relative flex size-2\">\n <span className=\"absolute inline-flex size-full animate-ping rounded-full bg-status-success-text opacity-75\" />\n <span className=\"relative inline-flex size-2 rounded-full bg-status-success-text\" />\n </span>\n {t('bigconsole.widget.aiPreviewMini.live', 'Live')}\n </span>\n ) : null}\n <span className=\"truncate text-xs text-text-muted\" data-testid=\"ai-preview-ago\">\n {isRunning\n ? caption\n ? caption\n : t('bigconsole.widget.aiPreviewMini.working', 'Working…')\n : error\n ? t('bigconsole.widget.aiPreviewMini.stopped', 'Stopped')\n : t('bigconsole.widget.aiPreviewMini.done', 'Done')}{' '}\n · {t('bigconsole.widget.aiPreviewMini.updated', 'updated')} {formatAgo(agoSeconds)}\n </span>\n </div>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={closePreview}\n aria-label={t('bigconsole.widget.aiPreviewMini.closeAriaLabel', 'Close preview')}\n data-testid=\"ai-preview-close\"\n >\n <X className=\"size-4\" />\n </Button>\n </div>\n\n {/* Step rail */}\n <div className=\"border-b border-border-subtle px-3 py-2\">\n <StepRail steps={steps} />\n </div>\n\n {/* Quick links to what has been built so far. The full per-entity tabs live\n on the /bigconsole pages; here we surface the direct links instead. */}\n <div className=\"space-y-2 p-3\">\n {links.length === 0 ? (\n <p className=\"px-1 py-4 text-sm text-text-muted\">\n {isRunning\n ? t('bigconsole.widget.aiPreviewMini.building', 'Building… links appear here as each piece is created.')\n : t('bigconsole.widget.aiPreviewMini.nothingBuilt', 'Nothing built yet.')}\n </p>\n ) : (\n links.map((link) => (\n <div\n key={link.key}\n data-testid=\"ai-preview-row\"\n className=\"flex items-center justify-between gap-3 rounded-md border border-border-subtle bg-bg-surface px-3 py-2\"\n >\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {t('bigconsole.widget.aiPreviewMini.linkReady', '{{label}} ready', { label: link.label })}\n </p>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => go(link.path)}>\n {t('bigconsole.widget.aiPreviewMini.open', 'Open')}\n <ArrowUpRight className=\"ml-1 size-3.5\" />\n </Button>\n </div>\n ))\n )}\n {entityIds.dashboard ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"w-full\"\n data-testid=\"ai-preview-open-in-bigconsole\"\n onClick={() => go(`/bigconsole/dashboards/${entityIds.dashboard ?? ''}`)}\n >\n {t('bigconsole.widget.aiPreviewMini.openInBigConsole', 'Open in BigConsole for the live view')}\n <ArrowUpRight className=\"ml-1 size-3.5\" />\n </Button>\n ) : null}\n </div>\n\n {error ? (\n <p\n className=\"border-t border-border-subtle px-3 py-2 text-xs text-status-error-text\"\n data-testid=\"ai-preview-error\"\n >\n {error}\n </p>\n ) : null}\n </section>\n );\n}\n\nexport default AiPreviewMiniPanel;\n"],"mappings":"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"}