@burdenoff/microfe-bigconsole 2026.805.2 → 2026.805.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +109 -113
- package/dist/bigconsole/components/builder/inspector/DataBindingSection.js.map +1 -1
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +115 -113
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js +71 -62
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js +90 -89
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +56 -54
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js +126 -125
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +151 -150
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js +77 -76
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js.map +1 -1
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +97 -96
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +172 -191
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js +75 -83
- package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +151 -154
- package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +181 -190
- package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js.map +1 -1
- package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js +1 -0
- package/dist/bigconsole/components/dashboard/WidgetPalette.js +110 -108
- package/dist/bigconsole/components/dashboard/WidgetPalette.js.map +1 -1
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js +102 -103
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +36 -41
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +38 -37
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +113 -117
- package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +182 -197
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +166 -168
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/DataSinkSelector.js +87 -86
- package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ImportParserDialog.js +196 -211
- package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserSelector.js +130 -128
- package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +66 -63
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewPanel.js +95 -91
- package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js +222 -215
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js.map +1 -1
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +149 -145
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +504 -470
- package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +44 -47
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/ChartConfig.js +143 -142
- package/dist/bigconsole/components/widgets/chart/ChartConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js +101 -88
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js.map +1 -1
- package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +135 -134
- package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js.map +1 -1
- package/dist/bigconsole/components/widgets/form/FormWidget.js +167 -159
- package/dist/bigconsole/components/widgets/form/FormWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +69 -68
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js +64 -72
- package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +60 -59
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/list/ListConfig.js +57 -56
- package/dist/bigconsole/components/widgets/list/ListConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +70 -69
- package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +35 -34
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +20 -19
- package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js.map +1 -1
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +178 -177
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +76 -75
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableConfig.js +183 -168
- package/dist/bigconsole/components/widgets/table/TableConfig.js.map +1 -1
- package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +187 -180
- package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js.map +1 -1
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +40 -38
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js.map +1 -1
- package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js +150 -148
- package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js.map +1 -1
- package/dist/bigconsole/pages/DashboardBuilderPage.js +181 -176
- package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardViewPage.js +196 -192
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +223 -219
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/DataParsersPage.js +167 -170
- package/dist/bigconsole/pages/DataParsersPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkBuilderPage.js +204 -189
- package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +175 -169
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSourcesPage.js +8 -7
- package/dist/bigconsole/pages/DataSourcesPage.js.map +1 -1
- package/dist/bigconsole/pages/InsightsPage.js +238 -208
- package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserBuilderPage.js +464 -412
- package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserViewPage.js +92 -90
- package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineBuilderPage.js +221 -194
- package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineViewPage.js +181 -184
- package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelinesPage.js +128 -127
- package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
- package/dist/bigconsole/pages/SettingsPage.js +177 -172
- package/dist/bigconsole/pages/SettingsPage.js.map +1 -1
- package/dist/bigconsole/pages/WidgetDemoPage.js +121 -83
- package/dist/bigconsole/pages/WidgetDemoPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowViewPage.js +199 -193
- package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowsPage.js +129 -128
- package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +111 -109
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +127 -126
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/CustomRenderer.js +103 -103
- package/dist/bigconsole/renderers/adapters/CustomRenderer.js.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"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 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 { 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 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 || '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('Could not create the widget. Check the connection and try again.');\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 || '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]\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 document.querySelector<HTMLInputElement>('input[placeholder=\"Search widgets...\"]')?.focus();\n });\n }, [sidebarCollapsed, toggleSidebar]);\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\n className=\"flex items-center justify-between gap-3 border-b border-status-error-border bg-status-error-bg px-4 py-2 text-sm text-status-error-text\"\n role=\"alert\"\n >\n <span>{widgetCreateError}</span>\n <button\n type=\"button\"\n className=\"font-medium underline underline-offset-2\"\n onClick={() => setWidgetCreateError(null)}\n >\n Dismiss\n </button>\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 className=\"flex flex-1 min-w-0 overflow-hidden\"\n style={{\n marginRight: configPanelOpen ? 'var(--bc-inspector-width, 420px)' : undefined,\n transition: 'margin-right 0.2s ease',\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 {/* Canvas area */}\n <div\n ref={containerRef}\n className=\"flex-1 min-w-0 overflow-auto bg-bg-sunken\"\n onDrop={handleCanvasDrop}\n onDragOver={handleCanvasDragOver}\n onClick={() => {\n if (isEditMode) {\n selectWidget(null);\n }\n }}\n style={{\n position: 'relative',\n border: isDraggingWidget ? '2px dashed var(--color-action-primary-bg)' : 'none',\n backgroundColor: isDraggingWidget ? 'var(--color-bg-hover)' : undefined,\n transition: 'border-color 0.2s, background-color 0.2s',\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 Drop to add <strong>{dragWidgetType}</strong> 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 flex-col items-center justify-center min-h-[400px] p-8\">\n <div className=\"w-16 h-16 mb-4 flex items-center justify-center rounded-full bg-bg-surface\">\n <svg className=\"w-8 h-8 text-text-secondary\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n </div>\n <h3 className=\"text-lg font-medium text-text-primary mb-2\">No widgets yet</h3>\n <p className=\"text-sm text-text-secondary mb-4 text-center max-w-[300px]\">\n Add widgets to build your dashboard. Click the "Add Widget" button to get started.\n </p>\n {isEditMode && (\n <button\n type=\"button\"\n onClick={openWidgetPalette}\n className=\"\n inline-flex items-center gap-2\n px-4 py-2\n text-sm font-medium\n text-action-primary-fg\n bg-action-primary-bg\n hover:bg-action-primary-bgHover\n rounded-md\n transition-colors\n \"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 4v16m8-8H4\" />\n </svg>\n Add Widget\n </button>\n )}\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 || '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 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 { 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\n className=\"flex items-center justify-between gap-3 border-b border-status-error-border bg-status-error-bg px-4 py-2 text-sm text-status-error-text\"\n role=\"alert\"\n >\n <span>{widgetCreateError}</span>\n <button\n type=\"button\"\n className=\"font-medium underline underline-offset-2\"\n onClick={() => setWidgetCreateError(null)}\n >\n {t('bigconsole.dashboards.canvas.dismiss', 'Dismiss')}\n </button>\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 className=\"flex flex-1 min-w-0 overflow-hidden\"\n style={{\n marginRight: configPanelOpen ? 'var(--bc-inspector-width, 420px)' : undefined,\n transition: 'margin-right 0.2s ease',\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 {/* Canvas area */}\n <div\n ref={containerRef}\n className=\"flex-1 min-w-0 overflow-auto bg-bg-sunken\"\n onDrop={handleCanvasDrop}\n onDragOver={handleCanvasDragOver}\n onClick={() => {\n if (isEditMode) {\n selectWidget(null);\n }\n }}\n style={{\n position: 'relative',\n border: isDraggingWidget ? '2px dashed var(--color-action-primary-bg)' : 'none',\n backgroundColor: isDraggingWidget ? 'var(--color-bg-hover)' : undefined,\n transition: 'border-color 0.2s, background-color 0.2s',\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 flex-col items-center justify-center min-h-[400px] p-8\">\n <div className=\"w-16 h-16 mb-4 flex items-center justify-center rounded-full bg-bg-surface\">\n <svg className=\"w-8 h-8 text-text-secondary\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n </div>\n <h3 className=\"text-lg font-medium text-text-primary mb-2\">\n {t('bigconsole.dashboards.canvas.noWidgetsTitle', 'No widgets yet')}\n </h3>\n <p className=\"text-sm text-text-secondary mb-4 text-center max-w-[300px]\">\n {t(\n 'bigconsole.dashboards.canvas.noWidgetsDescription',\n 'Add widgets to build your dashboard. Click the \"Add Widget\" button to get started.'\n )}\n </p>\n {isEditMode && (\n <button\n type=\"button\"\n onClick={openWidgetPalette}\n className=\"\n inline-flex items-center gap-2\n px-4 py-2\n text-sm font-medium\n text-action-primary-fg\n bg-action-primary-bg\n hover:bg-action-primary-bgHover\n rounded-md\n transition-colors\n \"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 4v16m8-8H4\" />\n </svg>\n {t('bigconsole.dashboards.canvas.addWidget', 'Add Widget')}\n </button>\n )}\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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"}
|
|
@@ -8,172 +8,173 @@ import { memo as i, useCallback as a } from "react";
|
|
|
8
8
|
import { PermissionButton as o } from "@burdenoff/fe-libs/shared/components";
|
|
9
9
|
import { usePermissions as s } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
10
10
|
import { Clipboard as c, Download as l, Eye as u, FileText as d, Image as f, Minus as p, PanelLeft as m, PanelLeftClose as h, Pencil as g, Plus as _, RotateCcw as v, Save as y, Share2 as b, Store as x } from "lucide-react";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { useI18n as S } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
12
|
+
import { Button as C, Spinner as w, Tooltip as T, TooltipContent as E, TooltipProvider as D, TooltipTrigger as O } from "@burdenoff/fe-libs/ui";
|
|
13
|
+
import { Fragment as k, jsx as A, jsxs as j } from "react/jsx-runtime";
|
|
13
14
|
//#region src/bigconsole/components/dashboard/DashboardToolbar.tsx
|
|
14
|
-
var
|
|
15
|
-
let { hasPermission:
|
|
16
|
-
|
|
17
|
-
}, [
|
|
18
|
-
return /* @__PURE__ */
|
|
15
|
+
var M = i(function({ dashboardId: i, title: M, onAddWidget: N, onTogglePalette: P, isPaletteOpen: F = !1, onPaste: I, onSave: L, isSaving: R = !1, onExport: z, onPublishTemplate: B, onExportImage: V, onExportPdf: H, isExportingImage: U = !1, onShare: W }) {
|
|
16
|
+
let { hasPermission: G } = s(), { t: K } = S(), q = t((e) => e.viewMode === "edit"), J = t((e) => e.toggleViewMode), Y = t((e) => e.zoomLevel), X = t((e) => e.setZoomLevel), Z = !!e((e) => e.clipboardWidgetId), Q = a(() => {
|
|
17
|
+
Z && I && I();
|
|
18
|
+
}, [Z, I]);
|
|
19
|
+
return /* @__PURE__ */ A(D, { children: /* @__PURE__ */ j("div", {
|
|
19
20
|
className: "flex flex-wrap items-center justify-between gap-x-4 gap-y-2 px-4 py-2 bg-bg-surface border-b",
|
|
20
21
|
children: [
|
|
21
|
-
/* @__PURE__ */
|
|
22
|
+
/* @__PURE__ */ j("div", {
|
|
22
23
|
className: "flex min-w-0 items-center gap-4",
|
|
23
|
-
children: [
|
|
24
|
+
children: [M && /* @__PURE__ */ A("h1", {
|
|
24
25
|
className: "min-w-0 truncate text-lg font-semibold",
|
|
25
|
-
title:
|
|
26
|
-
children:
|
|
27
|
-
}), i && /* @__PURE__ */
|
|
26
|
+
title: M,
|
|
27
|
+
children: M
|
|
28
|
+
}), i && /* @__PURE__ */ A(r, { dashboardId: i })]
|
|
28
29
|
}),
|
|
29
|
-
/* @__PURE__ */
|
|
30
|
+
/* @__PURE__ */ j("div", {
|
|
30
31
|
className: "flex flex-shrink-0 items-center gap-1",
|
|
31
32
|
children: [
|
|
32
|
-
/* @__PURE__ */
|
|
33
|
+
/* @__PURE__ */ j(T, { children: [/* @__PURE__ */ A(O, {
|
|
33
34
|
asChild: !0,
|
|
34
|
-
children: /* @__PURE__ */
|
|
35
|
+
children: /* @__PURE__ */ A(C, {
|
|
35
36
|
variant: "ghost",
|
|
36
37
|
size: "icon-sm",
|
|
37
|
-
onClick: () =>
|
|
38
|
-
disabled:
|
|
39
|
-
children: /* @__PURE__ */
|
|
38
|
+
onClick: () => X(Math.max(50, Y - 10)),
|
|
39
|
+
disabled: Y <= 50,
|
|
40
|
+
children: /* @__PURE__ */ A(p, { className: "size-4" })
|
|
40
41
|
})
|
|
41
|
-
}), /* @__PURE__ */
|
|
42
|
-
/* @__PURE__ */
|
|
42
|
+
}), /* @__PURE__ */ A(E, { children: K("bigconsole.dashboards.toolbar.zoomOut", "Zoom out") })] }),
|
|
43
|
+
/* @__PURE__ */ j("span", {
|
|
43
44
|
className: "text-sm text-text-secondary w-12 text-center",
|
|
44
|
-
children: [
|
|
45
|
+
children: [Y, "%"]
|
|
45
46
|
}),
|
|
46
|
-
/* @__PURE__ */
|
|
47
|
+
/* @__PURE__ */ j(T, { children: [/* @__PURE__ */ A(O, {
|
|
47
48
|
asChild: !0,
|
|
48
|
-
children: /* @__PURE__ */
|
|
49
|
+
children: /* @__PURE__ */ A(C, {
|
|
49
50
|
variant: "ghost",
|
|
50
51
|
size: "icon-sm",
|
|
51
|
-
onClick: () =>
|
|
52
|
-
disabled:
|
|
53
|
-
children: /* @__PURE__ */
|
|
52
|
+
onClick: () => X(Math.min(200, Y + 10)),
|
|
53
|
+
disabled: Y >= 200,
|
|
54
|
+
children: /* @__PURE__ */ A(_, { className: "size-4" })
|
|
54
55
|
})
|
|
55
|
-
}), /* @__PURE__ */
|
|
56
|
-
/* @__PURE__ */
|
|
56
|
+
}), /* @__PURE__ */ A(E, { children: K("bigconsole.dashboards.toolbar.zoomIn", "Zoom in") })] }),
|
|
57
|
+
/* @__PURE__ */ j(T, { children: [/* @__PURE__ */ A(O, {
|
|
57
58
|
asChild: !0,
|
|
58
|
-
children: /* @__PURE__ */
|
|
59
|
+
children: /* @__PURE__ */ j(C, {
|
|
59
60
|
variant: "ghost",
|
|
60
61
|
size: "sm",
|
|
61
|
-
onClick: () =>
|
|
62
|
-
children: [/* @__PURE__ */
|
|
62
|
+
onClick: () => X(100),
|
|
63
|
+
children: [/* @__PURE__ */ A(v, { className: "size-3.5 mr-1" }), K("bigconsole.dashboards.toolbar.reset", "Reset")]
|
|
63
64
|
})
|
|
64
|
-
}), /* @__PURE__ */
|
|
65
|
+
}), /* @__PURE__ */ A(E, { children: K("bigconsole.dashboards.toolbar.resetZoomTooltip", "Reset zoom to 100%") })] })
|
|
65
66
|
]
|
|
66
67
|
}),
|
|
67
|
-
/* @__PURE__ */
|
|
68
|
+
/* @__PURE__ */ j("div", {
|
|
68
69
|
className: "flex flex-wrap items-center justify-end gap-2",
|
|
69
70
|
children: [
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
q && /* @__PURE__ */ j(k, { children: [
|
|
72
|
+
P && /* @__PURE__ */ j(T, { children: [/* @__PURE__ */ A(O, {
|
|
72
73
|
asChild: !0,
|
|
73
|
-
children: /* @__PURE__ */
|
|
74
|
-
variant:
|
|
74
|
+
children: /* @__PURE__ */ j(C, {
|
|
75
|
+
variant: F ? "secondary" : "outline",
|
|
75
76
|
size: "sm",
|
|
76
|
-
onClick:
|
|
77
|
-
children: [
|
|
77
|
+
onClick: P,
|
|
78
|
+
children: [A(F ? h : m, { className: "size-4" }), K("bigconsole.dashboards.toolbar.widgets", "Widgets")]
|
|
78
79
|
})
|
|
79
|
-
}), /* @__PURE__ */
|
|
80
|
-
/* @__PURE__ */
|
|
80
|
+
}), /* @__PURE__ */ A(E, { children: F ? K("bigconsole.dashboards.toolbar.hidePalette", "Hide widget palette") : K("bigconsole.dashboards.toolbar.showPalette", "Show widget palette for drag & drop") })] }),
|
|
81
|
+
/* @__PURE__ */ j(T, { children: [/* @__PURE__ */ A(O, {
|
|
81
82
|
asChild: !0,
|
|
82
|
-
children: /* @__PURE__ */
|
|
83
|
+
children: /* @__PURE__ */ j(C, {
|
|
83
84
|
variant: "ghost",
|
|
84
85
|
size: "sm",
|
|
85
|
-
onClick:
|
|
86
|
-
disabled: !
|
|
87
|
-
children: [/* @__PURE__ */
|
|
86
|
+
onClick: Q,
|
|
87
|
+
disabled: !Z,
|
|
88
|
+
children: [/* @__PURE__ */ A(c, { className: "size-4" }), K("bigconsole.dashboards.toolbar.paste", "Paste")]
|
|
88
89
|
})
|
|
89
|
-
}), /* @__PURE__ */
|
|
90
|
-
/* @__PURE__ */
|
|
90
|
+
}), /* @__PURE__ */ A(E, { children: K("bigconsole.dashboards.toolbar.pasteTooltip", "Paste widget from clipboard") })] }),
|
|
91
|
+
/* @__PURE__ */ A(o, {
|
|
91
92
|
permission: n.WIDGET_CREATE,
|
|
92
93
|
deniedBehavior: "hide",
|
|
93
|
-
children: /* @__PURE__ */
|
|
94
|
+
children: /* @__PURE__ */ j(C, {
|
|
94
95
|
size: "sm",
|
|
95
|
-
onClick:
|
|
96
|
-
children: [/* @__PURE__ */
|
|
96
|
+
onClick: N,
|
|
97
|
+
children: [/* @__PURE__ */ A(_, { className: "size-4" }), K("bigconsole.dashboards.toolbar.addWidget", "Add Widget")]
|
|
97
98
|
})
|
|
98
99
|
})
|
|
99
100
|
] }),
|
|
100
|
-
/* @__PURE__ */
|
|
101
|
-
variant:
|
|
101
|
+
/* @__PURE__ */ A(C, {
|
|
102
|
+
variant: q ? "secondary" : "outline",
|
|
102
103
|
size: "sm",
|
|
103
|
-
onClick:
|
|
104
|
-
children:
|
|
104
|
+
onClick: J,
|
|
105
|
+
children: q ? /* @__PURE__ */ j(k, { children: [/* @__PURE__ */ A(u, { className: "size-4" }), K("bigconsole.dashboards.toolbar.preview", "Preview")] }) : /* @__PURE__ */ j(k, { children: [/* @__PURE__ */ A(g, { className: "size-4" }), K("bigconsole.dashboards.toolbar.edit", "Edit")] })
|
|
105
106
|
}),
|
|
106
|
-
|
|
107
|
+
L && /* @__PURE__ */ A(C, {
|
|
107
108
|
variant: "outline",
|
|
108
109
|
size: "sm",
|
|
109
|
-
onClick:
|
|
110
|
-
disabled:
|
|
111
|
-
children:
|
|
110
|
+
onClick: L,
|
|
111
|
+
disabled: R,
|
|
112
|
+
children: R ? /* @__PURE__ */ j(k, { children: [/* @__PURE__ */ A(w, { className: "size-4" }), K("bigconsole.dashboards.toolbar.saving", "Saving...")] }) : /* @__PURE__ */ j(k, { children: [/* @__PURE__ */ A(y, { className: "size-4" }), K("bigconsole.dashboards.toolbar.save", "Save")] })
|
|
112
113
|
}),
|
|
113
|
-
|
|
114
|
+
W && /* @__PURE__ */ A(o, {
|
|
114
115
|
permission: n.DASHBOARD_SHARE,
|
|
115
116
|
deniedBehavior: "hide",
|
|
116
|
-
children: /* @__PURE__ */
|
|
117
|
+
children: /* @__PURE__ */ j(T, { children: [/* @__PURE__ */ A(O, {
|
|
117
118
|
asChild: !0,
|
|
118
|
-
children: /* @__PURE__ */
|
|
119
|
+
children: /* @__PURE__ */ j(C, {
|
|
119
120
|
variant: "outline",
|
|
120
121
|
size: "sm",
|
|
121
|
-
onClick:
|
|
122
|
-
children: [/* @__PURE__ */
|
|
122
|
+
onClick: W,
|
|
123
|
+
children: [/* @__PURE__ */ A(b, { className: "size-4" }), K("bigconsole.dashboards.toolbar.share", "Share")]
|
|
123
124
|
})
|
|
124
|
-
}), /* @__PURE__ */
|
|
125
|
+
}), /* @__PURE__ */ A(E, { children: K("bigconsole.dashboards.toolbar.shareTooltip", "Share dashboard with others") })] })
|
|
125
126
|
}),
|
|
126
|
-
|
|
127
|
+
B && /* @__PURE__ */ A(o, {
|
|
127
128
|
permission: n.DASHBOARD_PUBLISH,
|
|
128
129
|
deniedBehavior: "hide",
|
|
129
|
-
children: /* @__PURE__ */
|
|
130
|
+
children: /* @__PURE__ */ j(T, { children: [/* @__PURE__ */ A(O, {
|
|
130
131
|
asChild: !0,
|
|
131
|
-
children: /* @__PURE__ */
|
|
132
|
+
children: /* @__PURE__ */ j(C, {
|
|
132
133
|
variant: "outline",
|
|
133
134
|
size: "sm",
|
|
134
|
-
onClick:
|
|
135
|
-
children: [/* @__PURE__ */
|
|
135
|
+
onClick: B,
|
|
136
|
+
children: [/* @__PURE__ */ A(x, { className: "size-4" }), /* @__PURE__ */ A("span", {
|
|
136
137
|
className: "hidden sm:inline",
|
|
137
|
-
children: "Publish"
|
|
138
|
+
children: K("bigconsole.dashboards.toolbar.publish", "Publish")
|
|
138
139
|
})]
|
|
139
140
|
})
|
|
140
|
-
}), /* @__PURE__ */
|
|
141
|
+
}), /* @__PURE__ */ A(E, { children: K("bigconsole.dashboards.toolbar.publishTooltip", "Publish this dashboard as a reusable template") })] })
|
|
141
142
|
}),
|
|
142
|
-
|
|
143
|
+
z && /* @__PURE__ */ j(T, { children: [/* @__PURE__ */ A(O, {
|
|
143
144
|
asChild: !0,
|
|
144
|
-
children: /* @__PURE__ */
|
|
145
|
+
children: /* @__PURE__ */ j(C, {
|
|
145
146
|
variant: "outline",
|
|
146
147
|
size: "sm",
|
|
147
|
-
onClick:
|
|
148
|
-
children: [/* @__PURE__ */
|
|
148
|
+
onClick: z,
|
|
149
|
+
children: [/* @__PURE__ */ A(l, { className: "size-4" }), K("bigconsole.dashboards.toolbar.export", "Export")]
|
|
149
150
|
})
|
|
150
|
-
}), /* @__PURE__ */
|
|
151
|
-
|
|
151
|
+
}), /* @__PURE__ */ A(E, { children: K("bigconsole.dashboards.toolbar.exportTooltip", "Export dashboard to JSON") })] }),
|
|
152
|
+
V && /* @__PURE__ */ j(T, { children: [/* @__PURE__ */ A(O, {
|
|
152
153
|
asChild: !0,
|
|
153
|
-
children: /* @__PURE__ */
|
|
154
|
+
children: /* @__PURE__ */ A(C, {
|
|
154
155
|
variant: "outline",
|
|
155
156
|
size: "icon-sm",
|
|
156
|
-
onClick:
|
|
157
|
-
disabled:
|
|
158
|
-
children:
|
|
157
|
+
onClick: V,
|
|
158
|
+
disabled: U,
|
|
159
|
+
children: A(U ? w : f, { className: "size-4" })
|
|
159
160
|
})
|
|
160
|
-
}), /* @__PURE__ */
|
|
161
|
-
|
|
161
|
+
}), /* @__PURE__ */ A(E, { children: K("bigconsole.dashboards.toolbar.exportPngTooltip", "Export board as PNG image") })] }),
|
|
162
|
+
H && /* @__PURE__ */ j(T, { children: [/* @__PURE__ */ A(O, {
|
|
162
163
|
asChild: !0,
|
|
163
|
-
children: /* @__PURE__ */
|
|
164
|
+
children: /* @__PURE__ */ A(C, {
|
|
164
165
|
variant: "outline",
|
|
165
166
|
size: "icon-sm",
|
|
166
|
-
onClick:
|
|
167
|
-
disabled:
|
|
168
|
-
children: /* @__PURE__ */
|
|
167
|
+
onClick: H,
|
|
168
|
+
disabled: U,
|
|
169
|
+
children: /* @__PURE__ */ A(d, { className: "size-4" })
|
|
169
170
|
})
|
|
170
|
-
}), /* @__PURE__ */
|
|
171
|
+
}), /* @__PURE__ */ A(E, { children: K("bigconsole.dashboards.toolbar.exportPdfTooltip", "Export board as PDF") })] })
|
|
171
172
|
]
|
|
172
173
|
})
|
|
173
174
|
]
|
|
174
175
|
}) });
|
|
175
176
|
});
|
|
176
177
|
//#endregion
|
|
177
|
-
export {
|
|
178
|
+
export { M as default };
|
|
178
179
|
|
|
179
180
|
//# sourceMappingURL=DashboardToolbar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardToolbar.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/DashboardToolbar.tsx"],"sourcesContent":["/**\n * DashboardToolbar Component\n *\n * Toolbar for dashboard actions and controls.\n * Uses fe-libs shared components for consistent styling.\n * Phase 2: Added SavedViewsDropdown and Share button.\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport {\n Clipboard,\n Download,\n Eye,\n FileText,\n Image as ImageIcon,\n Minus,\n PanelLeft,\n PanelLeftClose,\n Pencil,\n Plus,\n RotateCcw,\n Save,\n Share2,\n Store,\n} from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { useDashboardStore, useWidgetStore } from '../../store';\nimport { BIGCONSOLE_PERMISSIONS } from '../../../constants/permissions';\nimport { SavedViewsDropdown } from './SavedViewsDropdown';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DashboardToolbarProps {\n /** Dashboard ID for saved views */\n dashboardId?: string;\n /** Dashboard title */\n title?: string;\n /** Callback to add widget */\n onAddWidget: () => void;\n /** Callback to toggle widget palette */\n onTogglePalette?: () => void;\n /** Whether palette is open */\n isPaletteOpen?: boolean;\n /** Callback to paste widget */\n onPaste?: () => void;\n /** Callback to save dashboard */\n onSave?: () => void;\n /** Whether save is in progress */\n isSaving?: boolean;\n /** Callback to export dashboard (JSON console definition) */\n onExport?: () => void;\n /** Publish this dashboard as a reusable template. */\n onPublishTemplate?: () => void;\n /** Callback to export the board as a PNG image */\n onExportImage?: () => void;\n /** Callback to export the board as a PDF */\n onExportPdf?: () => void;\n /** Whether an image/PDF export is in progress */\n isExportingImage?: boolean;\n /** Callback to open share dialog */\n onShare?: () => void;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardToolbar: FC<DashboardToolbarProps> = memo(function DashboardToolbar({\n dashboardId,\n title,\n onAddWidget,\n onTogglePalette,\n isPaletteOpen = false,\n onPaste,\n onSave,\n isSaving = false,\n onExport,\n onPublishTemplate,\n onExportImage,\n onExportPdf,\n isExportingImage = false,\n onShare,\n}) {\n const { hasPermission } = usePermissions();\n const isEditMode = useDashboardStore((state) => state.viewMode === 'edit');\n const toggleViewMode = useDashboardStore((state) => state.toggleViewMode);\n const zoomLevel = useDashboardStore((state) => state.zoomLevel);\n const setZoomLevel = useDashboardStore((state) => state.setZoomLevel);\n\n const clipboardWidgetId = useWidgetStore((state) => state.clipboardWidgetId);\n const hasClipboard = !!clipboardWidgetId;\n\n // Handle paste - use provided callback or fallback\n const handlePaste = useCallback(() => {\n if (hasClipboard && onPaste) {\n onPaste();\n }\n }, [hasClipboard, onPaste]);\n\n return (\n <TooltipProvider>\n <div className=\"flex flex-wrap items-center justify-between gap-x-4 gap-y-2 px-4 py-2 bg-bg-surface border-b\">\n {/* Left section */}\n <div className=\"flex min-w-0 items-center gap-4\">\n {title && (\n <h1 className=\"min-w-0 truncate text-lg font-semibold\" title={title}>\n {title}\n </h1>\n )}\n {/* Saved Views Dropdown */}\n {dashboardId && <SavedViewsDropdown dashboardId={dashboardId} />}\n </div>\n\n {/* Center section - Zoom controls */}\n <div className=\"flex flex-shrink-0 items-center gap-1\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setZoomLevel(Math.max(50, zoomLevel - 10))}\n disabled={zoomLevel <= 50}\n >\n <Minus className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Zoom out</TooltipContent>\n </Tooltip>\n\n <span className=\"text-sm text-text-secondary w-12 text-center\">{zoomLevel}%</span>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setZoomLevel(Math.min(200, zoomLevel + 10))}\n disabled={zoomLevel >= 200}\n >\n <Plus className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Zoom in</TooltipContent>\n </Tooltip>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setZoomLevel(100)}>\n <RotateCcw className=\"size-3.5 mr-1\" />\n Reset\n </Button>\n </TooltipTrigger>\n <TooltipContent>Reset zoom to 100%</TooltipContent>\n </Tooltip>\n </div>\n\n {/* Right section - Actions */}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {isEditMode && (\n <>\n {/* Widget Palette Toggle */}\n {onTogglePalette && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant={isPaletteOpen ? 'secondary' : 'outline'} size=\"sm\" onClick={onTogglePalette}>\n {isPaletteOpen ? <PanelLeftClose className=\"size-4\" /> : <PanelLeft className=\"size-4\" />}\n Widgets\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {isPaletteOpen ? 'Hide widget palette' : 'Show widget palette for drag & drop'}\n </TooltipContent>\n </Tooltip>\n )}\n\n {/* Paste */}\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" onClick={handlePaste} disabled={!hasClipboard}>\n <Clipboard className=\"size-4\" />\n Paste\n </Button>\n </TooltipTrigger>\n <TooltipContent>Paste widget from clipboard</TooltipContent>\n </Tooltip>\n\n {/* Add Widget (Dialog) */}\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.WIDGET_CREATE} deniedBehavior=\"hide\">\n <Button size=\"sm\" onClick={onAddWidget}>\n <Plus className=\"size-4\" />\n Add Widget\n </Button>\n </PermissionButton>\n </>\n )}\n\n {/* Edit/View Mode Toggle */}\n <Button variant={isEditMode ? 'secondary' : 'outline'} size=\"sm\" onClick={toggleViewMode}>\n {isEditMode ? (\n <>\n <Eye className=\"size-4\" />\n Preview\n </>\n ) : (\n <>\n <Pencil className=\"size-4\" />\n Edit\n </>\n )}\n </Button>\n\n {/* Save */}\n {onSave && (\n <Button variant=\"outline\" size=\"sm\" onClick={onSave} disabled={isSaving}>\n {isSaving ? (\n <>\n <Spinner className=\"size-4\" />\n Saving...\n </>\n ) : (\n <>\n <Save className=\"size-4\" />\n Save\n </>\n )}\n </Button>\n )}\n\n {/* Share */}\n {onShare && (\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.DASHBOARD_SHARE} deniedBehavior=\"hide\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onShare}>\n <Share2 className=\"size-4\" />\n Share\n </Button>\n </TooltipTrigger>\n <TooltipContent>Share dashboard with others</TooltipContent>\n </Tooltip>\n </PermissionButton>\n )}\n\n {/* Publish as a store template */}\n {onPublishTemplate && (\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.DASHBOARD_PUBLISH} deniedBehavior=\"hide\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onPublishTemplate}>\n <Store className=\"size-4\" />\n <span className=\"hidden sm:inline\">Publish</span>\n </Button>\n </TooltipTrigger>\n <TooltipContent>Publish this dashboard as a reusable template</TooltipContent>\n </Tooltip>\n </PermissionButton>\n )}\n\n {/* Export — JSON definition */}\n {onExport && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onExport}>\n <Download className=\"size-4\" />\n Export\n </Button>\n </TooltipTrigger>\n <TooltipContent>Export dashboard to JSON</TooltipContent>\n </Tooltip>\n )}\n\n {/* Export — board PNG */}\n {onExportImage && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"icon-sm\" onClick={onExportImage} disabled={isExportingImage}>\n {isExportingImage ? <Spinner className=\"size-4\" /> : <ImageIcon className=\"size-4\" />}\n </Button>\n </TooltipTrigger>\n <TooltipContent>Export board as PNG image</TooltipContent>\n </Tooltip>\n )}\n\n {/* Export — board PDF */}\n {onExportPdf && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"icon-sm\" onClick={onExportPdf} disabled={isExportingImage}>\n <FileText className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Export board as PDF</TooltipContent>\n </Tooltip>\n )}\n </div>\n </div>\n </TooltipProvider>\n );\n});\n\nexport default DashboardToolbar;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardToolbar.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/DashboardToolbar.tsx"],"sourcesContent":["/**\n * DashboardToolbar Component\n *\n * Toolbar for dashboard actions and controls.\n * Uses fe-libs shared components for consistent styling.\n * Phase 2: Added SavedViewsDropdown and Share button.\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport {\n Clipboard,\n Download,\n Eye,\n FileText,\n Image as ImageIcon,\n Minus,\n PanelLeft,\n PanelLeftClose,\n Pencil,\n Plus,\n RotateCcw,\n Save,\n Share2,\n Store,\n} from 'lucide-react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { useDashboardStore, useWidgetStore } from '../../store';\nimport { BIGCONSOLE_PERMISSIONS } from '../../../constants/permissions';\nimport { SavedViewsDropdown } from './SavedViewsDropdown';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DashboardToolbarProps {\n /** Dashboard ID for saved views */\n dashboardId?: string;\n /** Dashboard title */\n title?: string;\n /** Callback to add widget */\n onAddWidget: () => void;\n /** Callback to toggle widget palette */\n onTogglePalette?: () => void;\n /** Whether palette is open */\n isPaletteOpen?: boolean;\n /** Callback to paste widget */\n onPaste?: () => void;\n /** Callback to save dashboard */\n onSave?: () => void;\n /** Whether save is in progress */\n isSaving?: boolean;\n /** Callback to export dashboard (JSON console definition) */\n onExport?: () => void;\n /** Publish this dashboard as a reusable template. */\n onPublishTemplate?: () => void;\n /** Callback to export the board as a PNG image */\n onExportImage?: () => void;\n /** Callback to export the board as a PDF */\n onExportPdf?: () => void;\n /** Whether an image/PDF export is in progress */\n isExportingImage?: boolean;\n /** Callback to open share dialog */\n onShare?: () => void;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardToolbar: FC<DashboardToolbarProps> = memo(function DashboardToolbar({\n dashboardId,\n title,\n onAddWidget,\n onTogglePalette,\n isPaletteOpen = false,\n onPaste,\n onSave,\n isSaving = false,\n onExport,\n onPublishTemplate,\n onExportImage,\n onExportPdf,\n isExportingImage = false,\n onShare,\n}) {\n const { hasPermission } = usePermissions();\n const { t } = useI18n();\n const isEditMode = useDashboardStore((state) => state.viewMode === 'edit');\n const toggleViewMode = useDashboardStore((state) => state.toggleViewMode);\n const zoomLevel = useDashboardStore((state) => state.zoomLevel);\n const setZoomLevel = useDashboardStore((state) => state.setZoomLevel);\n\n const clipboardWidgetId = useWidgetStore((state) => state.clipboardWidgetId);\n const hasClipboard = !!clipboardWidgetId;\n\n // Handle paste - use provided callback or fallback\n const handlePaste = useCallback(() => {\n if (hasClipboard && onPaste) {\n onPaste();\n }\n }, [hasClipboard, onPaste]);\n\n return (\n <TooltipProvider>\n <div className=\"flex flex-wrap items-center justify-between gap-x-4 gap-y-2 px-4 py-2 bg-bg-surface border-b\">\n {/* Left section */}\n <div className=\"flex min-w-0 items-center gap-4\">\n {title && (\n <h1 className=\"min-w-0 truncate text-lg font-semibold\" title={title}>\n {title}\n </h1>\n )}\n {/* Saved Views Dropdown */}\n {dashboardId && <SavedViewsDropdown dashboardId={dashboardId} />}\n </div>\n\n {/* Center section - Zoom controls */}\n <div className=\"flex flex-shrink-0 items-center gap-1\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setZoomLevel(Math.max(50, zoomLevel - 10))}\n disabled={zoomLevel <= 50}\n >\n <Minus className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{t('bigconsole.dashboards.toolbar.zoomOut', 'Zoom out')}</TooltipContent>\n </Tooltip>\n\n <span className=\"text-sm text-text-secondary w-12 text-center\">{zoomLevel}%</span>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setZoomLevel(Math.min(200, zoomLevel + 10))}\n disabled={zoomLevel >= 200}\n >\n <Plus className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{t('bigconsole.dashboards.toolbar.zoomIn', 'Zoom in')}</TooltipContent>\n </Tooltip>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setZoomLevel(100)}>\n <RotateCcw className=\"size-3.5 mr-1\" />\n {t('bigconsole.dashboards.toolbar.reset', 'Reset')}\n </Button>\n </TooltipTrigger>\n <TooltipContent>{t('bigconsole.dashboards.toolbar.resetZoomTooltip', 'Reset zoom to 100%')}</TooltipContent>\n </Tooltip>\n </div>\n\n {/* Right section - Actions */}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {isEditMode && (\n <>\n {/* Widget Palette Toggle */}\n {onTogglePalette && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant={isPaletteOpen ? 'secondary' : 'outline'} size=\"sm\" onClick={onTogglePalette}>\n {isPaletteOpen ? <PanelLeftClose className=\"size-4\" /> : <PanelLeft className=\"size-4\" />}\n {t('bigconsole.dashboards.toolbar.widgets', 'Widgets')}\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {isPaletteOpen\n ? t('bigconsole.dashboards.toolbar.hidePalette', 'Hide widget palette')\n : t('bigconsole.dashboards.toolbar.showPalette', 'Show widget palette for drag & drop')}\n </TooltipContent>\n </Tooltip>\n )}\n\n {/* Paste */}\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" onClick={handlePaste} disabled={!hasClipboard}>\n <Clipboard className=\"size-4\" />\n {t('bigconsole.dashboards.toolbar.paste', 'Paste')}\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {t('bigconsole.dashboards.toolbar.pasteTooltip', 'Paste widget from clipboard')}\n </TooltipContent>\n </Tooltip>\n\n {/* Add Widget (Dialog) */}\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.WIDGET_CREATE} deniedBehavior=\"hide\">\n <Button size=\"sm\" onClick={onAddWidget}>\n <Plus className=\"size-4\" />\n {t('bigconsole.dashboards.toolbar.addWidget', 'Add Widget')}\n </Button>\n </PermissionButton>\n </>\n )}\n\n {/* Edit/View Mode Toggle */}\n <Button variant={isEditMode ? 'secondary' : 'outline'} size=\"sm\" onClick={toggleViewMode}>\n {isEditMode ? (\n <>\n <Eye className=\"size-4\" />\n {t('bigconsole.dashboards.toolbar.preview', 'Preview')}\n </>\n ) : (\n <>\n <Pencil className=\"size-4\" />\n {t('bigconsole.dashboards.toolbar.edit', 'Edit')}\n </>\n )}\n </Button>\n\n {/* Save */}\n {onSave && (\n <Button variant=\"outline\" size=\"sm\" onClick={onSave} disabled={isSaving}>\n {isSaving ? (\n <>\n <Spinner className=\"size-4\" />\n {t('bigconsole.dashboards.toolbar.saving', 'Saving...')}\n </>\n ) : (\n <>\n <Save className=\"size-4\" />\n {t('bigconsole.dashboards.toolbar.save', 'Save')}\n </>\n )}\n </Button>\n )}\n\n {/* Share */}\n {onShare && (\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.DASHBOARD_SHARE} deniedBehavior=\"hide\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onShare}>\n <Share2 className=\"size-4\" />\n {t('bigconsole.dashboards.toolbar.share', 'Share')}\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {t('bigconsole.dashboards.toolbar.shareTooltip', 'Share dashboard with others')}\n </TooltipContent>\n </Tooltip>\n </PermissionButton>\n )}\n\n {/* Publish as a store template */}\n {onPublishTemplate && (\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.DASHBOARD_PUBLISH} deniedBehavior=\"hide\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onPublishTemplate}>\n <Store className=\"size-4\" />\n <span className=\"hidden sm:inline\">{t('bigconsole.dashboards.toolbar.publish', 'Publish')}</span>\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {t('bigconsole.dashboards.toolbar.publishTooltip', 'Publish this dashboard as a reusable template')}\n </TooltipContent>\n </Tooltip>\n </PermissionButton>\n )}\n\n {/* Export — JSON definition */}\n {onExport && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onExport}>\n <Download className=\"size-4\" />\n {t('bigconsole.dashboards.toolbar.export', 'Export')}\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {t('bigconsole.dashboards.toolbar.exportTooltip', 'Export dashboard to JSON')}\n </TooltipContent>\n </Tooltip>\n )}\n\n {/* Export — board PNG */}\n {onExportImage && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"icon-sm\" onClick={onExportImage} disabled={isExportingImage}>\n {isExportingImage ? <Spinner className=\"size-4\" /> : <ImageIcon className=\"size-4\" />}\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {t('bigconsole.dashboards.toolbar.exportPngTooltip', 'Export board as PNG image')}\n </TooltipContent>\n </Tooltip>\n )}\n\n {/* Export — board PDF */}\n {onExportPdf && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"icon-sm\" onClick={onExportPdf} disabled={isExportingImage}>\n <FileText className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {t('bigconsole.dashboards.toolbar.exportPdfTooltip', 'Export board as PDF')}\n </TooltipContent>\n </Tooltip>\n )}\n </div>\n </div>\n </TooltipProvider>\n );\n});\n\nexport default DashboardToolbar;\n"],"mappings":"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"}
|