@burdenoff/microfe-bigconsole 2026.812.2 → 2026.827.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/components/builder/inspector/LayoutSection.js +71 -0
- package/dist/bigconsole/components/builder/inspector/LayoutSection.js.map +1 -0
- package/dist/bigconsole/components/builder/inspector/index.js +1 -0
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js +137 -121
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js +204 -93
- package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.js +259 -133
- package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/StoreTemplatePicker.js +87 -39
- package/dist/bigconsole/components/dashboard/StoreTemplatePicker.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkCard.js +88 -68
- package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +213 -199
- package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
- package/dist/bigconsole/components/workflow/WorkflowCard.js +46 -27
- package/dist/bigconsole/components/workflow/WorkflowCard.js.map +1 -1
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +44 -47
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js.map +1 -1
- package/dist/bigconsole/customWidget/proposalSource.js +31 -24
- package/dist/bigconsole/customWidget/proposalSource.js.map +1 -1
- package/dist/bigconsole/hooks/useDashboardOperations.js +74 -64
- package/dist/bigconsole/hooks/useDashboardOperations.js.map +1 -1
- package/dist/bigconsole/hooks/useDataSinkOperations.js +2 -2
- package/dist/bigconsole/hooks/useDataSinkOperations.js.map +1 -1
- package/dist/bigconsole/pages/DashboardBuilderPage.js +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +386 -316
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinksPage.js +251 -206
- package/dist/bigconsole/pages/DataSinksPage.js.map +1 -1
- package/dist/bigconsole/pages/InsightsPage.js +646 -378
- package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowsPage.js +153 -108
- package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
- package/dist/bigconsole/utils/format.js +18 -0
- package/dist/bigconsole/utils/format.js.map +1 -0
- package/dist/bigconsole/utils/widgetPositioning.js +1 -1
- package/dist/generated/wspace-operations.js +1 -0
- package/dist/generated/wspace-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardCanvas.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/DashboardCanvas.tsx"],"sourcesContent":["/**\n * DashboardCanvas Component\n *\n * Main canvas for dashboard with grid layout and widget rendering.\n * Phase 2: Added GlobalFiltersBar and PageTabsManager integration.\n * Phase 3: Added WidgetPalette sidebar for drag-and-drop widget creation.\n */\n\nimport { type FC, memo, useRef, useState, useCallback, useEffect, useMemo } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus } from 'lucide-react';\nimport type { Layout } from 'react-grid-layout';\nimport type { Widget, WidgetType } from '../../types';\nimport { useDashboardStore, useWidgetStore } from '../../store';\nimport { useWidgetOperations } from '../../hooks/useWidgetOperations';\nimport { useInstalledWidgets } from '../../hooks/useInstalledWidgets';\nimport { useWidgetCatalog } from '../../hooks/useWidgetCatalog';\nimport { useDashboardImageExport } from '../../hooks/useDashboardImageExport';\nimport { useBigConsole } from '../../context';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Section } from '../common';\nimport { DashboardGrid } from './DashboardGrid';\nimport { DashboardToolbar } from './DashboardToolbar';\nimport { GlobalFiltersBar } from './GlobalFiltersBar';\nimport { LiveBoardBanner } from './LiveBoardBanner';\nimport { TemplateSetupBanner } from './TemplateSetupBanner';\nimport { PageTabsManager, type DashboardPageTab } from './PageTabsManager';\nimport { ShareDialog } from './ShareDialog';\nimport { WidgetPalette } from './WidgetPalette';\nimport { WidgetInspector } from '../builder';\nimport { getWidgetDefinition, type WidgetDefinition } from '../widgets/WidgetRegistry';\n// Phase 2: Cross-filtering context\nimport { CrossFilterProvider } from '../../contexts';\n// Smart widget positioning\nimport { findNextAvailablePosition, calculatePositionFromDrop } from '../../utils/widgetPositioning';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DashboardCanvasProps {\n /** Dashboard ID */\n dashboardId: string;\n /** Page ID (for loading widgets) */\n pageId?: string;\n /** Dashboard title */\n title?: string;\n /** Callback to paste widget */\n onPaste?: () => void;\n /** Callback when dashboard is saved */\n onSave?: () => void;\n /** Whether save is in progress */\n isSaving?: boolean;\n /** Callback to export dashboard */\n onExport?: () => void;\n onPublishTemplate?: () => void;\n /** Dashboard pages for PageTabsManager */\n pages?: DashboardPageTab[];\n /** Active page ID */\n activePageId?: string;\n /** Callback when page changes */\n onPageChange?: (pageId: string) => void;\n /** Callback when pages are reordered */\n onPagesReorder?: (pages: DashboardPageTab[]) => void;\n /** Callback when page is added */\n onPageAdd?: () => void;\n /** Callback when page is renamed */\n onPageRename?: (pageId: string, newName: string) => void;\n /** Callback when page is duplicated */\n onPageDuplicate?: (pageId: string) => void;\n /** Callback when page is deleted */\n onPageDelete?: (pageId: string) => void;\n /** Whether to show global filters bar */\n showFilters?: boolean;\n /** Whether to show page tabs */\n showPageTabs?: boolean;\n /** Whether to enable cross-filtering (widget clicks update filters) */\n enableCrossFilter?: boolean;\n}\n\ninterface WidgetPlacement {\n position: { x: number; y: number; width: number; height: number };\n definition?: WidgetDefinition | null;\n}\n\ninterface PaletteDropData {\n type: WidgetType;\n name?: string;\n category?: WidgetDefinition['category'];\n defaultConfig?: Record<string, unknown>;\n defaultSize?: WidgetDefinition['defaultSize'];\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardCanvas: FC<DashboardCanvasProps> = memo(function DashboardCanvas({\n dashboardId,\n pageId,\n title,\n onPaste,\n onSave,\n isSaving = false,\n onExport,\n onPublishTemplate,\n pages = [],\n activePageId,\n onPageChange,\n onPagesReorder,\n onPageAdd,\n onPageRename,\n onPageDuplicate,\n onPageDelete,\n showFilters = true,\n showPageTabs = true,\n enableCrossFilter = true,\n}) {\n const { t } = useI18n();\n const containerRef = useRef<HTMLDivElement>(null);\n // Inner board element (the grid surface) — rasterized for PNG/PDF board-pack export.\n const boardRef = useRef<HTMLDivElement>(null);\n const [containerWidth, setContainerWidth] = useState(1200);\n const [isShareDialogOpen, setIsShareDialogOpen] = useState(false);\n const [widgetCreateError, setWidgetCreateError] = useState<string | null>(null);\n const [isDraggingWidget, setIsDraggingWidget] = useState(false);\n const [dragWidgetType, setDragWidgetType] = useState<WidgetType | null>(null);\n\n // Store state\n const isEditMode = useDashboardStore((state) => state.viewMode === 'edit');\n\n // Fetch installed widgets only in edit mode — the palette and add-widget dialog\n // are not usable in read-only view, so skip the request for viewer-only sessions.\n const { workspaceId, apiGatewayUrl, authToken } = useBigConsole();\n const { installedWidgets, loading: installedWidgetsLoading } = useInstalledWidgets({\n workspaceId,\n apiGatewayUrl,\n authToken,\n enabled: isEditMode,\n });\n // Built-in widget catalog from the backend (getWidgetTypes); falls back to the\n // bundled registry while loading or if the supergraph hasn't adopted it yet.\n const { definitions: widgetCatalog } = useWidgetCatalog({\n workspaceId,\n apiGatewayUrl,\n authToken,\n enabled: isEditMode,\n });\n const zoomLevel = useDashboardStore((state) => state.zoomLevel);\n const sidebarCollapsed = useDashboardStore((state) => state.sidebarCollapsed);\n const toggleSidebar = useDashboardStore((state) => state.toggleSidebar);\n const isPaletteOpen = !sidebarCollapsed;\n const setGlobalFilterValues = useDashboardStore((state) => state.setGlobalFilterValues);\n\n // Handle filter changes - update store for widget access\n const handleFilterChange = useCallback(\n (filterValues: Record<string, unknown>) => {\n // Update global filter values in dashboard store\n setGlobalFilterValues(filterValues);\n\n // Widgets can now access filter values via useGlobalFilterValues() hook\n // When backend supports filter parameters, widget queries will include these values\n },\n [setGlobalFilterValues]\n );\n\n // Widget operations hook (connects to backend) - skipFetch since parent page fetches\n const { createWidget: createWidgetApi } = useWidgetOperations(pageId, dashboardId, { skipFetch: true });\n\n const widgetsMap = useWidgetStore((state) => state.widgets);\n // Filter widgets to only show widgets for the current page\n const widgetsArray = useMemo(() => {\n const allWidgets = Array.from(widgetsMap.values());\n if (!pageId) return allWidgets;\n return allWidgets.filter((widget) => widget.pageId === pageId);\n }, [widgetsMap, pageId]);\n const selectedWidgetId = useWidgetStore((state) => state.selectedWidgetId);\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n const addWidgetToStore = useWidgetStore((state) => state.addWidget);\n const updateWidgetPosition = useWidgetStore((state) => state.updateWidgetPosition);\n const configPanelOpen = useWidgetStore((state) => state.configPanelOpen);\n const closeConfigPanel = useWidgetStore((state) => state.closeConfigPanel);\n\n // Keep react-grid-layout's explicit width in sync with the actual canvas.\n // A ResizeObserver is required because opening the docked inspector or widget\n // palette resizes this element without resizing the browser window.\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const updateWidth = () => {\n setContainerWidth(container.offsetWidth);\n };\n\n updateWidth();\n\n if (typeof ResizeObserver !== 'undefined') {\n const observer = new ResizeObserver(updateWidth);\n observer.observe(container);\n return () => observer.disconnect();\n }\n\n window.addEventListener('resize', updateWidth);\n return () => window.removeEventListener('resize', updateWidth);\n }, []);\n\n // Handle layout change\n const handleLayoutChange = useCallback(\n (layout: Layout[]) => {\n layout.forEach((item) => {\n updateWidgetPosition(item.i, {\n x: item.x,\n y: item.y,\n width: item.w,\n height: item.h,\n });\n });\n },\n [updateWidgetPosition]\n );\n\n // Handle widget select\n const handleWidgetSelect = useCallback(\n (widgetId: string) => {\n selectWidget(widgetId);\n },\n [selectWidget]\n );\n\n // Create with defaults, select the result, then configure it in the inspector.\n const handleAddWidget = useCallback(\n async (widgetData: Partial<Widget>, placement?: WidgetPlacement) => {\n setWidgetCreateError(null);\n const definition = placement?.definition || (widgetData.type ? getWidgetDefinition(widgetData.type) : null);\n const widgetSize = {\n width: definition?.defaultSize?.width ?? 4,\n height: definition?.defaultSize?.height ?? 4,\n };\n\n let position: { x: number; y: number; width: number; height: number };\n if (placement?.position) {\n position = placement.position;\n } else {\n // Use smart placement algorithm - horizontal first, then next row\n const { x, y } = findNextAvailablePosition(widgetsArray, widgetSize, { cols: 12 });\n position = {\n x,\n y,\n width: widgetSize.width,\n height: widgetSize.height,\n };\n }\n\n // Preserve legacy binding fields when creating from imported/default data.\n const extendedData = widgetData as Partial<Widget> & { dataSinkId?: string; parserId?: string };\n\n // Try to create via API first\n if (pageId) {\n const widget = await createWidgetApi({\n pageId,\n dashboardId,\n type: widgetData.type!,\n title: widgetData.title || t('bigconsole.dashboards.canvas.untitledWidget', 'Untitled Widget'),\n description: widgetData.description,\n // v2.0: Renderer and template support\n renderer: widgetData.renderer,\n template: widgetData.template,\n config: widgetData.config || {},\n position,\n refreshInterval: widgetData.refreshInterval ?? undefined,\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId: extendedData.dataSinkId,\n parserId: extendedData.parserId,\n });\n\n if (widget) {\n selectWidget(widget.id);\n return;\n }\n\n setWidgetCreateError(\n t(\n 'bigconsole.dashboards.canvas.widgetCreateError',\n 'Could not create the widget. Check the connection and try again.'\n )\n );\n return;\n }\n\n // Fallback to local store if API fails or no pageId\n const newWidget: Widget = {\n id: crypto.randomUUID(),\n dashboardId,\n pageId: pageId || 'local',\n type: widgetData.type!,\n title: widgetData.title || t('bigconsole.dashboards.canvas.untitledWidget', 'Untitled Widget'),\n description: widgetData.description,\n // v2.0: Renderer and template support\n renderer: widgetData.renderer,\n template: widgetData.template,\n refreshInterval: widgetData.refreshInterval,\n config: widgetData.config || {},\n position,\n positionX: position.x,\n positionY: position.y,\n positionWidth: position.width,\n positionHeight: position.height,\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId: extendedData.dataSinkId,\n parserId: extendedData.parserId,\n translations: [],\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n };\n\n addWidgetToStore(newWidget);\n selectWidget(newWidget.id);\n },\n [dashboardId, pageId, createWidgetApi, addWidgetToStore, selectWidget, widgetsArray, t]\n );\n\n // Handle drag start from palette\n const handlePaletteDragStart = useCallback((type: WidgetType) => {\n setIsDraggingWidget(true);\n setDragWidgetType(type);\n }, []);\n\n // Handle drag end from palette\n const handlePaletteDragEnd = useCallback(() => {\n setIsDraggingWidget(false);\n setDragWidgetType(null);\n }, []);\n\n // Handle drop on canvas - create immediately with defaults.\n const handleCanvasDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault();\n setIsDraggingWidget(false);\n setDragWidgetType(null);\n\n try {\n const data = e.dataTransfer.getData('application/json');\n if (!data) return;\n\n const parsedData: unknown = JSON.parse(data);\n if (!parsedData || typeof parsedData !== 'object' || !('type' in parsedData)) return;\n const widgetData = parsedData as PaletteDropData;\n const definition = getWidgetDefinition(widgetData.type);\n\n // If the dropped data includes defaultConfig (from WidgetPalette drag serialization),\n // reconstruct a full WidgetDefinition so installed/store widget identity is preserved.\n let droppedDefinition: WidgetDefinition | null = null;\n if (widgetData.defaultConfig && widgetData.category) {\n droppedDefinition = {\n ...definition,\n name: widgetData.name || definition.name,\n category: widgetData.category,\n defaultConfig: widgetData.defaultConfig,\n defaultSize: widgetData.defaultSize || definition.defaultSize,\n };\n }\n\n // Calculate drop position relative to canvas\n const canvasRect = containerRef.current?.getBoundingClientRect();\n if (!canvasRect) return;\n\n // Use utility to calculate grid position from drop coordinates\n const widgetSize = {\n width: definition.defaultSize.width,\n height: definition.defaultSize.height,\n };\n\n const { x: gridX, y: gridY } = calculatePositionFromDrop(\n e.clientX,\n e.clientY,\n canvasRect,\n containerWidth,\n widgetSize,\n { cols: 12 }\n );\n\n const position = {\n x: gridX,\n y: gridY,\n width: widgetSize.width,\n height: widgetSize.height,\n };\n\n void handleAddWidget(\n {\n type: widgetData.type,\n title: widgetData.name || definition.name,\n config: widgetData.defaultConfig || definition.defaultConfig,\n },\n { position, definition: droppedDefinition }\n );\n } catch (error) {\n console.error('Failed to handle widget drop:', error);\n }\n },\n [containerWidth, handleAddWidget]\n );\n\n // Handle drag over to allow drop\n const handleCanvasDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'copy';\n }, []);\n\n // Handle widget select from palette (quick add without drag)\n const handlePaletteWidgetSelect = useCallback(\n (type: WidgetType, paletteDefinition?: WidgetDefinition) => {\n const definition = paletteDefinition || getWidgetDefinition(type);\n handleAddWidget({\n type,\n title: definition.name,\n config: definition.defaultConfig,\n });\n },\n [handleAddWidget]\n );\n\n // Toggle palette\n const togglePalette = useCallback(() => {\n toggleSidebar();\n }, [toggleSidebar]);\n\n const openWidgetPalette = useCallback(() => {\n if (sidebarCollapsed) toggleSidebar();\n window.requestAnimationFrame(() => {\n const searchPlaceholder = t('bigconsole.dashboards.widgetPalette.searchPlaceholder', 'Search widgets...');\n document.querySelector<HTMLInputElement>(`input[placeholder=\"${searchPlaceholder}\"]`)?.focus();\n });\n }, [sidebarCollapsed, toggleSidebar, t]);\n\n // Board-pack export (PNG + PDF) — rasterizes the inner board element.\n const {\n exportPNG,\n exportPDF,\n isExporting: isExportingImage,\n } = useDashboardImageExport({ containerRef: boardRef, name: title || 'dashboard' });\n const handleExportImage = useCallback(() => {\n void exportPNG();\n }, [exportPNG]);\n const handleExportPdf = useCallback(() => {\n void exportPDF();\n }, [exportPDF]);\n\n return (\n <div className=\"flex flex-col h-full min-w-0 overflow-hidden\">\n {/* Toolbar */}\n <DashboardToolbar\n dashboardId={dashboardId}\n title={title}\n onAddWidget={openWidgetPalette}\n onTogglePalette={togglePalette}\n isPaletteOpen={isPaletteOpen}\n onPaste={onPaste}\n onSave={onSave}\n isSaving={isSaving}\n onExport={onExport}\n onPublishTemplate={onPublishTemplate}\n onExportImage={handleExportImage}\n onExportPdf={handleExportPdf}\n isExportingImage={isExportingImage}\n onShare={() => setIsShareDialogOpen(true)}\n />\n\n {widgetCreateError && (\n <div role=\"alert\" className=\"mx-card-padding mt-control-gap flex-shrink-0\">\n {/* The section title has no catalog key — BOFF-6004 translated only the\n message and the Dismiss action — so it stays a literal until a key\n lands in src/translations/en.ts. */}\n <Section\n tone=\"danger\"\n padding=\"compact\"\n headingLevel={3}\n data-testid=\"widget-create-error\"\n title=\"Widget not created\"\n description={widgetCreateError}\n actions={\n <Button variant=\"outline\" size=\"sm\" onClick={() => setWidgetCreateError(null)}>\n {t('bigconsole.dashboards.canvas.dismiss', 'Dismiss')}\n </Button>\n }\n />\n </div>\n )}\n\n {/* Live wall-board freshness banner — renders only when the page has\n auto-refreshing widgets (e.g. the Live Bed Board) and not in edit mode. */}\n <TemplateSetupBanner widgets={widgetsArray} isEditMode={isEditMode} />\n <LiveBoardBanner widgets={widgetsArray} isEditMode={isEditMode} />\n\n {/* Global Filters Bar */}\n {showFilters && <GlobalFiltersBar dashboardId={dashboardId} onFilterChange={handleFilterChange} collapsible />}\n\n {/* Page Tabs - always show in edit mode so user can add first page */}\n {showPageTabs && (isEditMode || pages.length > 0) && (\n <PageTabsManager\n pages={pages}\n activePageId={activePageId || pages[0]?.id}\n editable={isEditMode}\n onPageSelect={onPageChange || (() => {})}\n onPagesReorder={\n onPagesReorder\n ? (pageIds) => {\n // Convert string[] to DashboardPageTab[] for the callback\n const reorderedPages = pageIds.map((id) => pages.find((p) => p.id === id)!).filter(Boolean);\n onPagesReorder(reorderedPages);\n }\n : undefined\n }\n onPageCreate={onPageAdd}\n onPageRename={onPageRename}\n onPageDuplicate={onPageDuplicate}\n onPageDelete={onPageDelete}\n />\n )}\n\n {/* Main content area with palette and canvas.\n When the properties inspector is open we reserve equal space on the\n right so the (fixed, non-modal) panel docks beside the canvas instead\n of overlaying the selected widget. */}\n <div\n data-testid=\"dashboard-canvas-panes\"\n className={`flex flex-1 min-w-0 overflow-hidden ${\n // Only reserve the inspector gutter where there is room for both. The\n // panel is `min(420px, 100vw)` wide, so below `lg` it overlays the\n // board instead — reserving 420px there left the canvas a sliver.\n configPanelOpen ? 'lg:mr-[var(--bc-inspector-width,420px)]' : ''\n }`}\n >\n {/* Widget Palette Sidebar */}\n {isEditMode && (\n <WidgetPalette\n isOpen={isPaletteOpen}\n onClose={togglePalette}\n onWidgetSelect={handlePaletteWidgetSelect}\n onDragStart={handlePaletteDragStart}\n onDragEnd={handlePaletteDragEnd}\n installedWidgets={installedWidgets}\n installedWidgetsLoading={installedWidgetsLoading}\n definitions={widgetCatalog}\n />\n )}\n\n {/*\n The board. This is the one `primary` region on the route — everything\n above it is bare chrome or a compact status strip, so the widgets are\n what the eye lands on. It keeps the recessed canvas surface on purpose:\n the widgets are the raised cards, the board is the well they sit in.\n The drop border is always 2px (transparent when idle) so drag-over\n highlights without nudging the whole board.\n */}\n <div\n ref={containerRef}\n role=\"region\"\n aria-label=\"Dashboard board\"\n data-section-emphasis=\"primary\"\n data-testid=\"dashboard-board\"\n className=\"relative min-w-0 flex-1 overflow-auto border-2 border-transparent bg-bg-sunken motion-safe:transition-[border-color,background-color]\"\n onDrop={handleCanvasDrop}\n onDragOver={handleCanvasDragOver}\n onClick={() => {\n if (isEditMode) {\n selectWidget(null);\n }\n }}\n style={\n isDraggingWidget\n ? {\n borderStyle: 'dashed',\n borderColor: 'var(--color-action-primary-bg)',\n backgroundColor: 'var(--color-bg-accentSubtle)',\n }\n : undefined\n }\n >\n {/* Drop zone indicator */}\n {isDraggingWidget && (\n <div\n style={{\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n padding: 'var(--space-pagePadding)',\n backgroundColor: 'var(--color-bg-surface)',\n borderRadius: 'var(--radius-card)',\n boxShadow: 'var(--shadow-elevation-2)',\n textAlign: 'center',\n pointerEvents: 'none',\n zIndex: 100,\n }}\n >\n <p style={{ fontSize: 'var(--font-size-body-md)', color: 'var(--color-text-primary)', margin: 0 }}>\n {t('bigconsole.dashboards.canvas.dropToAddBefore', 'Drop to add')} <strong>{dragWidgetType}</strong>{' '}\n {t('bigconsole.dashboards.canvas.dropToAddAfter', 'widget')}\n </p>\n </div>\n )}\n\n <div\n ref={boardRef}\n className=\"min-h-full origin-top-left transition-transform\"\n style={{\n transform: `scale(${zoomLevel / 100})`,\n width: `${100 / (zoomLevel / 100)}%`,\n }}\n >\n {widgetsArray.length > 0 ? (\n <CrossFilterProvider\n enabled={enableCrossFilter}\n onCrossFilter={(_filters) => {\n // Cross-filter applied - widget queries can access filter values\n }}\n >\n <DashboardGrid\n widgets={widgetsArray}\n isEditMode={isEditMode}\n width={containerWidth}\n onLayoutChange={handleLayoutChange}\n onWidgetSelect={handleWidgetSelect}\n selectedWidgetId={selectedWidgetId}\n />\n </CrossFilterProvider>\n ) : (\n <div className=\"flex min-h-[min(400px,60vh)] items-center justify-center p-card-padding\">\n <Section\n headingLevel={3}\n className=\"w-full max-w-md\"\n data-testid=\"dashboard-empty-board\"\n headerClassName=\"mb-form-gap\"\n title={t('bigconsole.dashboards.canvas.noWidgetsTitle', 'No widgets yet')}\n description={t(\n 'bigconsole.dashboards.canvas.noWidgetsDescription',\n 'Add widgets to build your dashboard. Click the \"Add Widget\" button to get started.'\n )}\n >\n {isEditMode && (\n <Button onClick={openWidgetPalette}>\n <Plus className=\"size-4\" />\n {t('bigconsole.dashboards.canvas.addWidget', 'Add Widget')}\n </Button>\n )}\n </Section>\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* Properties Panel */}\n <WidgetInspector isOpen={configPanelOpen} onClose={closeConfigPanel} />\n\n {/* Share Dialog */}\n <ShareDialog\n dashboardId={dashboardId}\n dashboardName={title || t('bigconsole.dashboards.canvas.dashboardFallbackName', 'Dashboard')}\n isOpen={isShareDialogOpen}\n onClose={() => setIsShareDialogOpen(false)}\n />\n </div>\n );\n});\n\nexport default DashboardCanvas;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardCanvas.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/DashboardCanvas.tsx"],"sourcesContent":["/**\n * DashboardCanvas Component\n *\n * Main canvas for dashboard with grid layout and widget rendering.\n * Phase 2: Added GlobalFiltersBar and PageTabsManager integration.\n * Phase 3: Added WidgetPalette sidebar for drag-and-drop widget creation.\n */\n\nimport { type FC, memo, useRef, useState, useCallback, useEffect, useMemo } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus } from 'lucide-react';\nimport type { Layout } from 'react-grid-layout';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport type { DashboardVisibility, Widget, WidgetType } from '../../types';\nimport { BIGCONSOLE_PERMISSIONS } from '../../../constants/permissions';\nimport { useDashboardStore, useWidgetStore } from '../../store';\nimport { useDashboardOperations } from '../../hooks/useDashboardOperations';\nimport { useWidgetOperations } from '../../hooks/useWidgetOperations';\nimport { useInstalledWidgets } from '../../hooks/useInstalledWidgets';\nimport { useWidgetCatalog } from '../../hooks/useWidgetCatalog';\nimport { useDashboardImageExport } from '../../hooks/useDashboardImageExport';\nimport { useBigConsole } from '../../context';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Section } from '../common';\nimport { DashboardGrid } from './DashboardGrid';\nimport { DashboardToolbar } from './DashboardToolbar';\nimport { GlobalFiltersBar } from './GlobalFiltersBar';\nimport { LiveBoardBanner } from './LiveBoardBanner';\nimport { TemplateSetupBanner } from './TemplateSetupBanner';\nimport { PageTabsManager, type DashboardPageTab } from './PageTabsManager';\nimport { ShareDialog } from './ShareDialog';\nimport { WidgetPalette } from './WidgetPalette';\nimport { WidgetInspector } from '../builder';\nimport { getWidgetDefinition, type WidgetDefinition } from '../widgets/WidgetRegistry';\n// Phase 2: Cross-filtering context\nimport { CrossFilterProvider } from '../../contexts';\n// Smart widget positioning\nimport { findNextAvailablePosition, calculatePositionFromDrop } from '../../utils/widgetPositioning';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DashboardCanvasProps {\n /** Dashboard ID */\n dashboardId: string;\n /** Page ID (for loading widgets) */\n pageId?: string;\n /** Dashboard title */\n title?: string;\n /** Callback to paste widget */\n onPaste?: () => void;\n /** Callback when dashboard is saved */\n onSave?: () => void;\n /** Whether save is in progress */\n isSaving?: boolean;\n /** Callback to export dashboard */\n onExport?: () => void;\n onPublishTemplate?: () => void;\n /** Dashboard pages for PageTabsManager */\n pages?: DashboardPageTab[];\n /** Active page ID */\n activePageId?: string;\n /** Callback when page changes */\n onPageChange?: (pageId: string) => void;\n /** Callback when pages are reordered */\n onPagesReorder?: (pages: DashboardPageTab[]) => void;\n /** Callback when page is added */\n onPageAdd?: () => void;\n /** Callback when page is renamed */\n onPageRename?: (pageId: string, newName: string) => void;\n /** Callback when page is duplicated */\n onPageDuplicate?: (pageId: string) => void;\n /** Callback when page is deleted */\n onPageDelete?: (pageId: string) => void;\n /** Whether to show global filters bar */\n showFilters?: boolean;\n /** Whether to show page tabs */\n showPageTabs?: boolean;\n /** Whether to enable cross-filtering (widget clicks update filters) */\n enableCrossFilter?: boolean;\n}\n\ninterface WidgetPlacement {\n position: { x: number; y: number; width: number; height: number };\n definition?: WidgetDefinition | null;\n}\n\ninterface PaletteDropData {\n type: WidgetType;\n name?: string;\n category?: WidgetDefinition['category'];\n defaultConfig?: Record<string, unknown>;\n defaultSize?: WidgetDefinition['defaultSize'];\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardCanvas: FC<DashboardCanvasProps> = memo(function DashboardCanvas({\n dashboardId,\n pageId,\n title,\n onPaste,\n onSave,\n isSaving = false,\n onExport,\n onPublishTemplate,\n pages = [],\n activePageId,\n onPageChange,\n onPagesReorder,\n onPageAdd,\n onPageRename,\n onPageDuplicate,\n onPageDelete,\n showFilters = true,\n showPageTabs = true,\n enableCrossFilter = true,\n}) {\n const { t } = useI18n();\n const containerRef = useRef<HTMLDivElement>(null);\n // Inner board element (the grid surface) — rasterized for PNG/PDF board-pack export.\n const boardRef = useRef<HTMLDivElement>(null);\n const [containerWidth, setContainerWidth] = useState(1200);\n const [isShareDialogOpen, setIsShareDialogOpen] = useState(false);\n const [widgetCreateError, setWidgetCreateError] = useState<string | null>(null);\n const [isDraggingWidget, setIsDraggingWidget] = useState(false);\n const [dragWidgetType, setDragWidgetType] = useState<WidgetType | null>(null);\n /**\n * Locally confirmed general-access level, keyed by the dashboard it belongs\n * to. Kept out of the dashboard store on purpose: `setCurrentDashboard` also\n * resets the active page and clears undo history, which changing a share\n * setting must not do.\n */\n const [visibilityOverride, setVisibilityOverride] = useState<{ id: string; value: DashboardVisibility } | null>(null);\n\n // Store state\n const isEditMode = useDashboardStore((state) => state.viewMode === 'edit');\n const currentDashboard = useDashboardStore((state) => state.currentDashboard);\n\n // Mutation only — `skipFetch` keeps this from pulling the whole dashboard list.\n const { updateDashboard } = useDashboardOperations({ skipFetch: true });\n const { hasPermission } = usePermissions();\n const canUpdateDashboard = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_UPDATE);\n\n /**\n * General access for the board currently on screen. Only surfaced when the\n * loaded dashboard IS this one and its `visibility` came back from the\n * server — the share dialog hides the control otherwise rather than showing\n * a default that may not match reality.\n */\n const dashboardVisibility: DashboardVisibility | undefined =\n visibilityOverride?.id === dashboardId\n ? visibilityOverride.value\n : currentDashboard?.id === dashboardId\n ? currentDashboard.visibility\n : undefined;\n\n const handleVisibilityChange = useCallback(\n async (visibility: DashboardVisibility) => {\n // `updateDashboard` swallows Apollo errors and resolves to null, so null\n // is the failure signal the dialog needs in order to report it.\n const updated = await updateDashboard({ id: dashboardId, visibility });\n if (!updated) {\n throw new Error('The dashboard service did not accept the change.');\n }\n setVisibilityOverride({ id: dashboardId, value: updated.visibility ?? visibility });\n },\n [dashboardId, updateDashboard]\n );\n\n // Fetch installed widgets only in edit mode — the palette and add-widget dialog\n // are not usable in read-only view, so skip the request for viewer-only sessions.\n const { workspaceId, apiGatewayUrl, authToken } = useBigConsole();\n const { installedWidgets, loading: installedWidgetsLoading } = useInstalledWidgets({\n workspaceId,\n apiGatewayUrl,\n authToken,\n enabled: isEditMode,\n });\n // Built-in widget catalog from the backend (getWidgetTypes); falls back to the\n // bundled registry while loading or if the supergraph hasn't adopted it yet.\n const { definitions: widgetCatalog } = useWidgetCatalog({\n workspaceId,\n apiGatewayUrl,\n authToken,\n enabled: isEditMode,\n });\n const zoomLevel = useDashboardStore((state) => state.zoomLevel);\n const sidebarCollapsed = useDashboardStore((state) => state.sidebarCollapsed);\n const toggleSidebar = useDashboardStore((state) => state.toggleSidebar);\n const isPaletteOpen = !sidebarCollapsed;\n const setGlobalFilterValues = useDashboardStore((state) => state.setGlobalFilterValues);\n\n // Handle filter changes - update store for widget access\n const handleFilterChange = useCallback(\n (filterValues: Record<string, unknown>) => {\n // Update global filter values in dashboard store\n setGlobalFilterValues(filterValues);\n\n // Widgets can now access filter values via useGlobalFilterValues() hook\n // When backend supports filter parameters, widget queries will include these values\n },\n [setGlobalFilterValues]\n );\n\n // Widget operations hook (connects to backend) - skipFetch since parent page fetches\n const { createWidget: createWidgetApi } = useWidgetOperations(pageId, dashboardId, { skipFetch: true });\n\n const widgetsMap = useWidgetStore((state) => state.widgets);\n // Filter widgets to only show widgets for the current page\n const widgetsArray = useMemo(() => {\n const allWidgets = Array.from(widgetsMap.values());\n if (!pageId) return allWidgets;\n return allWidgets.filter((widget) => widget.pageId === pageId);\n }, [widgetsMap, pageId]);\n const selectedWidgetId = useWidgetStore((state) => state.selectedWidgetId);\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n const addWidgetToStore = useWidgetStore((state) => state.addWidget);\n const updateWidgetPosition = useWidgetStore((state) => state.updateWidgetPosition);\n const configPanelOpen = useWidgetStore((state) => state.configPanelOpen);\n const closeConfigPanel = useWidgetStore((state) => state.closeConfigPanel);\n\n // Keep react-grid-layout's explicit width in sync with the actual canvas.\n // A ResizeObserver is required because opening the docked inspector or widget\n // palette resizes this element without resizing the browser window.\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const updateWidth = () => {\n setContainerWidth(container.offsetWidth);\n };\n\n updateWidth();\n\n if (typeof ResizeObserver !== 'undefined') {\n const observer = new ResizeObserver(updateWidth);\n observer.observe(container);\n return () => observer.disconnect();\n }\n\n window.addEventListener('resize', updateWidth);\n return () => window.removeEventListener('resize', updateWidth);\n }, []);\n\n // Handle layout change\n const handleLayoutChange = useCallback(\n (layout: Layout[]) => {\n layout.forEach((item) => {\n updateWidgetPosition(item.i, {\n x: item.x,\n y: item.y,\n width: item.w,\n height: item.h,\n });\n });\n },\n [updateWidgetPosition]\n );\n\n // Handle widget select\n const handleWidgetSelect = useCallback(\n (widgetId: string) => {\n selectWidget(widgetId);\n },\n [selectWidget]\n );\n\n // Create with defaults, select the result, then configure it in the inspector.\n const handleAddWidget = useCallback(\n async (widgetData: Partial<Widget>, placement?: WidgetPlacement) => {\n setWidgetCreateError(null);\n const definition = placement?.definition || (widgetData.type ? getWidgetDefinition(widgetData.type) : null);\n const widgetSize = {\n width: definition?.defaultSize?.width ?? 4,\n height: definition?.defaultSize?.height ?? 4,\n };\n\n let position: { x: number; y: number; width: number; height: number };\n if (placement?.position) {\n position = placement.position;\n } else {\n // Use smart placement algorithm - horizontal first, then next row\n const { x, y } = findNextAvailablePosition(widgetsArray, widgetSize, { cols: 12 });\n position = {\n x,\n y,\n width: widgetSize.width,\n height: widgetSize.height,\n };\n }\n\n // Preserve legacy binding fields when creating from imported/default data.\n const extendedData = widgetData as Partial<Widget> & { dataSinkId?: string; parserId?: string };\n\n // Try to create via API first\n if (pageId) {\n const widget = await createWidgetApi({\n pageId,\n dashboardId,\n type: widgetData.type!,\n title: widgetData.title || t('bigconsole.dashboards.canvas.untitledWidget', 'Untitled Widget'),\n description: widgetData.description,\n // v2.0: Renderer and template support\n renderer: widgetData.renderer,\n template: widgetData.template,\n config: widgetData.config || {},\n position,\n refreshInterval: widgetData.refreshInterval ?? undefined,\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId: extendedData.dataSinkId,\n parserId: extendedData.parserId,\n });\n\n if (widget) {\n selectWidget(widget.id);\n return;\n }\n\n setWidgetCreateError(\n t(\n 'bigconsole.dashboards.canvas.widgetCreateError',\n 'Could not create the widget. Check the connection and try again.'\n )\n );\n return;\n }\n\n // Fallback to local store if API fails or no pageId\n const newWidget: Widget = {\n id: crypto.randomUUID(),\n dashboardId,\n pageId: pageId || 'local',\n type: widgetData.type!,\n title: widgetData.title || t('bigconsole.dashboards.canvas.untitledWidget', 'Untitled Widget'),\n description: widgetData.description,\n // v2.0: Renderer and template support\n renderer: widgetData.renderer,\n template: widgetData.template,\n refreshInterval: widgetData.refreshInterval,\n config: widgetData.config || {},\n position,\n positionX: position.x,\n positionY: position.y,\n positionWidth: position.width,\n positionHeight: position.height,\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId: extendedData.dataSinkId,\n parserId: extendedData.parserId,\n translations: [],\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n };\n\n addWidgetToStore(newWidget);\n selectWidget(newWidget.id);\n },\n [dashboardId, pageId, createWidgetApi, addWidgetToStore, selectWidget, widgetsArray, t]\n );\n\n // Handle drag start from palette\n const handlePaletteDragStart = useCallback((type: WidgetType) => {\n setIsDraggingWidget(true);\n setDragWidgetType(type);\n }, []);\n\n // Handle drag end from palette\n const handlePaletteDragEnd = useCallback(() => {\n setIsDraggingWidget(false);\n setDragWidgetType(null);\n }, []);\n\n // Handle drop on canvas - create immediately with defaults.\n const handleCanvasDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault();\n setIsDraggingWidget(false);\n setDragWidgetType(null);\n\n try {\n const data = e.dataTransfer.getData('application/json');\n if (!data) return;\n\n const parsedData: unknown = JSON.parse(data);\n if (!parsedData || typeof parsedData !== 'object' || !('type' in parsedData)) return;\n const widgetData = parsedData as PaletteDropData;\n const definition = getWidgetDefinition(widgetData.type);\n\n // If the dropped data includes defaultConfig (from WidgetPalette drag serialization),\n // reconstruct a full WidgetDefinition so installed/store widget identity is preserved.\n let droppedDefinition: WidgetDefinition | null = null;\n if (widgetData.defaultConfig && widgetData.category) {\n droppedDefinition = {\n ...definition,\n name: widgetData.name || definition.name,\n category: widgetData.category,\n defaultConfig: widgetData.defaultConfig,\n defaultSize: widgetData.defaultSize || definition.defaultSize,\n };\n }\n\n // Calculate drop position relative to canvas\n const canvasRect = containerRef.current?.getBoundingClientRect();\n if (!canvasRect) return;\n\n // Use utility to calculate grid position from drop coordinates\n const widgetSize = {\n width: definition.defaultSize.width,\n height: definition.defaultSize.height,\n };\n\n const { x: gridX, y: gridY } = calculatePositionFromDrop(\n e.clientX,\n e.clientY,\n canvasRect,\n containerWidth,\n widgetSize,\n { cols: 12 }\n );\n\n const position = {\n x: gridX,\n y: gridY,\n width: widgetSize.width,\n height: widgetSize.height,\n };\n\n void handleAddWidget(\n {\n type: widgetData.type,\n title: widgetData.name || definition.name,\n config: widgetData.defaultConfig || definition.defaultConfig,\n },\n { position, definition: droppedDefinition }\n );\n } catch (error) {\n console.error('Failed to handle widget drop:', error);\n }\n },\n [containerWidth, handleAddWidget]\n );\n\n // Handle drag over to allow drop\n const handleCanvasDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'copy';\n }, []);\n\n // Handle widget select from palette (quick add without drag)\n const handlePaletteWidgetSelect = useCallback(\n (type: WidgetType, paletteDefinition?: WidgetDefinition) => {\n const definition = paletteDefinition || getWidgetDefinition(type);\n handleAddWidget({\n type,\n title: definition.name,\n config: definition.defaultConfig,\n });\n },\n [handleAddWidget]\n );\n\n // Toggle palette\n const togglePalette = useCallback(() => {\n toggleSidebar();\n }, [toggleSidebar]);\n\n const openWidgetPalette = useCallback(() => {\n if (sidebarCollapsed) toggleSidebar();\n window.requestAnimationFrame(() => {\n const searchPlaceholder = t('bigconsole.dashboards.widgetPalette.searchPlaceholder', 'Search widgets...');\n document.querySelector<HTMLInputElement>(`input[placeholder=\"${searchPlaceholder}\"]`)?.focus();\n });\n }, [sidebarCollapsed, toggleSidebar, t]);\n\n // Board-pack export (PNG + PDF) — rasterizes the inner board element.\n const {\n exportPNG,\n exportPDF,\n isExporting: isExportingImage,\n } = useDashboardImageExport({ containerRef: boardRef, name: title || 'dashboard' });\n const handleExportImage = useCallback(() => {\n void exportPNG();\n }, [exportPNG]);\n const handleExportPdf = useCallback(() => {\n void exportPDF();\n }, [exportPDF]);\n\n return (\n <div className=\"flex flex-col h-full min-w-0 overflow-hidden\">\n {/* Toolbar */}\n <DashboardToolbar\n dashboardId={dashboardId}\n title={title}\n onAddWidget={openWidgetPalette}\n onTogglePalette={togglePalette}\n isPaletteOpen={isPaletteOpen}\n onPaste={onPaste}\n onSave={onSave}\n isSaving={isSaving}\n onExport={onExport}\n onPublishTemplate={onPublishTemplate}\n onExportImage={handleExportImage}\n onExportPdf={handleExportPdf}\n isExportingImage={isExportingImage}\n onShare={() => setIsShareDialogOpen(true)}\n />\n\n {widgetCreateError && (\n <div role=\"alert\" className=\"mx-card-padding mt-control-gap flex-shrink-0\">\n {/* The section title has no catalog key — BOFF-6004 translated only the\n message and the Dismiss action — so it stays a literal until a key\n lands in src/translations/en.ts. */}\n <Section\n tone=\"danger\"\n padding=\"compact\"\n headingLevel={3}\n data-testid=\"widget-create-error\"\n title=\"Widget not created\"\n description={widgetCreateError}\n actions={\n <Button variant=\"outline\" size=\"sm\" onClick={() => setWidgetCreateError(null)}>\n {t('bigconsole.dashboards.canvas.dismiss', 'Dismiss')}\n </Button>\n }\n />\n </div>\n )}\n\n {/* Live wall-board freshness banner — renders only when the page has\n auto-refreshing widgets (e.g. the Live Bed Board) and not in edit mode. */}\n <TemplateSetupBanner widgets={widgetsArray} isEditMode={isEditMode} />\n <LiveBoardBanner widgets={widgetsArray} isEditMode={isEditMode} />\n\n {/* Global Filters Bar */}\n {showFilters && <GlobalFiltersBar dashboardId={dashboardId} onFilterChange={handleFilterChange} collapsible />}\n\n {/* Page Tabs - always show in edit mode so user can add first page */}\n {showPageTabs && (isEditMode || pages.length > 0) && (\n <PageTabsManager\n pages={pages}\n activePageId={activePageId || pages[0]?.id}\n editable={isEditMode}\n onPageSelect={onPageChange || (() => {})}\n onPagesReorder={\n onPagesReorder\n ? (pageIds) => {\n // Convert string[] to DashboardPageTab[] for the callback\n const reorderedPages = pageIds.map((id) => pages.find((p) => p.id === id)!).filter(Boolean);\n onPagesReorder(reorderedPages);\n }\n : undefined\n }\n onPageCreate={onPageAdd}\n onPageRename={onPageRename}\n onPageDuplicate={onPageDuplicate}\n onPageDelete={onPageDelete}\n />\n )}\n\n {/* Main content area with palette and canvas.\n When the properties inspector is open we reserve equal space on the\n right so the (fixed, non-modal) panel docks beside the canvas instead\n of overlaying the selected widget. */}\n <div\n data-testid=\"dashboard-canvas-panes\"\n className={`flex flex-1 min-w-0 overflow-hidden ${\n // Only reserve the inspector gutter where there is room for both. The\n // panel is `min(420px, 100vw)` wide, so below `lg` it overlays the\n // board instead — reserving 420px there left the canvas a sliver.\n configPanelOpen ? 'lg:mr-[var(--bc-inspector-width,420px)]' : ''\n }`}\n >\n {/* Widget Palette Sidebar */}\n {isEditMode && (\n <WidgetPalette\n isOpen={isPaletteOpen}\n onClose={togglePalette}\n onWidgetSelect={handlePaletteWidgetSelect}\n onDragStart={handlePaletteDragStart}\n onDragEnd={handlePaletteDragEnd}\n installedWidgets={installedWidgets}\n installedWidgetsLoading={installedWidgetsLoading}\n definitions={widgetCatalog}\n />\n )}\n\n {/*\n The board. This is the one `primary` region on the route — everything\n above it is bare chrome or a compact status strip, so the widgets are\n what the eye lands on. It keeps the recessed canvas surface on purpose:\n the widgets are the raised cards, the board is the well they sit in.\n The drop border is always 2px (transparent when idle) so drag-over\n highlights without nudging the whole board.\n */}\n <div\n ref={containerRef}\n role=\"region\"\n aria-label=\"Dashboard board\"\n data-section-emphasis=\"primary\"\n data-testid=\"dashboard-board\"\n className=\"relative min-w-0 flex-1 overflow-auto border-2 border-transparent bg-bg-sunken motion-safe:transition-[border-color,background-color]\"\n onDrop={handleCanvasDrop}\n onDragOver={handleCanvasDragOver}\n onClick={() => {\n if (isEditMode) {\n selectWidget(null);\n }\n }}\n style={\n isDraggingWidget\n ? {\n borderStyle: 'dashed',\n borderColor: 'var(--color-action-primary-bg)',\n backgroundColor: 'var(--color-bg-accentSubtle)',\n }\n : undefined\n }\n >\n {/* Drop zone indicator */}\n {isDraggingWidget && (\n <div\n style={{\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n padding: 'var(--space-pagePadding)',\n backgroundColor: 'var(--color-bg-surface)',\n borderRadius: 'var(--radius-card)',\n boxShadow: 'var(--shadow-elevation-2)',\n textAlign: 'center',\n pointerEvents: 'none',\n zIndex: 100,\n }}\n >\n <p style={{ fontSize: 'var(--font-size-body-md)', color: 'var(--color-text-primary)', margin: 0 }}>\n {t('bigconsole.dashboards.canvas.dropToAddBefore', 'Drop to add')} <strong>{dragWidgetType}</strong>{' '}\n {t('bigconsole.dashboards.canvas.dropToAddAfter', 'widget')}\n </p>\n </div>\n )}\n\n <div\n ref={boardRef}\n className=\"min-h-full origin-top-left transition-transform\"\n style={{\n transform: `scale(${zoomLevel / 100})`,\n width: `${100 / (zoomLevel / 100)}%`,\n }}\n >\n {widgetsArray.length > 0 ? (\n <CrossFilterProvider\n enabled={enableCrossFilter}\n onCrossFilter={(_filters) => {\n // Cross-filter applied - widget queries can access filter values\n }}\n >\n <DashboardGrid\n widgets={widgetsArray}\n isEditMode={isEditMode}\n width={containerWidth}\n onLayoutChange={handleLayoutChange}\n onWidgetSelect={handleWidgetSelect}\n selectedWidgetId={selectedWidgetId}\n />\n </CrossFilterProvider>\n ) : (\n <div className=\"flex min-h-[min(400px,60vh)] items-center justify-center p-card-padding\">\n <Section\n headingLevel={3}\n className=\"w-full max-w-md\"\n data-testid=\"dashboard-empty-board\"\n headerClassName=\"mb-form-gap\"\n title={t('bigconsole.dashboards.canvas.noWidgetsTitle', 'No widgets yet')}\n description={t(\n 'bigconsole.dashboards.canvas.noWidgetsDescription',\n 'Add widgets to build your dashboard. Click the \"Add Widget\" button to get started.'\n )}\n >\n {isEditMode && (\n <Button onClick={openWidgetPalette}>\n <Plus className=\"size-4\" />\n {t('bigconsole.dashboards.canvas.addWidget', 'Add Widget')}\n </Button>\n )}\n </Section>\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* Properties Panel */}\n <WidgetInspector isOpen={configPanelOpen} onClose={closeConfigPanel} />\n\n {/* Share Dialog */}\n <ShareDialog\n dashboardId={dashboardId}\n dashboardName={title || t('bigconsole.dashboards.canvas.dashboardFallbackName', 'Dashboard')}\n isOpen={isShareDialogOpen}\n onClose={() => setIsShareDialogOpen(false)}\n visibility={dashboardVisibility}\n onVisibilityChange={handleVisibilityChange}\n canChangeVisibility={canUpdateDashboard}\n />\n </div>\n );\n});\n\nexport default DashboardCanvas;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoGA,IAAa,IAA4C,EAAK,SAAyB,EACrF,gBACA,WACA,UACA,aACA,YACA,eAAW,IACX,cACA,uBACA,WAAQ,EAAE,EACV,kBACA,kBACA,mBACA,eACA,kBACA,qBACA,kBACA,kBAAc,IACd,mBAAe,IACf,wBAAoB,MACnB;CACD,IAAM,EAAE,SAAM,IAAS,EACjB,IAAe,EAAuB,KAAK,EAE3C,IAAW,EAAuB,KAAK,EACvC,CAAC,GAAgB,MAAqB,EAAS,KAAK,EACpD,CAAC,IAAmB,KAAwB,EAAS,GAAM,EAC3D,CAAC,GAAmB,KAAwB,EAAwB,KAAK,EACzE,CAAC,GAAkB,KAAuB,EAAS,GAAM,EACzD,CAAC,IAAgB,KAAqB,EAA4B,KAAK,EAOvE,CAAC,GAAoB,MAAyB,EAA4D,KAAK,EAG/G,IAAa,GAAmB,MAAU,EAAM,aAAa,OAAO,EACpE,IAAmB,GAAmB,MAAU,EAAM,iBAAiB,EAGvE,EAAE,uBAAoB,EAAuB,EAAE,WAAW,IAAM,CAAC,EACjE,EAAE,sBAAkB,IAAgB,EACpC,KAAqB,GAAc,EAAuB,iBAAiB,EAQ3E,KACJ,GAAoB,OAAO,IACvB,EAAmB,QACnB,GAAkB,OAAO,IACvB,EAAiB,aACjB,KAAA,GAEF,KAAyB,EAC7B,OAAO,MAAoC;EAGzC,IAAM,IAAU,MAAM,EAAgB;GAAE,IAAI;GAAa;GAAY,CAAC;AACtE,MAAI,CAAC,EACH,OAAU,MAAM,mDAAmD;AAErE,KAAsB;GAAE,IAAI;GAAa,OAAO,EAAQ,cAAc;GAAY,CAAC;IAErF,CAAC,GAAa,EAAgB,CAC/B,EAIK,EAAE,gBAAa,kBAAe,iBAAc,GAAe,EAC3D,EAAE,sBAAkB,SAAS,OAA4B,EAAoB;EACjF;EACA;EACA;EACA,SAAS;EACV,CAAC,EAGI,EAAE,aAAa,OAAkB,EAAiB;EACtD;EACA;EACA;EACA,SAAS;EACV,CAAC,EACI,IAAY,GAAmB,MAAU,EAAM,UAAU,EACzD,IAAmB,GAAmB,MAAU,EAAM,iBAAiB,EACvE,IAAgB,GAAmB,MAAU,EAAM,cAAc,EACjE,IAAgB,CAAC,GACjB,IAAwB,GAAmB,MAAU,EAAM,sBAAsB,EAGjF,KAAqB,GACxB,MAA0C;AAEzC,IAAsB,EAAa;IAKrC,CAAC,EAAsB,CACxB,EAGK,EAAE,cAAc,MAAoB,EAAoB,GAAQ,GAAa,EAAE,WAAW,IAAM,CAAC,EAEjG,IAAa,GAAgB,MAAU,EAAM,QAAQ,EAErD,IAAe,SAAc;EACjC,IAAM,IAAa,MAAM,KAAK,EAAW,QAAQ,CAAC;AAElD,SADK,IACE,EAAW,QAAQ,MAAW,EAAO,WAAW,EAAO,GAD1C;IAEnB,CAAC,GAAY,EAAO,CAAC,EAClB,KAAmB,GAAgB,MAAU,EAAM,iBAAiB,EACpE,IAAe,GAAgB,MAAU,EAAM,aAAa,EAC5D,IAAmB,GAAgB,MAAU,EAAM,UAAU,EAC7D,IAAuB,GAAgB,MAAU,EAAM,qBAAqB,EAC5E,IAAkB,GAAgB,MAAU,EAAM,gBAAgB,EAClE,KAAmB,GAAgB,MAAU,EAAM,iBAAiB;AAK1E,UAAgB;EACd,IAAM,IAAY,EAAa;AAC/B,MAAI,CAAC,EAAW;EAEhB,IAAM,UAAoB;AACxB,MAAkB,EAAU,YAAY;;AAK1C,MAFA,GAAa,EAET,OAAO,iBAAmB,KAAa;GACzC,IAAM,IAAW,IAAI,eAAe,EAAY;AAEhD,UADA,EAAS,QAAQ,EAAU,QACd,EAAS,YAAY;;AAIpC,SADA,OAAO,iBAAiB,UAAU,EAAY,QACjC,OAAO,oBAAoB,UAAU,EAAY;IAC7D,EAAE,CAAC;CAGN,IAAM,KAAqB,GACxB,MAAqB;AACpB,IAAO,SAAS,MAAS;AACvB,KAAqB,EAAK,GAAG;IAC3B,GAAG,EAAK;IACR,GAAG,EAAK;IACR,OAAO,EAAK;IACZ,QAAQ,EAAK;IACd,CAAC;IACF;IAEJ,CAAC,EAAqB,CACvB,EAGK,KAAqB,GACxB,MAAqB;AACpB,IAAa,EAAS;IAExB,CAAC,EAAa,CACf,EAGK,IAAkB,EACtB,OAAO,GAA6B,MAAgC;AAClE,IAAqB,KAAK;EAC1B,IAAM,IAAa,GAAW,eAAe,EAAW,OAAO,EAAoB,EAAW,KAAK,GAAG,OAChG,IAAa;GACjB,OAAO,GAAY,aAAa,SAAS;GACzC,QAAQ,GAAY,aAAa,UAAU;GAC5C,EAEG;AACJ,MAAI,GAAW,SACb,KAAW,EAAU;OAChB;GAEL,IAAM,EAAE,MAAG,SAAM,GAA0B,GAAc,GAAY,EAAE,MAAM,IAAI,CAAC;AAClF,OAAW;IACT;IACA;IACA,OAAO,EAAW;IAClB,QAAQ,EAAW;IACpB;;EAIH,IAAM,IAAe;AAGrB,MAAI,GAAQ;GACV,IAAM,IAAS,MAAM,EAAgB;IACnC;IACA;IACA,MAAM,EAAW;IACjB,OAAO,EAAW,SAAS,EAAE,+CAA+C,kBAAkB;IAC9F,aAAa,EAAW;IAExB,UAAU,EAAW;IACrB,UAAU,EAAW;IACrB,QAAQ,EAAW,UAAU,EAAE;IAC/B;IACA,iBAAiB,EAAW,mBAAmB,KAAA;IAE/C,YAAY,EAAa;IACzB,UAAU,EAAa;IACxB,CAAC;AAEF,OAAI,GAAQ;AACV,MAAa,EAAO,GAAG;AACvB;;AAGF,KACE,EACE,kDACA,mEACD,CACF;AACD;;EAIF,IAAM,IAAoB;GACxB,IAAI,OAAO,YAAY;GACvB;GACA,QAAQ,KAAU;GAClB,MAAM,EAAW;GACjB,OAAO,EAAW,SAAS,EAAE,+CAA+C,kBAAkB;GAC9F,aAAa,EAAW;GAExB,UAAU,EAAW;GACrB,UAAU,EAAW;GACrB,iBAAiB,EAAW;GAC5B,QAAQ,EAAW,UAAU,EAAE;GAC/B;GACA,WAAW,EAAS;GACpB,WAAW,EAAS;GACpB,eAAe,EAAS;GACxB,gBAAgB,EAAS;GAEzB,YAAY,EAAa;GACzB,UAAU,EAAa;GACvB,cAAc,EAAE;GAChB,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,4BAAW,IAAI,MAAM,EAAC,aAAa;GACpC;AAGD,EADA,EAAiB,EAAU,EAC3B,EAAa,EAAU,GAAG;IAE5B;EAAC;EAAa;EAAQ;EAAiB;EAAkB;EAAc;EAAc;EAAE,CACxF,EAGK,KAAyB,GAAa,MAAqB;AAE/D,EADA,EAAoB,GAAK,EACzB,EAAkB,EAAK;IACtB,EAAE,CAAC,EAGA,KAAuB,QAAkB;AAE7C,EADA,EAAoB,GAAM,EAC1B,EAAkB,KAAK;IACtB,EAAE,CAAC,EAGA,KAAmB,GACtB,MAAuB;AAGtB,EAFA,EAAE,gBAAgB,EAClB,EAAoB,GAAM,EAC1B,EAAkB,KAAK;AAEvB,MAAI;GACF,IAAM,IAAO,EAAE,aAAa,QAAQ,mBAAmB;AACvD,OAAI,CAAC,EAAM;GAEX,IAAM,IAAsB,KAAK,MAAM,EAAK;AAC5C,OAAI,CAAC,KAAc,OAAO,KAAe,YAAY,EAAE,UAAU,GAAa;GAC9E,IAAM,IAAa,GACb,IAAa,EAAoB,EAAW,KAAK,EAInD,IAA6C;AACjD,GAAI,EAAW,iBAAiB,EAAW,aACzC,IAAoB;IAClB,GAAG;IACH,MAAM,EAAW,QAAQ,EAAW;IACpC,UAAU,EAAW;IACrB,eAAe,EAAW;IAC1B,aAAa,EAAW,eAAe,EAAW;IACnD;GAIH,IAAM,IAAa,EAAa,SAAS,uBAAuB;AAChE,OAAI,CAAC,EAAY;GAGjB,IAAM,IAAa;IACjB,OAAO,EAAW,YAAY;IAC9B,QAAQ,EAAW,YAAY;IAChC,EAEK,EAAE,GAAG,GAAO,GAAG,MAAU,GAC7B,EAAE,SACF,EAAE,SACF,GACA,GACA,GACA,EAAE,MAAM,IAAI,CACb,EAEK,IAAW;IACf,GAAG;IACH,GAAG;IACH,OAAO,EAAW;IAClB,QAAQ,EAAW;IACpB;AAEI,KACH;IACE,MAAM,EAAW;IACjB,OAAO,EAAW,QAAQ,EAAW;IACrC,QAAQ,EAAW,iBAAiB,EAAW;IAChD,EACD;IAAE;IAAU,YAAY;IAAmB,CAC5C;WACM,GAAO;AACd,WAAQ,MAAM,iCAAiC,EAAM;;IAGzD,CAAC,GAAgB,EAAgB,CAClC,EAGK,KAAuB,GAAa,MAAuB;AAE/D,EADA,EAAE,gBAAgB,EAClB,EAAE,aAAa,aAAa;IAC3B,EAAE,CAAC,EAGA,KAA4B,GAC/B,GAAkB,MAAyC;EAC1D,IAAM,IAAa,KAAqB,EAAoB,EAAK;AACjE,IAAgB;GACd;GACA,OAAO,EAAW;GAClB,QAAQ,EAAW;GACpB,CAAC;IAEJ,CAAC,EAAgB,CAClB,EAGK,KAAgB,QAAkB;AACtC,KAAe;IACd,CAAC,EAAc,CAAC,EAEb,KAAoB,QAAkB;AAE1C,EADI,KAAkB,GAAe,EACrC,OAAO,4BAA4B;GACjC,IAAM,IAAoB,EAAE,yDAAyD,oBAAoB;AACzG,YAAS,cAAgC,sBAAsB,EAAkB,IAAI,EAAE,OAAO;IAC9F;IACD;EAAC;EAAkB;EAAe;EAAE,CAAC,EAGlC,EACJ,eACA,cACA,aAAa,OACX,EAAwB;EAAE,cAAc;EAAU,MAAM,KAAS;EAAa,CAAC;AAQnF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,IAAD;IACe;IACN;IACP,aAAa;IACb,iBAAiB;IACF;IACN;IACD;IACE;IACA;IACS;IACnB,eArBoB,QAAkB;AACrC,SAAW;OACf,CAAC,GAAU,CAAC;IAoBT,aAnBkB,QAAkB;AACnC,QAAW;OACf,CAAC,EAAU,CAAC;IAkBS;IAClB,eAAe,EAAqB,GAAK;IACzC,CAAA;GAED,KACC,kBAAC,OAAD;IAAK,MAAK;IAAQ,WAAU;cAI1B,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,cAAc;KACd,eAAY;KACZ,OAAM;KACN,aAAa;KACb,SACE,kBAAC,GAAD;MAAQ,SAAQ;MAAU,MAAK;MAAK,eAAe,EAAqB,KAAK;gBAC1E,EAAE,wCAAwC,UAAU;MAC9C,CAAA;KAEX,CAAA;IACE,CAAA;GAKR,kBAAC,IAAD;IAAqB,SAAS;IAA0B;IAAc,CAAA;GACtE,kBAAC,IAAD;IAAiB,SAAS;IAA0B;IAAc,CAAA;GAGjE,MAAe,kBAAC,IAAD;IAA+B;IAAa,gBAAgB;IAAoB,aAAA;IAAc,CAAA;GAG7G,OAAiB,KAAc,EAAM,SAAS,MAC7C,kBAAC,IAAD;IACS;IACP,cAAc,MAAgB,EAAM,IAAI;IACxC,UAAU;IACV,cAAc,aAAuB;IACrC,gBACE,KACK,MAAY;AAGX,OADuB,EAAQ,KAAK,MAAO,EAAM,MAAM,MAAM,EAAE,OAAO,EAAG,CAAE,CAAC,OAAO,QAAQ,CAC7D;QAEhC,KAAA;IAEN,cAAc;IACA;IACG;IACH;IACd,CAAA;GAOJ,kBAAC,OAAD;IACE,eAAY;IACZ,WAAW,uCAIT,IAAkB,4CAA4C;cANlE,CAUG,KACC,kBAAC,IAAD;KACE,QAAQ;KACR,SAAS;KACT,gBAAgB;KAChB,aAAa;KACb,WAAW;KACO;KACO;KACzB,aAAa;KACb,CAAA,EAWJ,kBAAC,OAAD;KACE,KAAK;KACL,MAAK;KACL,cAAW;KACX,yBAAsB;KACtB,eAAY;KACZ,WAAU;KACV,QAAQ;KACR,YAAY;KACZ,eAAe;AACb,MAAI,KACF,EAAa,KAAK;;KAGtB,OACE,IACI;MACE,aAAa;MACb,aAAa;MACb,iBAAiB;MAClB,GACD,KAAA;eArBR,CAyBG,KACC,kBAAC,OAAD;MACE,OAAO;OACL,UAAU;OACV,KAAK;OACL,MAAM;OACN,WAAW;OACX,SAAS;OACT,iBAAiB;OACjB,cAAc;OACd,WAAW;OACX,WAAW;OACX,eAAe;OACf,QAAQ;OACT;gBAED,kBAAC,KAAD;OAAG,OAAO;QAAE,UAAU;QAA4B,OAAO;QAA6B,QAAQ;QAAG;iBAAjG;QACG,EAAE,gDAAgD,cAAc;QAAC;QAAC,kBAAC,UAAD,EAAA,UAAS,IAAwB,CAAA;QAAC;QACpG,EAAE,+CAA+C,SAAS;QACzD;;MACA,CAAA,EAGR,kBAAC,OAAD;MACE,KAAK;MACL,WAAU;MACV,OAAO;OACL,WAAW,SAAS,IAAY,IAAI;OACpC,OAAO,GAAG,OAAO,IAAY,KAAK;OACnC;gBAEA,EAAa,SAAS,IACrB,kBAAC,GAAD;OACE,SAAS;OACT,gBAAgB,MAAa;iBAI7B,kBAAC,IAAD;QACE,SAAS;QACG;QACZ,OAAO;QACP,gBAAgB;QAChB,gBAAgB;QACE;QAClB,CAAA;OACkB,CAAA,GAEtB,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QACE,cAAc;QACd,WAAU;QACV,eAAY;QACZ,iBAAgB;QAChB,OAAO,EAAE,+CAA+C,iBAAiB;QACzE,aAAa,EACX,qDACA,uFACD;kBAEA,KACC,kBAAC,GAAD;SAAQ,SAAS;mBAAjB,CACE,kBAAC,IAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EAAE,0CAA0C,aAAa,CACnD;;QAEH,CAAA;OACN,CAAA;MAEJ,CAAA,CACF;OACF;;GAGN,kBAAC,IAAD;IAAiB,QAAQ;IAAiB,SAAS;IAAoB,CAAA;GAGvE,kBAAC,IAAD;IACe;IACb,eAAe,KAAS,EAAE,sDAAsD,YAAY;IAC5F,QAAQ;IACR,eAAe,EAAqB,GAAM;IAC1C,YAAY;IACZ,oBAAoB;IACpB,qBAAqB;IACrB,CAAA;GACE;;EAER"}
|
|
@@ -2,18 +2,22 @@ import { Section as e, SectionStack as t } from "../common/Section.js";
|
|
|
2
2
|
import "../common/index.js";
|
|
3
3
|
import n from "../../hooks/useExportDashboard.js";
|
|
4
4
|
import "../../hooks/index.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { nativeCopyText as r } from "../../../utils/nativeBridge.js";
|
|
6
|
+
import { memo as i, useCallback as a, useEffect as o, useState as s } from "react";
|
|
7
|
+
import { Check as c, Copy as l, Download as u, FileJson as d } from "lucide-react";
|
|
8
|
+
import { Dialog as f, DialogContent as p, DialogDescription as m, DialogTitle as h, Spinner as g } from "@burdenoff/fe-libs/ui";
|
|
9
|
+
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
9
10
|
//#region src/bigconsole/components/dashboard/ExportDashboardDialog.tsx
|
|
10
|
-
var
|
|
11
|
+
var b = {
|
|
11
12
|
dialogContent: {
|
|
12
|
-
maxWidth: "
|
|
13
|
+
maxWidth: "640px",
|
|
13
14
|
width: "95vw",
|
|
15
|
+
maxHeight: "90dvh",
|
|
14
16
|
padding: 0,
|
|
15
17
|
borderRadius: "12px",
|
|
16
|
-
overflow: "hidden"
|
|
18
|
+
overflow: "hidden",
|
|
19
|
+
display: "flex",
|
|
20
|
+
flexDirection: "column"
|
|
17
21
|
},
|
|
18
22
|
header: {
|
|
19
23
|
display: "flex",
|
|
@@ -46,7 +50,41 @@ var v = {
|
|
|
46
50
|
color: "var(--color-text-secondary)",
|
|
47
51
|
margin: "4px 0 0 0"
|
|
48
52
|
},
|
|
49
|
-
content: {
|
|
53
|
+
content: {
|
|
54
|
+
padding: "20px 24px",
|
|
55
|
+
flex: "1 1 auto",
|
|
56
|
+
minHeight: 0,
|
|
57
|
+
overflowY: "auto"
|
|
58
|
+
},
|
|
59
|
+
previewBlock: {
|
|
60
|
+
margin: 0,
|
|
61
|
+
maxHeight: "260px",
|
|
62
|
+
overflow: "auto",
|
|
63
|
+
padding: "12px",
|
|
64
|
+
borderRadius: "8px",
|
|
65
|
+
border: "1px solid var(--color-border-subtle)",
|
|
66
|
+
backgroundColor: "var(--color-bg-sunken)",
|
|
67
|
+
color: "var(--color-text-primary)",
|
|
68
|
+
fontFamily: "var(--font-family-mono, monospace)",
|
|
69
|
+
fontSize: "12px",
|
|
70
|
+
lineHeight: 1.5,
|
|
71
|
+
whiteSpace: "pre",
|
|
72
|
+
tabSize: 2
|
|
73
|
+
},
|
|
74
|
+
previewAction: {
|
|
75
|
+
display: "inline-flex",
|
|
76
|
+
alignItems: "center",
|
|
77
|
+
gap: "6px",
|
|
78
|
+
minHeight: "var(--size-controlHeight-sm)",
|
|
79
|
+
padding: "4px 10px",
|
|
80
|
+
fontSize: "12px",
|
|
81
|
+
fontWeight: 500,
|
|
82
|
+
borderRadius: "6px",
|
|
83
|
+
border: "1px solid var(--color-border-subtle)",
|
|
84
|
+
backgroundColor: "var(--color-bg-surface)",
|
|
85
|
+
color: "var(--color-text-secondary)",
|
|
86
|
+
cursor: "pointer"
|
|
87
|
+
},
|
|
50
88
|
optionItem: {
|
|
51
89
|
display: "flex",
|
|
52
90
|
alignItems: "flex-start",
|
|
@@ -130,145 +168,218 @@ var v = {
|
|
|
130
168
|
opacity: .5,
|
|
131
169
|
cursor: "not-allowed"
|
|
132
170
|
}
|
|
133
|
-
},
|
|
134
|
-
return /* @__PURE__ */
|
|
171
|
+
}, x = i(function({ checked: e, onChange: t, disabled: n, label: r, description: i }) {
|
|
172
|
+
return /* @__PURE__ */ y("div", {
|
|
135
173
|
style: {
|
|
136
|
-
...
|
|
137
|
-
...e ?
|
|
174
|
+
...b.optionItem,
|
|
175
|
+
...e ? b.optionItemActive : {},
|
|
138
176
|
opacity: n ? .6 : 1,
|
|
139
177
|
cursor: n ? "not-allowed" : "pointer"
|
|
140
178
|
},
|
|
141
179
|
onClick: () => !n && t(!e),
|
|
142
|
-
children: [/* @__PURE__ */
|
|
180
|
+
children: [/* @__PURE__ */ v("div", {
|
|
143
181
|
style: {
|
|
144
|
-
...
|
|
145
|
-
...e ?
|
|
182
|
+
...b.checkbox,
|
|
183
|
+
...e ? b.checkboxChecked : {}
|
|
146
184
|
},
|
|
147
|
-
children: e && /* @__PURE__ */
|
|
185
|
+
children: e && /* @__PURE__ */ v(c, { style: {
|
|
148
186
|
width: "14px",
|
|
149
187
|
height: "14px",
|
|
150
188
|
color: "var(--color-action-primary-text)"
|
|
151
189
|
} })
|
|
152
|
-
}), /* @__PURE__ */
|
|
153
|
-
style:
|
|
154
|
-
children: [/* @__PURE__ */
|
|
155
|
-
style:
|
|
190
|
+
}), /* @__PURE__ */ y("div", {
|
|
191
|
+
style: b.optionText,
|
|
192
|
+
children: [/* @__PURE__ */ v("p", {
|
|
193
|
+
style: b.optionLabel,
|
|
156
194
|
children: r
|
|
157
|
-
}), /* @__PURE__ */
|
|
158
|
-
style:
|
|
195
|
+
}), /* @__PURE__ */ v("p", {
|
|
196
|
+
style: b.optionDescription,
|
|
159
197
|
children: i
|
|
160
198
|
})]
|
|
161
199
|
})]
|
|
162
200
|
});
|
|
163
|
-
}),
|
|
164
|
-
let [
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}, [
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
201
|
+
}), S = i(function({ isOpen: i, dashboardId: S, dashboardName: C, onClose: w, onSuccess: T }) {
|
|
202
|
+
let [E, D] = s(!0), [O, k] = s(!1), [A, j] = s(!0), [M, N] = s(null), [P, F] = s(null), { loading: I, error: L, downloadDashboard: R, clearError: z } = n(), { loading: B, error: V, exportDashboard: H } = n(), U = `${E}|${O}|${A}`, W = M?.key === U ? M.json : null, G = P === U;
|
|
203
|
+
o(() => {
|
|
204
|
+
i && (D(!0), k(!1), j(!0), z());
|
|
205
|
+
}, [i, z]), o(() => {
|
|
206
|
+
if (!i || !S) return;
|
|
207
|
+
let e = !1;
|
|
208
|
+
return H(S, {
|
|
209
|
+
includeDataSourceRefs: E,
|
|
210
|
+
includeWebhookMappings: O,
|
|
211
|
+
prettyPrint: A
|
|
212
|
+
}).then((t) => {
|
|
213
|
+
e || N({
|
|
214
|
+
key: U,
|
|
215
|
+
json: t
|
|
216
|
+
});
|
|
217
|
+
}), () => {
|
|
218
|
+
e = !0;
|
|
219
|
+
};
|
|
220
|
+
}, [
|
|
221
|
+
i,
|
|
222
|
+
S,
|
|
223
|
+
E,
|
|
224
|
+
O,
|
|
225
|
+
A,
|
|
226
|
+
U,
|
|
227
|
+
H
|
|
228
|
+
]);
|
|
229
|
+
let K = a(async () => {
|
|
230
|
+
W && await r(W) && (F(U), window.setTimeout(() => F(null), 2e3));
|
|
231
|
+
}, [W, U]), q = a(async () => {
|
|
232
|
+
S && await R(S, C, {
|
|
233
|
+
includeDataSourceRefs: E,
|
|
234
|
+
includeWebhookMappings: O,
|
|
235
|
+
prettyPrint: A
|
|
236
|
+
}) && (T?.(), w());
|
|
174
237
|
}, [
|
|
175
|
-
|
|
176
|
-
b,
|
|
238
|
+
S,
|
|
177
239
|
C,
|
|
240
|
+
E,
|
|
241
|
+
O,
|
|
242
|
+
A,
|
|
243
|
+
R,
|
|
178
244
|
T,
|
|
179
|
-
|
|
180
|
-
j,
|
|
181
|
-
S,
|
|
182
|
-
x
|
|
245
|
+
w
|
|
183
246
|
]);
|
|
184
|
-
return
|
|
185
|
-
open:
|
|
186
|
-
onOpenChange: (e) => !e && !
|
|
187
|
-
children: /* @__PURE__ */
|
|
188
|
-
style:
|
|
247
|
+
return S ? /* @__PURE__ */ v(f, {
|
|
248
|
+
open: i,
|
|
249
|
+
onOpenChange: (e) => !e && !I && w(),
|
|
250
|
+
children: /* @__PURE__ */ y(p, {
|
|
251
|
+
style: b.dialogContent,
|
|
189
252
|
children: [
|
|
190
|
-
/* @__PURE__ */
|
|
191
|
-
style:
|
|
192
|
-
children: [/* @__PURE__ */
|
|
193
|
-
style:
|
|
194
|
-
children: /* @__PURE__ */
|
|
253
|
+
/* @__PURE__ */ y("div", {
|
|
254
|
+
style: b.header,
|
|
255
|
+
children: [/* @__PURE__ */ v("div", {
|
|
256
|
+
style: b.iconWrapper,
|
|
257
|
+
children: /* @__PURE__ */ v(d, { style: {
|
|
195
258
|
width: "22px",
|
|
196
259
|
height: "22px"
|
|
197
260
|
} })
|
|
198
|
-
}), /* @__PURE__ */
|
|
199
|
-
style:
|
|
200
|
-
children: [/* @__PURE__ */
|
|
261
|
+
}), /* @__PURE__ */ y("div", {
|
|
262
|
+
style: b.headerText,
|
|
263
|
+
children: [/* @__PURE__ */ v(h, {
|
|
201
264
|
asChild: !0,
|
|
202
|
-
children: /* @__PURE__ */
|
|
203
|
-
style:
|
|
265
|
+
children: /* @__PURE__ */ v("h2", {
|
|
266
|
+
style: b.title,
|
|
204
267
|
children: "Export Dashboard"
|
|
205
268
|
})
|
|
206
|
-
}), /* @__PURE__ */
|
|
269
|
+
}), /* @__PURE__ */ v(m, {
|
|
207
270
|
asChild: !0,
|
|
208
|
-
children: /* @__PURE__ */
|
|
209
|
-
style:
|
|
271
|
+
children: /* @__PURE__ */ v("p", {
|
|
272
|
+
style: b.description,
|
|
210
273
|
children: "Export your dashboard configuration to a JSON file."
|
|
211
274
|
})
|
|
212
275
|
})]
|
|
213
276
|
})]
|
|
214
277
|
}),
|
|
215
|
-
/* @__PURE__ */
|
|
216
|
-
style:
|
|
217
|
-
children: /* @__PURE__ */
|
|
218
|
-
|
|
278
|
+
/* @__PURE__ */ v("div", {
|
|
279
|
+
style: b.content,
|
|
280
|
+
children: /* @__PURE__ */ y(t, { children: [
|
|
281
|
+
C && /* @__PURE__ */ v(e, {
|
|
219
282
|
headingLevel: 3,
|
|
220
283
|
emphasis: "muted",
|
|
221
284
|
padding: "compact",
|
|
222
285
|
title: "Exporting",
|
|
223
|
-
description:
|
|
286
|
+
description: C,
|
|
224
287
|
"data-testid": "export-target"
|
|
225
288
|
}),
|
|
226
|
-
/* @__PURE__ */
|
|
289
|
+
/* @__PURE__ */ y(e, {
|
|
227
290
|
headingLevel: 3,
|
|
228
291
|
title: "Export options",
|
|
229
292
|
description: "What to include in the JSON file.",
|
|
230
293
|
"data-testid": "export-options",
|
|
231
294
|
bodyClassName: "flex flex-col gap-form-gap",
|
|
232
295
|
children: [
|
|
233
|
-
/* @__PURE__ */
|
|
234
|
-
checked:
|
|
235
|
-
onChange:
|
|
236
|
-
disabled:
|
|
296
|
+
/* @__PURE__ */ v(x, {
|
|
297
|
+
checked: E,
|
|
298
|
+
onChange: D,
|
|
299
|
+
disabled: I,
|
|
237
300
|
label: "Include Data Source References",
|
|
238
301
|
description: "Store data source names for reference (IDs are not transferred)"
|
|
239
302
|
}),
|
|
240
|
-
/* @__PURE__ */
|
|
241
|
-
checked:
|
|
242
|
-
onChange:
|
|
243
|
-
disabled:
|
|
303
|
+
/* @__PURE__ */ v(x, {
|
|
304
|
+
checked: O,
|
|
305
|
+
onChange: k,
|
|
306
|
+
disabled: I,
|
|
244
307
|
label: "Include Webhook Mappings",
|
|
245
308
|
description: "Export webhook configurations (URLs will need to be reconfigured)"
|
|
246
309
|
}),
|
|
247
|
-
/* @__PURE__ */
|
|
248
|
-
checked:
|
|
249
|
-
onChange:
|
|
250
|
-
disabled:
|
|
310
|
+
/* @__PURE__ */ v(x, {
|
|
311
|
+
checked: A,
|
|
312
|
+
onChange: j,
|
|
313
|
+
disabled: I,
|
|
251
314
|
label: "Format JSON (Pretty Print)",
|
|
252
315
|
description: "Makes the export file human-readable (larger file size)"
|
|
253
316
|
})
|
|
254
317
|
]
|
|
255
318
|
}),
|
|
256
|
-
|
|
319
|
+
/* @__PURE__ */ y(e, {
|
|
320
|
+
headingLevel: 3,
|
|
321
|
+
title: "Preview",
|
|
322
|
+
description: "The exact JSON the download writes. Credentials are never serialized — only data source references.",
|
|
323
|
+
"data-testid": "export-preview",
|
|
324
|
+
actions: W ? /* @__PURE__ */ y("button", {
|
|
325
|
+
type: "button",
|
|
326
|
+
style: b.previewAction,
|
|
327
|
+
onClick: K,
|
|
328
|
+
children: [v(G ? c : l, {
|
|
329
|
+
style: {
|
|
330
|
+
width: "14px",
|
|
331
|
+
height: "14px"
|
|
332
|
+
},
|
|
333
|
+
"aria-hidden": "true"
|
|
334
|
+
}), G ? "Copied" : "Copy"]
|
|
335
|
+
}) : void 0,
|
|
336
|
+
children: [
|
|
337
|
+
B && !W && /* @__PURE__ */ y("div", {
|
|
338
|
+
style: {
|
|
339
|
+
display: "flex",
|
|
340
|
+
alignItems: "center",
|
|
341
|
+
gap: "8px",
|
|
342
|
+
color: "var(--color-text-secondary)"
|
|
343
|
+
},
|
|
344
|
+
children: [/* @__PURE__ */ v(g, { style: {
|
|
345
|
+
width: "16px",
|
|
346
|
+
height: "16px"
|
|
347
|
+
} }), /* @__PURE__ */ v("span", {
|
|
348
|
+
style: { fontSize: "13px" },
|
|
349
|
+
children: "Building the export…"
|
|
350
|
+
})]
|
|
351
|
+
}),
|
|
352
|
+
!B && V && /* @__PURE__ */ y("p", {
|
|
353
|
+
style: {
|
|
354
|
+
margin: 0,
|
|
355
|
+
fontSize: "13px",
|
|
356
|
+
color: "var(--color-status-error-text)"
|
|
357
|
+
},
|
|
358
|
+
children: ["Preview unavailable: ", V.message]
|
|
359
|
+
}),
|
|
360
|
+
W && /* @__PURE__ */ v("pre", {
|
|
361
|
+
style: b.previewBlock,
|
|
362
|
+
"data-testid": "export-preview-json",
|
|
363
|
+
children: W
|
|
364
|
+
})
|
|
365
|
+
]
|
|
366
|
+
}),
|
|
367
|
+
L && /* @__PURE__ */ v(e, {
|
|
257
368
|
headingLevel: 3,
|
|
258
369
|
tone: "danger",
|
|
259
370
|
padding: "compact",
|
|
260
371
|
title: "Export failed",
|
|
261
|
-
description:
|
|
372
|
+
description: L.message,
|
|
262
373
|
"data-testid": "export-error"
|
|
263
374
|
})
|
|
264
375
|
] })
|
|
265
376
|
}),
|
|
266
|
-
/* @__PURE__ */
|
|
267
|
-
style:
|
|
268
|
-
children: [/* @__PURE__ */
|
|
269
|
-
style:
|
|
270
|
-
onClick:
|
|
271
|
-
disabled:
|
|
377
|
+
/* @__PURE__ */ y("div", {
|
|
378
|
+
style: b.footer,
|
|
379
|
+
children: [/* @__PURE__ */ v("button", {
|
|
380
|
+
style: b.cancelButton,
|
|
381
|
+
onClick: w,
|
|
382
|
+
disabled: I,
|
|
272
383
|
onMouseEnter: (e) => {
|
|
273
384
|
e.currentTarget.style.backgroundColor = "var(--color-bg-muted)";
|
|
274
385
|
},
|
|
@@ -276,23 +387,23 @@ var v = {
|
|
|
276
387
|
e.currentTarget.style.backgroundColor = "var(--color-bg-surface)";
|
|
277
388
|
},
|
|
278
389
|
children: "Cancel"
|
|
279
|
-
}), /* @__PURE__ */
|
|
390
|
+
}), /* @__PURE__ */ v("button", {
|
|
280
391
|
style: {
|
|
281
|
-
...
|
|
282
|
-
...
|
|
392
|
+
...b.exportButton,
|
|
393
|
+
...I ? b.exportButtonDisabled : {}
|
|
283
394
|
},
|
|
284
|
-
onClick:
|
|
285
|
-
disabled:
|
|
395
|
+
onClick: q,
|
|
396
|
+
disabled: I,
|
|
286
397
|
onMouseEnter: (e) => {
|
|
287
|
-
|
|
398
|
+
I || (e.currentTarget.style.opacity = "0.9");
|
|
288
399
|
},
|
|
289
400
|
onMouseLeave: (e) => {
|
|
290
|
-
|
|
401
|
+
I || (e.currentTarget.style.opacity = "1");
|
|
291
402
|
},
|
|
292
|
-
children:
|
|
403
|
+
children: I ? /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(g, { style: {
|
|
293
404
|
width: "16px",
|
|
294
405
|
height: "16px"
|
|
295
|
-
} }), "Exporting..."] }) : /* @__PURE__ */ _
|
|
406
|
+
} }), "Exporting..."] }) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(u, { style: {
|
|
296
407
|
width: "16px",
|
|
297
408
|
height: "16px"
|
|
298
409
|
} }), "Download Export"] })
|
|
@@ -303,6 +414,6 @@ var v = {
|
|
|
303
414
|
}) : null;
|
|
304
415
|
});
|
|
305
416
|
//#endregion
|
|
306
|
-
export {
|
|
417
|
+
export { S as default };
|
|
307
418
|
|
|
308
419
|
//# sourceMappingURL=ExportDashboardDialog.js.map
|