@burdenoff/microfe-bigconsole 2026.716.2 → 2026.716.4

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 (32) hide show
  1. package/dist/bigconsole/BigConsoleRoot.js +100 -90
  2. package/dist/bigconsole/BigConsoleRoot.js.map +1 -1
  3. package/dist/bigconsole/assistant/assistantRunStore.js +3 -1
  4. package/dist/bigconsole/assistant/assistantRunStore.js.map +1 -1
  5. package/dist/bigconsole/assistant/index.js +2 -1
  6. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +70 -60
  7. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
  8. package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +187 -175
  9. package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js.map +1 -1
  10. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +133 -0
  11. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -0
  12. package/dist/bigconsole/components/preview/AiPreviewPanel.js +88 -136
  13. package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
  14. package/dist/bigconsole/components/preview/previewShared.js +61 -0
  15. package/dist/bigconsole/components/preview/previewShared.js.map +1 -0
  16. package/dist/bigconsole/hooks/useDashboardOperations.js +72 -67
  17. package/dist/bigconsole/hooks/useDashboardOperations.js.map +1 -1
  18. package/dist/bigconsole/hooks/useDashboardTemplates.js +108 -105
  19. package/dist/bigconsole/hooks/useDashboardTemplates.js.map +1 -1
  20. package/dist/bigconsole/hooks/useDashboardViews.js +77 -74
  21. package/dist/bigconsole/hooks/useDashboardViews.js.map +1 -1
  22. package/dist/bigconsole/hooks/useImportDashboard.js +68 -64
  23. package/dist/bigconsole/hooks/useImportDashboard.js.map +1 -1
  24. package/dist/bigconsole/pages/DashboardsPage.js +240 -234
  25. package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
  26. package/dist/bigconsole/pages/InsightsPage.js +161 -143
  27. package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
  28. package/dist/bigconsole/providers/BigConsoleQuotaProvider.js +70 -0
  29. package/dist/bigconsole/providers/BigConsoleQuotaProvider.js.map +1 -0
  30. package/dist/bigconsole/utils/quota.js +86 -0
  31. package/dist/bigconsole/utils/quota.js.map +1 -0
  32. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"SavedViewsDropdown.js","names":[],"sources":["../../../../../src/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.tsx"],"sourcesContent":["/**\n * SavedViewsDropdown Component\n *\n * Dropdown for managing and switching between saved dashboard views.\n */\n\nimport React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useDashboardViews, type DashboardView } from '../../../hooks';\nimport { SEMANTIC_COLORS } from '../../../utils/semanticTokens';\n\n// ============================================================================\n// Solid Background Colors (for portal-rendered dropdowns)\n// ============================================================================\n\n// Hook to detect dark mode and provide solid background color\nfunction useSolidBackground() {\n const [isDark, setIsDark] = useState(false);\n\n useEffect(() => {\n const checkDarkMode = () => {\n setIsDark(document.documentElement.classList.contains('dark'));\n };\n checkDarkMode();\n\n const observer = new MutationObserver(checkDarkMode);\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });\n return () => observer.disconnect();\n }, []);\n\n // Return solid opaque colors - no transparency\n return isDark ? '#1f2937' : '#ffffff';\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SavedViewsDropdownProps {\n dashboardId: string;\n currentFilters?: Record<string, unknown>;\n currentLayout?: Record<string, unknown>;\n onViewLoad?: (view: DashboardView) => void;\n className?: string;\n}\n\n// Use shared semantic tokens for portal rendering\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function SavedViewsDropdown({\n dashboardId,\n currentFilters,\n currentLayout,\n onViewLoad,\n className = '',\n}: SavedViewsDropdownProps) {\n const {\n views,\n currentView,\n loading,\n fetchViews,\n saveCurrentView,\n loadView,\n updateView,\n deleteView,\n setAsDefault,\n clearCurrentView,\n } = useDashboardViews();\n\n const [isOpen, setIsOpen] = useState(false);\n const [showSaveDialog, setShowSaveDialog] = useState(false);\n const [newViewName, setNewViewName] = useState('');\n const [newViewDescription, setNewViewDescription] = useState('');\n const [saveAsDefault, setSaveAsDefault] = useState(false);\n const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0 });\n\n const buttonRef = useRef<HTMLButtonElement>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Get solid background color for portal-rendered dropdown (no transparency)\n const solidBgColor = useSolidBackground();\n\n // Fetch views on mount\n useEffect(() => {\n if (dashboardId) {\n fetchViews(dashboardId);\n }\n }, [dashboardId, fetchViews]);\n\n // Update dropdown position when opening\n useEffect(() => {\n if (isOpen && buttonRef.current) {\n const rect = buttonRef.current.getBoundingClientRect();\n const dropdownWidth = 280;\n\n // Calculate left position - align left edge with button, but ensure it doesn't go off screen\n let leftPos = rect.left;\n\n // If dropdown would go off the right edge of screen, align right edge with button instead\n if (leftPos + dropdownWidth > window.innerWidth - 16) {\n leftPos = rect.right - dropdownWidth;\n }\n\n // Ensure minimum left margin\n leftPos = Math.max(16, leftPos);\n\n setDropdownPosition({\n top: rect.bottom + 4,\n left: leftPos,\n });\n }\n }, [isOpen]);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n function handleClickOutside(event: MouseEvent) {\n const target = event.target as Node;\n if (\n buttonRef.current &&\n !buttonRef.current.contains(target) &&\n dropdownRef.current &&\n !dropdownRef.current.contains(target)\n ) {\n setIsOpen(false);\n }\n }\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }\n }, [isOpen]);\n\n // Handle view selection\n const handleSelectView = useCallback(\n (viewId: string) => {\n const view = views.find((v) => v.id === viewId);\n if (view) {\n loadView(viewId);\n if (onViewLoad) {\n onViewLoad(view);\n }\n }\n setIsOpen(false);\n },\n [views, loadView, onViewLoad]\n );\n\n // Handle save new view\n const handleSaveView = useCallback(async () => {\n if (!newViewName.trim()) return;\n\n await saveCurrentView({\n dashboardId,\n name: newViewName.trim(),\n description: newViewDescription.trim() || undefined,\n filters: currentFilters,\n layout: currentLayout,\n isDefault: saveAsDefault,\n });\n\n setNewViewName('');\n setNewViewDescription('');\n setSaveAsDefault(false);\n setShowSaveDialog(false);\n }, [dashboardId, newViewName, newViewDescription, currentFilters, currentLayout, saveAsDefault, saveCurrentView]);\n\n // Handle update current view\n const handleUpdateCurrentView = useCallback(async () => {\n if (!currentView) return;\n\n await updateView({\n id: currentView.id,\n filters: currentFilters,\n layout: currentLayout,\n });\n }, [currentView, currentFilters, currentLayout, updateView]);\n\n // Handle delete view\n const handleDeleteView = useCallback(\n async (viewId: string, e: React.MouseEvent) => {\n e.stopPropagation();\n if (window.confirm('Are you sure you want to delete this view?')) {\n await deleteView(viewId);\n }\n },\n [deleteView]\n );\n\n // Handle set as default\n const handleSetDefault = useCallback(\n async (viewId: string, e: React.MouseEvent) => {\n e.stopPropagation();\n await setAsDefault(viewId);\n },\n [setAsDefault]\n );\n\n // Clear current view\n const handleClearView = useCallback(() => {\n clearCurrentView();\n setIsOpen(false);\n }, [clearCurrentView]);\n\n // Dropdown Menu rendered via portal\n const dropdownMenu = isOpen\n ? createPortal(\n <div\n ref={dropdownRef}\n style={{\n position: 'fixed',\n top: dropdownPosition.top,\n left: Math.max(16, dropdownPosition.left), // Ensure minimum left margin\n minWidth: '280px',\n backgroundColor: solidBgColor, // Solid opaque background - no transparency\n border: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '8px',\n boxShadow: '0 8px 24px rgba(0,0,0,0.15), 0 4px 8px rgba(0,0,0,0.1)',\n zIndex: 99998,\n overflow: 'hidden',\n }}\n >\n {/* Header */}\n <div\n style={{\n padding: '12px 16px',\n borderBottom: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n }}\n >\n <span style={{ fontSize: '13px', fontWeight: 600, color: SEMANTIC_COLORS.textPrimary }}>Saved Views</span>\n <button\n onClick={() => setShowSaveDialog(true)}\n style={{\n padding: '4px 10px',\n backgroundColor: SEMANTIC_COLORS.actionPrimaryBg,\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n }}\n >\n + Save Current\n </button>\n </div>\n\n {/* Views List */}\n <div style={{ maxHeight: '300px', overflowY: 'auto' }}>\n {loading && views.length === 0 ? (\n <div style={{ padding: '16px', color: SEMANTIC_COLORS.textSecondary, fontSize: '13px' }}>\n Loading views...\n </div>\n ) : views.length === 0 ? (\n <div style={{ padding: '16px', color: SEMANTIC_COLORS.textSecondary, fontSize: '13px' }}>\n No saved views yet. Save your current view to get started.\n </div>\n ) : (\n <>\n {/* Reset to Default Option */}\n <button\n onClick={handleClearView}\n style={{\n display: 'flex',\n alignItems: 'center',\n width: '100%',\n padding: '10px 16px',\n backgroundColor: !currentView ? SEMANTIC_COLORS.bgSecondary : SEMANTIC_COLORS.bgPrimary,\n border: 'none',\n cursor: 'pointer',\n textAlign: 'left',\n borderBottom: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n }}\n >\n <div>\n <div style={{ fontSize: '13px', fontWeight: 500, color: SEMANTIC_COLORS.textPrimary }}>\n Default View\n </div>\n <div style={{ fontSize: '11px', color: SEMANTIC_COLORS.textSecondary }}>\n Reset to original dashboard state\n </div>\n </div>\n </button>\n\n {/* Saved Views */}\n {views.map((view) => (\n <div\n key={view.id}\n onClick={() => handleSelectView(view.id)}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '10px 16px',\n backgroundColor:\n currentView?.id === view.id ? SEMANTIC_COLORS.bgSecondary : SEMANTIC_COLORS.bgPrimary,\n cursor: 'pointer',\n borderBottom: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n }}\n >\n <div style={{ flex: 1 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>\n <span\n style={{\n fontSize: '13px',\n fontWeight: 500,\n color: SEMANTIC_COLORS.textPrimary,\n }}\n >\n {view.name}\n </span>\n {view.isDefault && (\n <span\n style={{\n fontSize: '9px',\n padding: '2px 4px',\n backgroundColor: SEMANTIC_COLORS.actionPrimaryBgSubtle,\n color: SEMANTIC_COLORS.actionPrimaryBg,\n borderRadius: '4px',\n textTransform: 'uppercase',\n fontWeight: 600,\n }}\n >\n Default\n </span>\n )}\n </div>\n {view.description && (\n <div style={{ fontSize: '11px', color: SEMANTIC_COLORS.textSecondary, marginTop: '2px' }}>\n {view.description}\n </div>\n )}\n <div style={{ fontSize: '10px', color: SEMANTIC_COLORS.textSecondary, marginTop: '2px' }}>\n {new Date(view.updatedAt).toLocaleDateString()}\n </div>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>\n {!view.isDefault && (\n <button\n onClick={(e) => handleSetDefault(view.id, e)}\n title=\"Set as default\"\n style={{\n padding: '4px 6px',\n backgroundColor: 'transparent',\n border: 'none',\n cursor: 'pointer',\n color: SEMANTIC_COLORS.textSecondary,\n fontSize: '11px',\n }}\n >\n Set Default\n </button>\n )}\n <button\n onClick={(e) => handleDeleteView(view.id, e)}\n title=\"Delete view\"\n style={{\n padding: '4px 6px',\n backgroundColor: 'transparent',\n border: 'none',\n cursor: 'pointer',\n color: SEMANTIC_COLORS.statusErrorText,\n fontSize: '11px',\n }}\n >\n Delete\n </button>\n </div>\n </div>\n ))}\n </>\n )}\n </div>\n\n {/* Update Current View Button */}\n {currentView && (\n <div\n style={{\n padding: '12px 16px',\n borderTop: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n backgroundColor: SEMANTIC_COLORS.bgSecondary,\n }}\n >\n <button\n onClick={handleUpdateCurrentView}\n style={{\n width: '100%',\n padding: '8px',\n backgroundColor: SEMANTIC_COLORS.bgPrimary,\n border: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n color: SEMANTIC_COLORS.textPrimary,\n }}\n >\n Update \"{currentView.name}\" with current state\n </button>\n </div>\n )}\n </div>,\n document.body\n )\n : null;\n\n // Save Dialog rendered via portal\n const saveDialog = showSaveDialog\n ? createPortal(\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 99999,\n isolation: 'isolate',\n }}\n onClick={() => setShowSaveDialog(false)}\n >\n <div\n style={{\n width: '400px',\n backgroundColor: solidBgColor, // Solid opaque background - no transparency\n borderRadius: '8px',\n boxShadow: '0 4px 24px rgba(0, 0, 0, 0.3)',\n overflow: 'hidden',\n position: 'relative',\n zIndex: 100000,\n color: SEMANTIC_COLORS.textPrimary,\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n style={{\n padding: '16px 20px',\n borderBottom: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n }}\n >\n <h3 style={{ margin: 0, fontSize: '16px', fontWeight: 600, color: SEMANTIC_COLORS.textPrimary }}>\n Save Current View\n </h3>\n </div>\n <div style={{ padding: '20px' }}>\n <div style={{ marginBottom: '16px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '6px',\n fontSize: '13px',\n fontWeight: 500,\n color: SEMANTIC_COLORS.textPrimary,\n }}\n >\n View Name *\n </label>\n <input\n type=\"text\"\n value={newViewName}\n onChange={(e) => setNewViewName(e.target.value)}\n placeholder=\"e.g., Q4 Analysis, Sales Overview\"\n style={{\n width: '100%',\n padding: '10px 12px',\n border: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '6px',\n fontSize: '14px',\n backgroundColor: SEMANTIC_COLORS.bgPrimary,\n color: SEMANTIC_COLORS.textPrimary,\n boxSizing: 'border-box',\n }}\n />\n </div>\n <div style={{ marginBottom: '16px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '6px',\n fontSize: '13px',\n fontWeight: 500,\n color: SEMANTIC_COLORS.textPrimary,\n }}\n >\n Description\n </label>\n <textarea\n value={newViewDescription}\n onChange={(e) => setNewViewDescription(e.target.value)}\n placeholder=\"Optional description...\"\n rows={2}\n style={{\n width: '100%',\n padding: '10px 12px',\n border: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '6px',\n fontSize: '14px',\n resize: 'vertical',\n backgroundColor: SEMANTIC_COLORS.bgPrimary,\n color: SEMANTIC_COLORS.textPrimary,\n boxSizing: 'border-box',\n }}\n />\n </div>\n <label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>\n <input type=\"checkbox\" checked={saveAsDefault} onChange={(e) => setSaveAsDefault(e.target.checked)} />\n <span style={{ fontSize: '13px', color: SEMANTIC_COLORS.textPrimary }}>Set as default view</span>\n </label>\n </div>\n <div\n style={{\n padding: '16px 20px',\n borderTop: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n display: 'flex',\n justifyContent: 'flex-end',\n gap: '12px',\n backgroundColor: SEMANTIC_COLORS.bgSecondary,\n }}\n >\n <button\n onClick={() => setShowSaveDialog(false)}\n style={{\n padding: '10px 20px',\n backgroundColor: SEMANTIC_COLORS.bgPrimary,\n border: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '14px',\n color: SEMANTIC_COLORS.textPrimary,\n }}\n >\n Cancel\n </button>\n <button\n onClick={handleSaveView}\n disabled={!newViewName.trim()}\n style={{\n padding: '10px 20px',\n backgroundColor: newViewName.trim() ? SEMANTIC_COLORS.actionPrimaryBg : SEMANTIC_COLORS.bgSecondary,\n color: newViewName.trim() ? 'white' : SEMANTIC_COLORS.textSecondary,\n border: 'none',\n borderRadius: '6px',\n cursor: newViewName.trim() ? 'pointer' : 'not-allowed',\n fontSize: '14px',\n fontWeight: 500,\n }}\n >\n Save View\n </button>\n </div>\n </div>\n </div>,\n document.body\n )\n : null;\n\n return (\n <div className={className} style={{ position: 'relative', display: 'inline-block' }}>\n {/* Trigger Button */}\n <button\n ref={buttonRef}\n onClick={() => setIsOpen(!isOpen)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n padding: '6px 12px',\n backgroundColor: currentView ? SEMANTIC_COLORS.actionPrimaryBgSubtle : SEMANTIC_COLORS.bgSecondary,\n border: `1px solid ${currentView ? SEMANTIC_COLORS.actionPrimaryBg : SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '13px',\n color: currentView ? SEMANTIC_COLORS.actionPrimaryBg : SEMANTIC_COLORS.textPrimary,\n }}\n >\n <span style={{ fontWeight: 500 }}>{currentView ? currentView.name : 'Views'}</span>\n <span style={{ fontSize: '10px' }}>{isOpen ? '▲' : '▼'}</span>\n </button>\n\n {/* Dropdown Menu - rendered via portal */}\n {dropdownMenu}\n\n {/* Save Dialog - rendered via portal */}\n {saveDialog}\n </div>\n );\n}\n\nexport default SavedViewsDropdown;\n"],"mappings":"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"}
1
+ {"version":3,"file":"SavedViewsDropdown.js","names":[],"sources":["../../../../../src/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.tsx"],"sourcesContent":["/**\n * SavedViewsDropdown Component\n *\n * Dropdown for managing and switching between saved dashboard views.\n */\n\nimport React, { useState, useEffect, useCallback, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useDashboardViews, type DashboardView } from '../../../hooks';\nimport { SEMANTIC_COLORS } from '../../../utils/semanticTokens';\nimport { reportBigConsoleQuotaError } from '../../../utils/quota';\n\n// ============================================================================\n// Solid Background Colors (for portal-rendered dropdowns)\n// ============================================================================\n\n// Hook to detect dark mode and provide solid background color\nfunction useSolidBackground() {\n const [isDark, setIsDark] = useState(false);\n\n useEffect(() => {\n const checkDarkMode = () => {\n setIsDark(document.documentElement.classList.contains('dark'));\n };\n checkDarkMode();\n\n const observer = new MutationObserver(checkDarkMode);\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });\n return () => observer.disconnect();\n }, []);\n\n // Return solid opaque colors - no transparency\n return isDark ? '#1f2937' : '#ffffff';\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SavedViewsDropdownProps {\n dashboardId: string;\n currentFilters?: Record<string, unknown>;\n currentLayout?: Record<string, unknown>;\n onViewLoad?: (view: DashboardView) => void;\n className?: string;\n}\n\n// Use shared semantic tokens for portal rendering\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function SavedViewsDropdown({\n dashboardId,\n currentFilters,\n currentLayout,\n onViewLoad,\n className = '',\n}: SavedViewsDropdownProps) {\n const {\n views,\n currentView,\n loading,\n fetchViews,\n saveCurrentView,\n loadView,\n updateView,\n deleteView,\n setAsDefault,\n clearCurrentView,\n } = useDashboardViews();\n\n const [isOpen, setIsOpen] = useState(false);\n const [showSaveDialog, setShowSaveDialog] = useState(false);\n const [newViewName, setNewViewName] = useState('');\n const [newViewDescription, setNewViewDescription] = useState('');\n const [saveAsDefault, setSaveAsDefault] = useState(false);\n const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0 });\n\n const buttonRef = useRef<HTMLButtonElement>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Get solid background color for portal-rendered dropdown (no transparency)\n const solidBgColor = useSolidBackground();\n\n // Fetch views on mount\n useEffect(() => {\n if (dashboardId) {\n fetchViews(dashboardId);\n }\n }, [dashboardId, fetchViews]);\n\n // Update dropdown position when opening\n useEffect(() => {\n if (isOpen && buttonRef.current) {\n const rect = buttonRef.current.getBoundingClientRect();\n const dropdownWidth = 280;\n\n // Calculate left position - align left edge with button, but ensure it doesn't go off screen\n let leftPos = rect.left;\n\n // If dropdown would go off the right edge of screen, align right edge with button instead\n if (leftPos + dropdownWidth > window.innerWidth - 16) {\n leftPos = rect.right - dropdownWidth;\n }\n\n // Ensure minimum left margin\n leftPos = Math.max(16, leftPos);\n\n setDropdownPosition({\n top: rect.bottom + 4,\n left: leftPos,\n });\n }\n }, [isOpen]);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n function handleClickOutside(event: MouseEvent) {\n const target = event.target as Node;\n if (\n buttonRef.current &&\n !buttonRef.current.contains(target) &&\n dropdownRef.current &&\n !dropdownRef.current.contains(target)\n ) {\n setIsOpen(false);\n }\n }\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }\n }, [isOpen]);\n\n // Handle view selection\n const handleSelectView = useCallback(\n (viewId: string) => {\n const view = views.find((v) => v.id === viewId);\n if (view) {\n loadView(viewId);\n if (onViewLoad) {\n onViewLoad(view);\n }\n }\n setIsOpen(false);\n },\n [views, loadView, onViewLoad]\n );\n\n // Handle save new view\n const handleSaveView = useCallback(async () => {\n if (!newViewName.trim()) return;\n\n try {\n await saveCurrentView({\n dashboardId,\n name: newViewName.trim(),\n description: newViewDescription.trim() || undefined,\n filters: currentFilters,\n layout: currentLayout,\n isDefault: saveAsDefault,\n });\n } catch (err) {\n if (reportBigConsoleQuotaError(err, 'session')) {\n setShowSaveDialog(false);\n setIsOpen(false);\n return;\n }\n throw err;\n }\n\n setNewViewName('');\n setNewViewDescription('');\n setSaveAsDefault(false);\n setShowSaveDialog(false);\n }, [dashboardId, newViewName, newViewDescription, currentFilters, currentLayout, saveAsDefault, saveCurrentView]);\n\n // Handle update current view\n const handleUpdateCurrentView = useCallback(async () => {\n if (!currentView) return;\n\n await updateView({\n id: currentView.id,\n filters: currentFilters,\n layout: currentLayout,\n });\n }, [currentView, currentFilters, currentLayout, updateView]);\n\n // Handle delete view\n const handleDeleteView = useCallback(\n async (viewId: string, e: React.MouseEvent) => {\n e.stopPropagation();\n if (window.confirm('Are you sure you want to delete this view?')) {\n await deleteView(viewId);\n }\n },\n [deleteView]\n );\n\n // Handle set as default\n const handleSetDefault = useCallback(\n async (viewId: string, e: React.MouseEvent) => {\n e.stopPropagation();\n await setAsDefault(viewId);\n },\n [setAsDefault]\n );\n\n // Clear current view\n const handleClearView = useCallback(() => {\n clearCurrentView();\n setIsOpen(false);\n }, [clearCurrentView]);\n\n // Dropdown Menu rendered via portal\n const dropdownMenu = isOpen\n ? createPortal(\n <div\n ref={dropdownRef}\n style={{\n position: 'fixed',\n top: dropdownPosition.top,\n left: Math.max(16, dropdownPosition.left), // Ensure minimum left margin\n minWidth: '280px',\n backgroundColor: solidBgColor, // Solid opaque background - no transparency\n border: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '8px',\n boxShadow: '0 8px 24px rgba(0,0,0,0.15), 0 4px 8px rgba(0,0,0,0.1)',\n zIndex: 99998,\n overflow: 'hidden',\n }}\n >\n {/* Header */}\n <div\n style={{\n padding: '12px 16px',\n borderBottom: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n }}\n >\n <span style={{ fontSize: '13px', fontWeight: 600, color: SEMANTIC_COLORS.textPrimary }}>Saved Views</span>\n <button\n onClick={() => setShowSaveDialog(true)}\n style={{\n padding: '4px 10px',\n backgroundColor: SEMANTIC_COLORS.actionPrimaryBg,\n color: 'white',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n }}\n >\n + Save Current\n </button>\n </div>\n\n {/* Views List */}\n <div style={{ maxHeight: '300px', overflowY: 'auto' }}>\n {loading && views.length === 0 ? (\n <div style={{ padding: '16px', color: SEMANTIC_COLORS.textSecondary, fontSize: '13px' }}>\n Loading views...\n </div>\n ) : views.length === 0 ? (\n <div style={{ padding: '16px', color: SEMANTIC_COLORS.textSecondary, fontSize: '13px' }}>\n No saved views yet. Save your current view to get started.\n </div>\n ) : (\n <>\n {/* Reset to Default Option */}\n <button\n onClick={handleClearView}\n style={{\n display: 'flex',\n alignItems: 'center',\n width: '100%',\n padding: '10px 16px',\n backgroundColor: !currentView ? SEMANTIC_COLORS.bgSecondary : SEMANTIC_COLORS.bgPrimary,\n border: 'none',\n cursor: 'pointer',\n textAlign: 'left',\n borderBottom: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n }}\n >\n <div>\n <div style={{ fontSize: '13px', fontWeight: 500, color: SEMANTIC_COLORS.textPrimary }}>\n Default View\n </div>\n <div style={{ fontSize: '11px', color: SEMANTIC_COLORS.textSecondary }}>\n Reset to original dashboard state\n </div>\n </div>\n </button>\n\n {/* Saved Views */}\n {views.map((view) => (\n <div\n key={view.id}\n onClick={() => handleSelectView(view.id)}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '10px 16px',\n backgroundColor:\n currentView?.id === view.id ? SEMANTIC_COLORS.bgSecondary : SEMANTIC_COLORS.bgPrimary,\n cursor: 'pointer',\n borderBottom: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n }}\n >\n <div style={{ flex: 1 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>\n <span\n style={{\n fontSize: '13px',\n fontWeight: 500,\n color: SEMANTIC_COLORS.textPrimary,\n }}\n >\n {view.name}\n </span>\n {view.isDefault && (\n <span\n style={{\n fontSize: '9px',\n padding: '2px 4px',\n backgroundColor: SEMANTIC_COLORS.actionPrimaryBgSubtle,\n color: SEMANTIC_COLORS.actionPrimaryBg,\n borderRadius: '4px',\n textTransform: 'uppercase',\n fontWeight: 600,\n }}\n >\n Default\n </span>\n )}\n </div>\n {view.description && (\n <div style={{ fontSize: '11px', color: SEMANTIC_COLORS.textSecondary, marginTop: '2px' }}>\n {view.description}\n </div>\n )}\n <div style={{ fontSize: '10px', color: SEMANTIC_COLORS.textSecondary, marginTop: '2px' }}>\n {new Date(view.updatedAt).toLocaleDateString()}\n </div>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>\n {!view.isDefault && (\n <button\n onClick={(e) => handleSetDefault(view.id, e)}\n title=\"Set as default\"\n style={{\n padding: '4px 6px',\n backgroundColor: 'transparent',\n border: 'none',\n cursor: 'pointer',\n color: SEMANTIC_COLORS.textSecondary,\n fontSize: '11px',\n }}\n >\n Set Default\n </button>\n )}\n <button\n onClick={(e) => handleDeleteView(view.id, e)}\n title=\"Delete view\"\n style={{\n padding: '4px 6px',\n backgroundColor: 'transparent',\n border: 'none',\n cursor: 'pointer',\n color: SEMANTIC_COLORS.statusErrorText,\n fontSize: '11px',\n }}\n >\n Delete\n </button>\n </div>\n </div>\n ))}\n </>\n )}\n </div>\n\n {/* Update Current View Button */}\n {currentView && (\n <div\n style={{\n padding: '12px 16px',\n borderTop: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n backgroundColor: SEMANTIC_COLORS.bgSecondary,\n }}\n >\n <button\n onClick={handleUpdateCurrentView}\n style={{\n width: '100%',\n padding: '8px',\n backgroundColor: SEMANTIC_COLORS.bgPrimary,\n border: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n color: SEMANTIC_COLORS.textPrimary,\n }}\n >\n Update \"{currentView.name}\" with current state\n </button>\n </div>\n )}\n </div>,\n document.body\n )\n : null;\n\n // Save Dialog rendered via portal\n const saveDialog = showSaveDialog\n ? createPortal(\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 99999,\n isolation: 'isolate',\n }}\n onClick={() => setShowSaveDialog(false)}\n >\n <div\n style={{\n width: '400px',\n backgroundColor: solidBgColor, // Solid opaque background - no transparency\n borderRadius: '8px',\n boxShadow: '0 4px 24px rgba(0, 0, 0, 0.3)',\n overflow: 'hidden',\n position: 'relative',\n zIndex: 100000,\n color: SEMANTIC_COLORS.textPrimary,\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n style={{\n padding: '16px 20px',\n borderBottom: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n }}\n >\n <h3 style={{ margin: 0, fontSize: '16px', fontWeight: 600, color: SEMANTIC_COLORS.textPrimary }}>\n Save Current View\n </h3>\n </div>\n <div style={{ padding: '20px' }}>\n <div style={{ marginBottom: '16px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '6px',\n fontSize: '13px',\n fontWeight: 500,\n color: SEMANTIC_COLORS.textPrimary,\n }}\n >\n View Name *\n </label>\n <input\n type=\"text\"\n value={newViewName}\n onChange={(e) => setNewViewName(e.target.value)}\n placeholder=\"e.g., Q4 Analysis, Sales Overview\"\n style={{\n width: '100%',\n padding: '10px 12px',\n border: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '6px',\n fontSize: '14px',\n backgroundColor: SEMANTIC_COLORS.bgPrimary,\n color: SEMANTIC_COLORS.textPrimary,\n boxSizing: 'border-box',\n }}\n />\n </div>\n <div style={{ marginBottom: '16px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '6px',\n fontSize: '13px',\n fontWeight: 500,\n color: SEMANTIC_COLORS.textPrimary,\n }}\n >\n Description\n </label>\n <textarea\n value={newViewDescription}\n onChange={(e) => setNewViewDescription(e.target.value)}\n placeholder=\"Optional description...\"\n rows={2}\n style={{\n width: '100%',\n padding: '10px 12px',\n border: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '6px',\n fontSize: '14px',\n resize: 'vertical',\n backgroundColor: SEMANTIC_COLORS.bgPrimary,\n color: SEMANTIC_COLORS.textPrimary,\n boxSizing: 'border-box',\n }}\n />\n </div>\n <label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>\n <input type=\"checkbox\" checked={saveAsDefault} onChange={(e) => setSaveAsDefault(e.target.checked)} />\n <span style={{ fontSize: '13px', color: SEMANTIC_COLORS.textPrimary }}>Set as default view</span>\n </label>\n </div>\n <div\n style={{\n padding: '16px 20px',\n borderTop: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n display: 'flex',\n justifyContent: 'flex-end',\n gap: '12px',\n backgroundColor: SEMANTIC_COLORS.bgSecondary,\n }}\n >\n <button\n onClick={() => setShowSaveDialog(false)}\n style={{\n padding: '10px 20px',\n backgroundColor: SEMANTIC_COLORS.bgPrimary,\n border: `1px solid ${SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '14px',\n color: SEMANTIC_COLORS.textPrimary,\n }}\n >\n Cancel\n </button>\n <button\n onClick={handleSaveView}\n disabled={!newViewName.trim()}\n style={{\n padding: '10px 20px',\n backgroundColor: newViewName.trim() ? SEMANTIC_COLORS.actionPrimaryBg : SEMANTIC_COLORS.bgSecondary,\n color: newViewName.trim() ? 'white' : SEMANTIC_COLORS.textSecondary,\n border: 'none',\n borderRadius: '6px',\n cursor: newViewName.trim() ? 'pointer' : 'not-allowed',\n fontSize: '14px',\n fontWeight: 500,\n }}\n >\n Save View\n </button>\n </div>\n </div>\n </div>,\n document.body\n )\n : null;\n\n return (\n <div className={className} style={{ position: 'relative', display: 'inline-block' }}>\n {/* Trigger Button */}\n <button\n ref={buttonRef}\n onClick={() => setIsOpen(!isOpen)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n padding: '6px 12px',\n backgroundColor: currentView ? SEMANTIC_COLORS.actionPrimaryBgSubtle : SEMANTIC_COLORS.bgSecondary,\n border: `1px solid ${currentView ? SEMANTIC_COLORS.actionPrimaryBg : SEMANTIC_COLORS.borderDefault}`,\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '13px',\n color: currentView ? SEMANTIC_COLORS.actionPrimaryBg : SEMANTIC_COLORS.textPrimary,\n }}\n >\n <span style={{ fontWeight: 500 }}>{currentView ? currentView.name : 'Views'}</span>\n <span style={{ fontSize: '10px' }}>{isOpen ? '▲' : '▼'}</span>\n </button>\n\n {/* Dropdown Menu - rendered via portal */}\n {dropdownMenu}\n\n {/* Save Dialog - rendered via portal */}\n {saveDialog}\n </div>\n );\n}\n\nexport default SavedViewsDropdown;\n"],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ import { useAssistantRunStore as e } from "../../assistant/assistantRunStore.js";
2
+ import { StepRail as t, formatAgo as n, useAssistantSidebarWidth as r } from "./previewShared.js";
3
+ import { useEffect as i, useState as a } from "react";
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";
7
+ //#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());
10
+ i(() => {
11
+ if (!m) return;
12
+ let e = window.setInterval(() => E(Date.now()), 1e3);
13
+ return () => window.clearInterval(e);
14
+ }, [m]);
15
+ let D = r(), O = (e) => {
16
+ p ? p(e) : typeof window < "u" && window.location.assign(e);
17
+ };
18
+ if (!g || _) return null;
19
+ if (!h) return /* @__PURE__ */ d("div", {
20
+ className: "flex justify-end px-4 pt-1 md:px-6",
21
+ style: { marginRight: D },
22
+ children: /* @__PURE__ */ f(u, {
23
+ size: "sm",
24
+ variant: "outline",
25
+ onClick: C,
26
+ "data-testid": "ai-preview-open",
27
+ 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
31
+ ]
32
+ })
33
+ });
34
+ let k = S ? Math.max(0, Math.floor((T - S) / 1e3)) : 0, A = [];
35
+ return y.datasink && A.push({
36
+ key: "datasink",
37
+ label: "Data Sink",
38
+ path: `/bigconsole/datasinks/${y.datasink}`
39
+ }), y.dashboard && A.push({
40
+ key: "dashboard",
41
+ label: "Dashboard",
42
+ path: `/bigconsole/dashboards/${y.dashboard}`
43
+ }), y.parser && A.push({
44
+ key: "parser",
45
+ label: "Parser",
46
+ path: `/bigconsole/parsers/${y.parser}`
47
+ }), /* @__PURE__ */ f("section", {
48
+ "data-testid": "ai-preview-panel",
49
+ "data-variant": "mini",
50
+ "aria-label": "AI preview",
51
+ style: { marginRight: D ? D + 16 : void 0 },
52
+ className: "mx-4 mt-1 mb-3 overflow-hidden rounded-lg border border-border-default bg-bg-sunken shadow-sm md:mx-6",
53
+ children: [
54
+ /* @__PURE__ */ f("div", {
55
+ 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
+ className: "flex min-w-0 items-center gap-2",
58
+ children: [
59
+ /* @__PURE__ */ d(c, { className: "size-4 shrink-0 text-action-primary-bg" }),
60
+ /* @__PURE__ */ d("h2", {
61
+ className: "truncate text-sm font-semibold text-text-primary",
62
+ children: "AI Preview"
63
+ }),
64
+ m ? /* @__PURE__ */ f("span", {
65
+ 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
+ "data-testid": "ai-preview-live",
67
+ children: [/* @__PURE__ */ f("span", {
68
+ 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"]
71
+ }) : null,
72
+ /* @__PURE__ */ f("span", {
73
+ className: "truncate text-xs text-text-muted",
74
+ "data-testid": "ai-preview-ago",
75
+ children: [
76
+ m ? b || "Working…" : x ? "Stopped" : "Done",
77
+ " · updated",
78
+ " ",
79
+ n(k)
80
+ ]
81
+ })
82
+ ]
83
+ }), /* @__PURE__ */ d(u, {
84
+ size: "sm",
85
+ variant: "ghost",
86
+ onClick: w,
87
+ "aria-label": "Close preview",
88
+ "data-testid": "ai-preview-close",
89
+ children: /* @__PURE__ */ d(l, { className: "size-4" })
90
+ })]
91
+ }),
92
+ /* @__PURE__ */ d("div", {
93
+ className: "border-b border-border-subtle px-3 py-2",
94
+ children: /* @__PURE__ */ d(t, { steps: v })
95
+ }),
96
+ /* @__PURE__ */ f("div", {
97
+ className: "space-y-2 p-3",
98
+ children: [A.length === 0 ? /* @__PURE__ */ d("p", {
99
+ 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", {
102
+ "data-testid": "ai-preview-row",
103
+ className: "flex items-center justify-between gap-3 rounded-md border border-border-subtle bg-bg-surface px-3 py-2",
104
+ children: [/* @__PURE__ */ f("p", {
105
+ className: "truncate text-sm font-medium text-text-primary",
106
+ children: [e.label, " ready"]
107
+ }), /* @__PURE__ */ f(u, {
108
+ size: "sm",
109
+ variant: "ghost",
110
+ onClick: () => O(e.path),
111
+ children: ["Open", /* @__PURE__ */ d(o, { className: "ml-1 size-3.5" })]
112
+ })]
113
+ }, e.key)), y.dashboard ? /* @__PURE__ */ f(u, {
114
+ size: "sm",
115
+ variant: "outline",
116
+ className: "w-full",
117
+ "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" })]
120
+ }) : null]
121
+ }),
122
+ x ? /* @__PURE__ */ d("p", {
123
+ className: "border-t border-border-subtle px-3 py-2 text-xs text-status-error-text",
124
+ "data-testid": "ai-preview-error",
125
+ children: x
126
+ }) : null
127
+ ]
128
+ });
129
+ }
130
+ //#endregion
131
+ export { p as default };
132
+
133
+ //# sourceMappingURL=AiPreviewMiniPanel.js.map
@@ -0,0 +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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"}