@burdenoff/microfe-bigconsole 2026.805.2 → 2026.812.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.
Files changed (253) hide show
  1. package/dist/bigconsole/components/DrilldownDashboardRenderer.js +118 -106
  2. package/dist/bigconsole/components/DrilldownDashboardRenderer.js.map +1 -1
  3. package/dist/bigconsole/components/DrilldownModal.js +54 -50
  4. package/dist/bigconsole/components/DrilldownModal.js.map +1 -1
  5. package/dist/bigconsole/components/builder/BuilderCanvas.js +2 -2
  6. package/dist/bigconsole/components/builder/BuilderCanvas.js.map +1 -1
  7. package/dist/bigconsole/components/builder/BuilderTopbar.js +3 -3
  8. package/dist/bigconsole/components/builder/BuilderTopbar.js.map +1 -1
  9. package/dist/bigconsole/components/builder/inspector/AdvancedSection.js +24 -25
  10. package/dist/bigconsole/components/builder/inspector/AdvancedSection.js.map +1 -1
  11. package/dist/bigconsole/components/builder/inspector/AppearanceSection.js +40 -29
  12. package/dist/bigconsole/components/builder/inspector/AppearanceSection.js.map +1 -1
  13. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +145 -111
  14. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js.map +1 -1
  15. package/dist/bigconsole/components/builder/inspector/GeneralSection.js +58 -44
  16. package/dist/bigconsole/components/builder/inspector/GeneralSection.js.map +1 -1
  17. package/dist/bigconsole/components/common/Section.js +70 -0
  18. package/dist/bigconsole/components/common/Section.js.map +1 -0
  19. package/dist/bigconsole/components/common/index.js +2 -0
  20. package/dist/bigconsole/components/common/sectionStyles.js +42 -0
  21. package/dist/bigconsole/components/common/sectionStyles.js.map +1 -0
  22. package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +269 -251
  23. package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
  24. package/dist/bigconsole/components/dashboard/DashboardCanvas.js +214 -223
  25. package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
  26. package/dist/bigconsole/components/dashboard/DashboardGrid.js +12 -18
  27. package/dist/bigconsole/components/dashboard/DashboardGrid.js.map +1 -1
  28. package/dist/bigconsole/components/dashboard/DashboardToolbar.js +110 -102
  29. package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
  30. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +81 -68
  31. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
  32. package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js +125 -159
  33. package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js.map +1 -1
  34. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js +147 -144
  35. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js.map +1 -1
  36. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +154 -153
  37. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js.map +1 -1
  38. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js +82 -79
  39. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js.map +1 -1
  40. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +171 -284
  41. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
  42. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +250 -273
  43. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
  44. package/dist/bigconsole/components/dashboard/LiveBoardBanner.js +45 -53
  45. package/dist/bigconsole/components/dashboard/LiveBoardBanner.js.map +1 -1
  46. package/dist/bigconsole/components/dashboard/PageTabsManager/PageTabsManager.js +157 -148
  47. package/dist/bigconsole/components/dashboard/PageTabsManager/PageTabsManager.js.map +1 -1
  48. package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js +75 -83
  49. package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js.map +1 -1
  50. package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +167 -161
  51. package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js.map +1 -1
  52. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +181 -190
  53. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js.map +1 -1
  54. package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.js +101 -95
  55. package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.js.map +1 -1
  56. package/dist/bigconsole/components/dashboard/StoreTemplatePicker.js +20 -18
  57. package/dist/bigconsole/components/dashboard/StoreTemplatePicker.js.map +1 -1
  58. package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js +1 -0
  59. package/dist/bigconsole/components/dashboard/TemplateSetupBanner.js +26 -58
  60. package/dist/bigconsole/components/dashboard/TemplateSetupBanner.js.map +1 -1
  61. package/dist/bigconsole/components/dashboard/WidgetPalette.js +307 -229
  62. package/dist/bigconsole/components/dashboard/WidgetPalette.js.map +1 -1
  63. package/dist/bigconsole/components/datasink/CacheTTLSelector.js +128 -129
  64. package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
  65. package/dist/bigconsole/components/datasink/DataSinkCard.js +78 -79
  66. package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
  67. package/dist/bigconsole/components/datasink/DataSinkDataViewer.js +33 -34
  68. package/dist/bigconsole/components/datasink/DataSinkDataViewer.js.map +1 -1
  69. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +79 -61
  70. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js.map +1 -1
  71. package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +75 -64
  72. package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js.map +1 -1
  73. package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js +1 -1
  74. package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js.map +1 -1
  75. package/dist/bigconsole/components/datasink/DataSinkTableViewer.js +20 -19
  76. package/dist/bigconsole/components/datasink/DataSinkTableViewer.js.map +1 -1
  77. package/dist/bigconsole/components/datasink/ExportDataSinkDialog.js +127 -171
  78. package/dist/bigconsole/components/datasink/ExportDataSinkDialog.js.map +1 -1
  79. package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +113 -117
  80. package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js.map +1 -1
  81. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +312 -307
  82. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
  83. package/dist/bigconsole/components/datasink/LinkedParsersPanel.js +65 -66
  84. package/dist/bigconsole/components/datasink/LinkedParsersPanel.js.map +1 -1
  85. package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js +121 -127
  86. package/dist/bigconsole/components/datasink/LinkedWorkflowsPanel.js.map +1 -1
  87. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +194 -189
  88. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js.map +1 -1
  89. package/dist/bigconsole/components/datasink/SetupStateBadge.js +3 -3
  90. package/dist/bigconsole/components/datasink/SetupStateBadge.js.map +1 -1
  91. package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js +95 -95
  92. package/dist/bigconsole/components/datasink/WorkflowStatusPanel.js.map +1 -1
  93. package/dist/bigconsole/components/parser/DataSinkSelector.js +163 -131
  94. package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
  95. package/dist/bigconsole/components/parser/ExpressionEditor.js +3 -3
  96. package/dist/bigconsole/components/parser/ExpressionEditor.js.map +1 -1
  97. package/dist/bigconsole/components/parser/ImportParserDialog.js +205 -260
  98. package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
  99. package/dist/bigconsole/components/parser/ParserCard.js +133 -114
  100. package/dist/bigconsole/components/parser/ParserCard.js.map +1 -1
  101. package/dist/bigconsole/components/parser/ParserSelector.js +193 -174
  102. package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
  103. package/dist/bigconsole/components/parser/ParserStatusBadge.js +2 -2
  104. package/dist/bigconsole/components/parser/ParserStatusBadge.js.map +1 -1
  105. package/dist/bigconsole/components/parser/ParserTypeBadge.js +4 -4
  106. package/dist/bigconsole/components/parser/ParserTypeBadge.js.map +1 -1
  107. package/dist/bigconsole/components/pipeline/PipelineCard.js +134 -89
  108. package/dist/bigconsole/components/pipeline/PipelineCard.js.map +1 -1
  109. package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js +77 -64
  110. package/dist/bigconsole/components/pipeline/PipelineDeleteDialog.js.map +1 -1
  111. package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js +7 -10
  112. package/dist/bigconsole/components/pipeline/PipelineStatusBadge.js.map +1 -1
  113. package/dist/bigconsole/components/preview/AiPreviewContent.js +31 -32
  114. package/dist/bigconsole/components/preview/AiPreviewContent.js.map +1 -1
  115. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +152 -103
  116. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
  117. package/dist/bigconsole/components/preview/AiPreviewPanel.js +193 -148
  118. package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
  119. package/dist/bigconsole/components/preview/previewShared.js +41 -23
  120. package/dist/bigconsole/components/preview/previewShared.js.map +1 -1
  121. package/dist/bigconsole/components/widgets/ActionConfigDialog.js +405 -364
  122. package/dist/bigconsole/components/widgets/ActionConfigDialog.js.map +1 -1
  123. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +265 -237
  124. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
  125. package/dist/bigconsole/components/widgets/PropertiesPanel.js +764 -821
  126. package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
  127. package/dist/bigconsole/components/widgets/WidgetErrorBoundary.js +7 -5
  128. package/dist/bigconsole/components/widgets/WidgetErrorBoundary.js.map +1 -1
  129. package/dist/bigconsole/components/widgets/WidgetHeader.js +7 -8
  130. package/dist/bigconsole/components/widgets/WidgetHeader.js.map +1 -1
  131. package/dist/bigconsole/components/widgets/WidgetWrapper.js +8 -7
  132. package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
  133. package/dist/bigconsole/components/widgets/adaptive-card/AdaptiveCardWidget.js +1 -1
  134. package/dist/bigconsole/components/widgets/adaptive-card/AdaptiveCardWidget.js.map +1 -1
  135. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +46 -49
  136. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
  137. package/dist/bigconsole/components/widgets/chart/ChartConfig.js +154 -147
  138. package/dist/bigconsole/components/widgets/chart/ChartConfig.js.map +1 -1
  139. package/dist/bigconsole/components/widgets/comments/WidgetComments.js +118 -101
  140. package/dist/bigconsole/components/widgets/comments/WidgetComments.js.map +1 -1
  141. package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +244 -191
  142. package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js.map +1 -1
  143. package/dist/bigconsole/components/widgets/form/FormWidget.js +167 -159
  144. package/dist/bigconsole/components/widgets/form/FormWidget.js.map +1 -1
  145. package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +92 -76
  146. package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js.map +1 -1
  147. package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js +1 -1
  148. package/dist/bigconsole/components/widgets/gauge/GaugeWidget.js.map +1 -1
  149. package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js +89 -86
  150. package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js.map +1 -1
  151. package/dist/bigconsole/components/widgets/iframe-widget/IframeWidget.js +2 -2
  152. package/dist/bigconsole/components/widgets/iframe-widget/IframeWidget.js.map +1 -1
  153. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +64 -63
  154. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
  155. package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js +2 -2
  156. package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js.map +1 -1
  157. package/dist/bigconsole/components/widgets/list/ListConfig.js +65 -58
  158. package/dist/bigconsole/components/widgets/list/ListConfig.js.map +1 -1
  159. package/dist/bigconsole/components/widgets/list/ListItem.js +1 -1
  160. package/dist/bigconsole/components/widgets/list/ListItem.js.map +1 -1
  161. package/dist/bigconsole/components/widgets/map-widget/MapWidget.js +1 -1
  162. package/dist/bigconsole/components/widgets/map-widget/MapWidget.js.map +1 -1
  163. package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +79 -72
  164. package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js.map +1 -1
  165. package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +35 -34
  166. package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js.map +1 -1
  167. package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +20 -19
  168. package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js.map +1 -1
  169. package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +181 -180
  170. package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js.map +1 -1
  171. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +100 -90
  172. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js.map +1 -1
  173. package/dist/bigconsole/components/widgets/retention/RetentionWidget.js +3 -3
  174. package/dist/bigconsole/components/widgets/retention/RetentionWidget.js.map +1 -1
  175. package/dist/bigconsole/components/widgets/states/EmptyState.js +6 -4
  176. package/dist/bigconsole/components/widgets/states/EmptyState.js.map +1 -1
  177. package/dist/bigconsole/components/widgets/states/ErrorState.js +7 -5
  178. package/dist/bigconsole/components/widgets/states/ErrorState.js.map +1 -1
  179. package/dist/bigconsole/components/widgets/states/LoadingState.js +5 -4
  180. package/dist/bigconsole/components/widgets/states/LoadingState.js.map +1 -1
  181. package/dist/bigconsole/components/widgets/table/TableConfig.js +193 -172
  182. package/dist/bigconsole/components/widgets/table/TableConfig.js.map +1 -1
  183. package/dist/bigconsole/components/widgets/table/TableHeader.js +1 -1
  184. package/dist/bigconsole/components/widgets/table/TableHeader.js.map +1 -1
  185. package/dist/bigconsole/components/widgets/table/TableWidget.js +1 -1
  186. package/dist/bigconsole/components/widgets/table/TableWidget.js.map +1 -1
  187. package/dist/bigconsole/components/widgets/text-widget/TextWidget.js +1 -1
  188. package/dist/bigconsole/components/widgets/text-widget/TextWidget.js.map +1 -1
  189. package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js +3 -3
  190. package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js.map +1 -1
  191. package/dist/bigconsole/components/widgets/utils/conditionalFormat.js +2 -2
  192. package/dist/bigconsole/components/widgets/utils/conditionalFormat.js.map +1 -1
  193. package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +283 -249
  194. package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js.map +1 -1
  195. package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js +28 -20
  196. package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js.map +1 -1
  197. package/dist/bigconsole/components/workflow/WorkflowCard.js +92 -77
  198. package/dist/bigconsole/components/workflow/WorkflowCard.js.map +1 -1
  199. package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js +70 -51
  200. package/dist/bigconsole/components/workflow/WorkflowDeleteDialog.js.map +1 -1
  201. package/dist/bigconsole/components/workflow/WorkflowStatusBadge.js +16 -17
  202. package/dist/bigconsole/components/workflow/WorkflowStatusBadge.js.map +1 -1
  203. package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +46 -42
  204. package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js.map +1 -1
  205. package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js +372 -348
  206. package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js.map +1 -1
  207. package/dist/bigconsole/hooks/useModalFocusTrap.js +53 -0
  208. package/dist/bigconsole/hooks/useModalFocusTrap.js.map +1 -0
  209. package/dist/bigconsole/pages/DashboardBuilderPage.js +153 -142
  210. package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
  211. package/dist/bigconsole/pages/DashboardViewPage.js +230 -220
  212. package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
  213. package/dist/bigconsole/pages/DashboardsPage.js +832 -790
  214. package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
  215. package/dist/bigconsole/pages/DataParsersPage.js +249 -304
  216. package/dist/bigconsole/pages/DataParsersPage.js.map +1 -1
  217. package/dist/bigconsole/pages/DataSinkBuilderPage.js +324 -349
  218. package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
  219. package/dist/bigconsole/pages/DataSinkViewPage.js +336 -323
  220. package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
  221. package/dist/bigconsole/pages/DataSinksPage.js +356 -458
  222. package/dist/bigconsole/pages/DataSinksPage.js.map +1 -1
  223. package/dist/bigconsole/pages/DataSourcesPage.js +14 -8
  224. package/dist/bigconsole/pages/DataSourcesPage.js.map +1 -1
  225. package/dist/bigconsole/pages/HomePage.js +186 -193
  226. package/dist/bigconsole/pages/HomePage.js.map +1 -1
  227. package/dist/bigconsole/pages/InsightsPage.js +901 -724
  228. package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
  229. package/dist/bigconsole/pages/ParserBuilderPage.js +769 -675
  230. package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
  231. package/dist/bigconsole/pages/ParserViewPage.js +382 -354
  232. package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
  233. package/dist/bigconsole/pages/PipelineBuilderPage.js +540 -420
  234. package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
  235. package/dist/bigconsole/pages/PipelineViewPage.js +416 -384
  236. package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
  237. package/dist/bigconsole/pages/PipelinesPage.js +260 -309
  238. package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
  239. package/dist/bigconsole/pages/SettingsPage.js +177 -172
  240. package/dist/bigconsole/pages/SettingsPage.js.map +1 -1
  241. package/dist/bigconsole/pages/WidgetDemoPage.js +278 -142
  242. package/dist/bigconsole/pages/WidgetDemoPage.js.map +1 -1
  243. package/dist/bigconsole/pages/WorkflowViewPage.js +382 -366
  244. package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
  245. package/dist/bigconsole/pages/WorkflowsPage.js +202 -316
  246. package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
  247. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +111 -109
  248. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js.map +1 -1
  249. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +127 -126
  250. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js.map +1 -1
  251. package/dist/bigconsole/renderers/adapters/CustomRenderer.js +103 -103
  252. package/dist/bigconsole/renderers/adapters/CustomRenderer.js.map +1 -1
  253. package/package.json +2 -1
@@ -1,52 +1,57 @@
1
1
  import { useWidgetStore as e } from "../store/widgetStore.js";
2
2
  import { useDashboardStore as t } from "../store/dashboardStore.js";
3
3
  import "../store/index.js";
4
- import { useBigConsoleNavigate as n } from "../context/BigConsoleContext.js";
4
+ import { SECTION_FOCUS_RING as n } from "../components/common/sectionStyles.js";
5
+ import { Section as r } from "../components/common/Section.js";
6
+ import "../components/common/index.js";
7
+ import { useBigConsoleNavigate as i } from "../context/BigConsoleContext.js";
5
8
  import "../context/index.js";
6
- import r from "../hooks/useWidgetOperations.js";
7
- import i from "../hooks/useDashboardOperations.js";
8
- import a from "../hooks/usePageOperations.js";
9
+ import a from "../hooks/useWidgetOperations.js";
10
+ import o from "../hooks/useDashboardOperations.js";
11
+ import s from "../hooks/usePageOperations.js";
9
12
  import "../hooks/index.js";
10
- import { BIGCONSOLE_PERMISSIONS as o } from "../../constants/permissions.js";
11
- import { BuilderCanvas as s } from "../components/builder/BuilderCanvas.js";
12
- import { BuilderTopbar as c } from "../components/builder/BuilderTopbar.js";
13
- import { DashboardEditorShell as ee } from "../components/builder/DashboardEditorShell.js";
13
+ import { BIGCONSOLE_PERMISSIONS as c } from "../../constants/permissions.js";
14
+ import { BuilderCanvas as ee } from "../components/builder/BuilderCanvas.js";
15
+ import { BuilderTopbar as te } from "../components/builder/BuilderTopbar.js";
16
+ import { DashboardEditorShell as l } from "../components/builder/DashboardEditorShell.js";
14
17
  import "../components/builder/index.js";
15
- import { findNextAvailablePosition as l } from "../utils/widgetPositioning.js";
16
- import u from "../components/dashboard/DashboardCanvas.js";
17
- import te from "../components/dashboard/ExportDashboardDialog.js";
18
+ import { findNextAvailablePosition as ne } from "../utils/widgetPositioning.js";
19
+ import re from "../components/dashboard/DashboardCanvas.js";
20
+ import ie from "../components/dashboard/ExportDashboardDialog.js";
18
21
  import "../components/dashboard/index.js";
19
- import ne from "../customWidget/CustomWidgetBuilderSheet.js";
22
+ import ae from "../customWidget/CustomWidgetBuilderSheet.js";
20
23
  import "../customWidget/index.js";
21
- import { memo as d, useCallback as f, useEffect as p, useState as m } from "react";
22
- import { useParams as h } from "react-router";
23
- import { AccessDenied as g } from "@burdenoff/fe-libs/shared/components";
24
- import { usePermissions as _ } from "@burdenoff/fe-libs/shared/providers/shell";
25
- import { jsx as v, jsxs as y } from "react/jsx-runtime";
24
+ import { memo as u, useCallback as d, useEffect as f, useState as p } from "react";
25
+ import { useParams as oe } from "react-router";
26
+ import { AccessDenied as se } from "@burdenoff/fe-libs/shared/components";
27
+ import { usePermissions as ce } from "@burdenoff/fe-libs/shared/providers/shell";
28
+ import { useI18n as le } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
29
+ import { Button as ue } from "@burdenoff/fe-libs/ui";
30
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
26
31
  //#region src/bigconsole/pages/DashboardBuilderPage.tsx
27
- function b() {
32
+ function g() {
28
33
  try {
29
34
  return window.__breadcrumbRegistry;
30
35
  } catch {
31
36
  return;
32
37
  }
33
38
  }
34
- function re(e, t) {
35
- let n = b();
39
+ function de(e, t) {
40
+ let n = g();
36
41
  n?.register && n.register(e, t);
37
42
  }
38
- function ie(e) {
39
- let t = b();
43
+ function fe(e) {
44
+ let t = g();
40
45
  t?.unregister && t.unregister(e);
41
46
  }
42
- var x = d(function() {
43
- let { dashboardId: d } = h(), b = n(), { hasPermission: x, isLoading: ae } = _(), oe = x(o.DASHBOARD_READ), se = x(o.DASHBOARD_UPDATE), S = x(o.DASHBOARD_EXPORT), C = x(o.WIDGET_CREATE), [ce, w] = m(!1), [T, E] = m(null), [D, O] = m(!1), [le, k] = m(!1), [A, j] = m(null), { fetchDashboard: M, updateDashboard: N, loading: ue, error: P } = i(), { createWidget: F, updateWidget: I, deleteWidget: L, duplicateWidget: R, batchUpdatePositions: z } = r(void 0, d), { createPage: B, updatePage: V, deletePage: H, reorderPages: U } = a(), W = t((e) => e.setCurrentDashboard), G = t((e) => e.currentDashboard), K = t((e) => e.setViewMode), q = e((e) => e.selectWidget), J = e((e) => e.widgets), Y = e((e) => e.clipboardWidgetId), X = e((e) => e.clipboardAction), Z = e((e) => e.clearClipboard);
44
- p(() => (K("edit"), () => K("view")), [K]), p(() => {
45
- if (!d) {
46
- b("/dashboards");
47
+ var _ = u(function() {
48
+ let { dashboardId: u } = oe(), g = i(), { t: _ } = le(), { hasPermission: v, isLoading: y } = ce(), b = v(c.DASHBOARD_READ), x = v(c.DASHBOARD_UPDATE), pe = v(c.DASHBOARD_EXPORT), me = v(c.WIDGET_CREATE), [S, C] = p(!1), [w, T] = p(null), [E, D] = p(!1), [he, O] = p(!1), [k, A] = p(null), { fetchDashboard: j, updateDashboard: M, loading: N, error: P } = o(), { createWidget: F, updateWidget: I, deleteWidget: L, duplicateWidget: R, batchUpdatePositions: z } = a(void 0, u), { createPage: B, updatePage: V, deletePage: H, reorderPages: U } = s(), W = t((e) => e.setCurrentDashboard), G = t((e) => e.currentDashboard), K = t((e) => e.setViewMode), q = e((e) => e.selectWidget), J = e((e) => e.widgets), Y = e((e) => e.clipboardWidgetId), X = e((e) => e.clipboardAction), Z = e((e) => e.clearClipboard);
49
+ f(() => (K("edit"), () => K("view")), [K]), f(() => {
50
+ if (!u) {
51
+ g("/dashboards");
47
52
  return;
48
53
  }
49
- return M(d).then((e) => {
54
+ return j(u).then((e) => {
50
55
  e ? (W({
51
56
  ...e,
52
57
  title: e.name,
@@ -58,20 +63,20 @@ var x = d(function() {
58
63
  index: t,
59
64
  layoutConfig: e.layoutConfig ?? {}
60
65
  }))
61
- }), e.pages.length > 0 && !A && j(e.pages[0].id), e.name && re(d, e.name)) : b("/dashboards");
66
+ }), e.pages.length > 0 && !k && A(e.pages[0].id), e.name && de(u, e.name)) : g("/dashboards");
62
67
  }), () => {
63
- d && ie(d);
68
+ u && fe(u);
64
69
  };
65
70
  }, [
66
- d,
67
- b,
71
+ u,
72
+ g,
68
73
  W,
69
- M,
70
- A
74
+ j,
75
+ k
71
76
  ]);
72
- let Q = f(async () => {
73
- let e = A ?? G?.pages?.[0]?.id;
74
- if (!Y || !d || !e) return;
77
+ let Q = d(async () => {
78
+ let e = k ?? G?.pages?.[0]?.id;
79
+ if (!Y || !u || !e) return;
75
80
  let t = J.get(Y);
76
81
  if (!t) {
77
82
  Z();
@@ -80,15 +85,15 @@ var x = d(function() {
80
85
  let n = Array.from(J.values()).filter((t) => t.pageId === e), r = {
81
86
  width: t.positionWidth ?? 4,
82
87
  height: t.positionHeight ?? 4
83
- }, { x: i, y: a } = l(n, r, { cols: 12 }), o = {
88
+ }, { x: i, y: a } = ne(n, r, { cols: 12 }), o = {
84
89
  x: i,
85
90
  y: a,
86
91
  width: r.width,
87
92
  height: r.height
88
- }, s = X === "copy" ? `${t.title} (Copy)` : t.title, c = await R(Y, o, s).catch(() => null);
93
+ }, s = X === "copy" ? _("bigconsole.dashboards.builder.actions.copySuffix", "{{title}} (Copy)", { title: t.title }) : t.title, c = await R(Y, o, s).catch(() => null);
89
94
  c ||= await F({
90
95
  pageId: e,
91
- dashboardId: d,
96
+ dashboardId: u,
92
97
  type: t.type,
93
98
  title: s,
94
99
  description: t.description,
@@ -104,17 +109,18 @@ var x = d(function() {
104
109
  }, [
105
110
  Y,
106
111
  X,
107
- d,
108
- A,
112
+ u,
113
+ k,
109
114
  G,
110
115
  J,
111
116
  R,
112
117
  F,
113
118
  L,
114
119
  q,
115
- Z
120
+ Z,
121
+ _
116
122
  ]);
117
- p(() => {
123
+ f(() => {
118
124
  let n = (n) => {
119
125
  if (t.getState().viewMode !== "edit" || n.target?.closest("input, textarea, select, [contenteditable=\"true\"]") || document.querySelector("[data-slot=\"dialog-overlay\"]")) return;
120
126
  let r = e.getState(), i = r.selectedWidgetId;
@@ -130,11 +136,11 @@ var x = d(function() {
130
136
  };
131
137
  return document.addEventListener("keydown", n, !0), () => document.removeEventListener("keydown", n, !0);
132
138
  }, [L, Q]);
133
- let $ = f(() => {
134
- b("/dashboards");
135
- }, [b]), de = f(async () => {
136
- if (d) {
137
- w(!0), E(null);
139
+ let ge = d(() => {
140
+ g("/dashboards");
141
+ }, [g]), _e = d(async () => {
142
+ if (u) {
143
+ C(!0), T(null);
138
144
  try {
139
145
  let t = e.getState(), n = Array.from(t.widgets.values()), r = t.dirtyWidgetIds, i = n.filter((e) => r.has(e.id));
140
146
  if (i.length > 0) {
@@ -158,34 +164,35 @@ var x = d(function() {
158
164
  width: e.positionWidth ?? 4,
159
165
  height: e.positionHeight ?? 4
160
166
  }
161
- }))), G && await N({
162
- id: d,
167
+ }))), G && await M({
168
+ id: u,
163
169
  name: G.title,
164
170
  description: G.description
165
171
  });
166
172
  } catch (e) {
167
- E(e instanceof Error ? e.message : "Failed to save dashboard");
173
+ T(e instanceof Error ? e.message : _("bigconsole.dashboards.builder.errors.saveFailed", "Failed to save dashboard"));
168
174
  } finally {
169
- w(!1);
175
+ C(!1);
170
176
  }
171
177
  }
172
178
  }, [
173
- d,
179
+ u,
174
180
  G,
175
181
  I,
176
182
  z,
177
- N
178
- ]), fe = f(() => {
179
- O(!0);
180
- }, []), pe = f(() => {
181
- O(!1);
182
- }, []), me = f((e) => {
183
- j(e);
184
- }, []), he = f(async () => {
185
- if (!G || !d) return;
183
+ M,
184
+ _
185
+ ]), ve = d(() => {
186
+ D(!0);
187
+ }, []), $ = d(() => {
188
+ D(!1);
189
+ }, []), ye = d((e) => {
190
+ A(e);
191
+ }, []), be = d(async () => {
192
+ if (!G || !u) return;
186
193
  let e = await B({
187
- dashboardId: d,
188
- name: `Page ${G.pages.length + 1}`
194
+ dashboardId: u,
195
+ name: _("bigconsole.dashboards.builder.actions.newPageName", "Page {{count}}", { count: G.pages.length + 1 })
189
196
  });
190
197
  e && (W({
191
198
  ...G,
@@ -195,13 +202,14 @@ var x = d(function() {
195
202
  index: e.order,
196
203
  layoutConfig: e.layoutConfig || {}
197
204
  }]
198
- }), j(e.id));
205
+ }), A(e.id));
199
206
  }, [
200
207
  G,
201
- d,
208
+ u,
202
209
  W,
203
- B
204
- ]), ge = f(async (e, t) => {
210
+ B,
211
+ _
212
+ ]), xe = d(async (e, t) => {
205
213
  G && await V({
206
214
  id: e,
207
215
  name: t
@@ -216,13 +224,13 @@ var x = d(function() {
216
224
  G,
217
225
  W,
218
226
  V
219
- ]), _e = f(async (e) => {
220
- if (!G || !d) return;
227
+ ]), Se = d(async (e) => {
228
+ if (!G || !u) return;
221
229
  let t = G.pages.find((t) => t.id === e);
222
230
  if (!t) return;
223
231
  let n = await B({
224
- dashboardId: d,
225
- name: `${t.title} (Copy)`
232
+ dashboardId: u,
233
+ name: _("bigconsole.dashboards.builder.actions.copySuffix", "{{title}} (Copy)", { title: t.title ?? "" })
226
234
  });
227
235
  n && (W({
228
236
  ...G,
@@ -232,13 +240,14 @@ var x = d(function() {
232
240
  index: n.order,
233
241
  layoutConfig: n.layoutConfig || {}
234
242
  }]
235
- }), j(n.id));
243
+ }), A(n.id));
236
244
  }, [
237
245
  G,
238
- d,
246
+ u,
239
247
  W,
240
- B
241
- ]), ve = f(async (e) => {
248
+ B,
249
+ _
250
+ ]), Ce = d(async (e) => {
242
251
  if (!(!G || G.pages.length <= 1) && await H(e)) {
243
252
  let t = G.pages.filter((t) => t.id !== e).map((e, t) => ({
244
253
  ...e,
@@ -247,115 +256,117 @@ var x = d(function() {
247
256
  W({
248
257
  ...G,
249
258
  pages: t
250
- }), A === e && j(t[0]?.id || null);
259
+ }), k === e && A(t[0]?.id || null);
251
260
  }
252
261
  }, [
253
262
  G,
254
263
  W,
255
- A,
264
+ k,
256
265
  H
257
- ]), ye = f(async (e) => {
258
- !G || !d || (W({
266
+ ]), we = d(async (e) => {
267
+ !G || !u || (W({
259
268
  ...G,
260
269
  pages: e.map((e, t) => ({
261
270
  ...e,
262
271
  index: t
263
272
  }))
264
- }), await U(d, e.map((e) => e.id)));
273
+ }), await U(u, e.map((e) => e.id)));
265
274
  }, [
266
275
  G,
267
- d,
276
+ u,
268
277
  W,
269
278
  U
270
279
  ]);
271
- return ae ? /* @__PURE__ */ v("div", {
280
+ return y ? /* @__PURE__ */ m("div", {
272
281
  className: "flex items-center justify-center h-full",
273
- children: /* @__PURE__ */ v("div", { className: "animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full" })
274
- }) : oe ? P ? /* @__PURE__ */ v("div", {
275
- className: "flex flex-col items-center justify-center h-full p-6",
276
- children: /* @__PURE__ */ y("div", {
277
- className: "bg-status-error-bg border border-status-error-border rounded-lg p-4 max-w-md",
278
- children: [
279
- /* @__PURE__ */ v("h3", {
280
- className: "text-sm font-medium text-status-error-text",
281
- children: "Failed to load dashboard"
282
- }),
283
- /* @__PURE__ */ v("p", {
284
- className: "text-sm text-text-secondary mt-1",
285
- children: P.message
286
- }),
287
- /* @__PURE__ */ v("button", {
288
- type: "button",
289
- onClick: () => b("/dashboards"),
290
- className: "mt-3 px-3 py-1.5 text-sm font-medium text-action-primary-fg bg-action-primary-bg hover:bg-action-primary-bgHover rounded-md",
291
- children: "Back to Dashboards"
292
- })
293
- ]
282
+ children: /* @__PURE__ */ m("div", { className: "animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full" })
283
+ }) : b ? P ? /* @__PURE__ */ m("div", {
284
+ className: "flex h-full flex-col items-center justify-center p-6",
285
+ children: /* @__PURE__ */ m(r, {
286
+ headingLevel: 2,
287
+ tone: "danger",
288
+ title: _("bigconsole.dashboards.builder.errors.loadFailed", "Failed to load dashboard"),
289
+ description: /* @__PURE__ */ m("span", {
290
+ className: "break-words",
291
+ children: P.message
292
+ }),
293
+ className: "w-full max-w-md",
294
+ "data-testid": "dashboard-builder-error",
295
+ actions: /* @__PURE__ */ m(ue, {
296
+ size: "sm",
297
+ onClick: () => g("/dashboards"),
298
+ children: _("bigconsole.dashboards.builder.actions.backToDashboards", "Back to Dashboards")
299
+ })
294
300
  })
295
- }) : ue || !G ? /* @__PURE__ */ v("div", {
301
+ }) : N || !G ? /* @__PURE__ */ h("div", {
296
302
  className: "flex items-center justify-center h-full",
297
- children: /* @__PURE__ */ v("div", { className: "animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full" })
298
- }) : /* @__PURE__ */ y(ee, {
299
- topbar: /* @__PURE__ */ v(c, {
300
- title: G.title || "Untitled dashboard",
301
- onBack: $,
302
- actions: /* @__PURE__ */ y("div", {
303
+ role: "status",
304
+ "aria-live": "polite",
305
+ children: [/* @__PURE__ */ m("div", { className: "animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full" }), /* @__PURE__ */ m("span", {
306
+ className: "sr-only",
307
+ children: "Loading dashboard…"
308
+ })]
309
+ }) : /* @__PURE__ */ h(l, {
310
+ topbar: /* @__PURE__ */ m(te, {
311
+ title: G.title || _("bigconsole.dashboards.builder.header.untitledDashboard", "Untitled dashboard"),
312
+ onBack: ge,
313
+ actions: /* @__PURE__ */ h("div", {
303
314
  className: "flex items-center gap-2",
304
- children: [T ? /* @__PURE__ */ v("span", {
315
+ children: [w ? /* @__PURE__ */ m("span", {
305
316
  className: "text-xs font-medium text-status-error-text",
306
317
  role: "alert",
307
- children: T
308
- }) : null, C ? /* @__PURE__ */ v("button", {
318
+ children: w
319
+ }) : null, me ? /* @__PURE__ */ m("button", {
309
320
  type: "button",
310
321
  "data-testid": "open-custom-widget-builder",
311
- onClick: () => k(!0),
312
- className: "rounded-md border border-border-default px-2 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken focus:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg",
313
- children: "AI widget"
322
+ onClick: () => O(!0),
323
+ className: `inline-flex min-h-control-md items-center rounded-button border border-[var(--color-border-subtle)] px-2 py-1 text-xs font-medium text-text-primary motion-safe:transition-colors hover:bg-bg-sunken ${n}`,
324
+ children: _("bigconsole.dashboards.builder.toolbar.aiWidget", "AI widget")
314
325
  }) : null]
315
326
  })
316
327
  }),
317
328
  children: [
318
- /* @__PURE__ */ v(s, { canvas: /* @__PURE__ */ v(u, {
329
+ /* @__PURE__ */ m(ee, { canvas: /* @__PURE__ */ m(re, {
319
330
  dashboardId: G.id,
320
- pageId: A || G.pages?.[0]?.id,
331
+ pageId: k || G.pages?.[0]?.id,
321
332
  title: G.title,
322
333
  onPaste: Q,
323
- onSave: se ? de : void 0,
324
- isSaving: ce,
325
- onExport: S ? fe : void 0,
334
+ onSave: x ? _e : void 0,
335
+ isSaving: S,
336
+ onExport: pe ? ve : void 0,
326
337
  pages: G.pages.map((e) => ({
327
338
  id: e.id,
328
- name: e.title || `Page ${(e.index ?? 0) + 1}`,
339
+ name: e.title || _("bigconsole.dashboards.builder.actions.newPageName", "Page {{count}}", { count: (e.index ?? 0) + 1 }),
329
340
  order: e.index ?? 0
330
341
  })),
331
- activePageId: A || G.pages?.[0]?.id,
332
- onPageChange: me,
333
- onPageAdd: he,
334
- onPageRename: ge,
335
- onPageDuplicate: _e,
336
- onPageDelete: ve,
337
- onPagesReorder: ye,
342
+ activePageId: k || G.pages?.[0]?.id,
343
+ onPageChange: ye,
344
+ onPageAdd: be,
345
+ onPageRename: xe,
346
+ onPageDuplicate: Se,
347
+ onPageDelete: Ce,
348
+ onPagesReorder: we,
338
349
  showPageTabs: !0
339
350
  }) }),
340
- /* @__PURE__ */ v(te, {
341
- isOpen: D,
351
+ /* @__PURE__ */ m(ie, {
352
+ isOpen: E,
342
353
  dashboardId: G.id,
343
354
  dashboardName: G.title,
344
- onClose: pe
355
+ onClose: $
345
356
  }),
346
- /* @__PURE__ */ v(ne, {
347
- isOpen: le,
348
- onClose: () => k(!1),
357
+ /* @__PURE__ */ m(ae, {
358
+ isOpen: he,
359
+ onClose: () => O(!1),
349
360
  dashboardId: G.id,
350
- pageId: A || G.pages?.[0]?.id || ""
361
+ pageId: k || G.pages?.[0]?.id || ""
351
362
  })
352
363
  ]
353
- }) : /* @__PURE__ */ v(g, {
354
- title: "Access Denied",
355
- description: "You don't have permission to view this dashboard."
364
+ }) : /* @__PURE__ */ m(se, {
365
+ title: _("bigconsole.dashboards.builder.errors.accessDeniedTitle", "Access Denied"),
366
+ description: _("bigconsole.dashboards.builder.errors.accessDeniedDescription", "You don't have permission to view this dashboard.")
356
367
  });
357
368
  });
358
369
  //#endregion
359
- export { x as DashboardBuilderPage, x as default };
370
+ export { _ as DashboardBuilderPage, _ as default };
360
371
 
361
372
  //# sourceMappingURL=DashboardBuilderPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardBuilderPage.js","names":[],"sources":["../../../src/bigconsole/pages/DashboardBuilderPage.tsx"],"sourcesContent":["/**\n * DashboardBuilderPage\n *\n * Full dashboard builder experience with side panels.\n */\n\nimport { type FC, memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\nimport { useBigConsoleNavigate } from '../context';\nimport { DashboardCanvas, ExportDashboardDialog } from '../components/dashboard';\nimport { BuilderCanvas, BuilderTopbar, DashboardEditorShell } from '../components/builder';\nimport { useDashboardStore, useWidgetStore } from '../store';\nimport { useDashboardOperations, useWidgetOperations, usePageOperations } from '../hooks';\nimport { CustomWidgetBuilderSheet } from '../customWidget';\nimport type { Dashboard, DashboardPage } from '../types';\nimport { findNextAvailablePosition } from '../utils/widgetPositioning';\n\n// Breadcrumb name registration helpers (get from shell at call time to avoid timing issues)\nfunction getBreadcrumbRegistry() {\n try {\n return (\n window as unknown as {\n __breadcrumbRegistry?: { register: (id: string, name: string) => void; unregister: (id: string) => void };\n }\n ).__breadcrumbRegistry;\n } catch {\n return undefined;\n }\n}\n\nfunction registerBreadcrumbName(id: string, name: string) {\n const registry = getBreadcrumbRegistry();\n if (registry?.register) {\n registry.register(id, name);\n }\n}\n\nfunction unregisterBreadcrumbName(id: string) {\n const registry = getBreadcrumbRegistry();\n if (registry?.unregister) {\n registry.unregister(id);\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardBuilderPage: FC = memo(function DashboardBuilderPage() {\n const { dashboardId } = useParams<{ dashboardId: string }>();\n const navigate = useBigConsoleNavigate();\n\n // RBAC gating. The builder is the edit surface: a read gate blocks it for\n // callers without dashboard read access, and action gates hide Save/Export and\n // the AI-widget (widget-create) control for callers lacking those permissions.\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_READ);\n const canUpdate = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_UPDATE);\n const canExport = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_EXPORT);\n const canCreateWidget = hasPermission(BIGCONSOLE_PERMISSIONS.WIDGET_CREATE);\n\n const [isSaving, setIsSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);\n // BOFF-5532: AI custom-widget authoring/review surface.\n const [isCustomWidgetBuilderOpen, setIsCustomWidgetBuilderOpen] = useState(false);\n const [activePageId, setActivePageId] = useState<string | null>(null);\n\n // Dashboard operations from GraphQL\n const {\n fetchDashboard,\n updateDashboard,\n loading: dashboardLoading,\n error: dashboardError,\n } = useDashboardOperations();\n\n // Widget operations from GraphQL\n const {\n createWidget: createWidgetMutation,\n updateWidget: updateWidgetMutation,\n deleteWidget: deleteWidgetMutation,\n duplicateWidget: duplicateWidgetMutation,\n batchUpdatePositions,\n } = useWidgetOperations(undefined, dashboardId);\n\n // Page operations from GraphQL\n const {\n createPage: createPageMutation,\n updatePage: updatePageMutation,\n deletePage: deletePageMutation,\n reorderPages: reorderPagesMutation,\n } = usePageOperations();\n\n // Store actions\n const setCurrentDashboard = useDashboardStore((state) => state.setCurrentDashboard);\n const currentDashboard = useDashboardStore((state) => state.currentDashboard);\n const setViewMode = useDashboardStore((state) => state.setViewMode);\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n const widgets = useWidgetStore((state) => state.widgets);\n const clipboardWidgetId = useWidgetStore((state) => state.clipboardWidgetId);\n const clipboardAction = useWidgetStore((state) => state.clipboardAction);\n const clearClipboard = useWidgetStore((state) => state.clearClipboard);\n\n // Set edit mode on mount\n useEffect(() => {\n setViewMode('edit');\n return () => setViewMode('view');\n }, [setViewMode]);\n\n // Load dashboard data from API\n useEffect(() => {\n if (!dashboardId) {\n navigate('/dashboards');\n return;\n }\n\n // Fetch dashboard from API\n fetchDashboard(dashboardId).then((dashboard) => {\n if (dashboard) {\n const builderDashboard: Dashboard = {\n ...dashboard,\n title: dashboard.name,\n description: dashboard.description ?? '',\n pages: dashboard.pages.map((page, index) => ({\n ...page,\n id: page.id,\n title: page.name ?? '',\n index,\n layoutConfig: page.layoutConfig ?? {},\n })),\n };\n setCurrentDashboard(builderDashboard);\n // Set active page to first page\n if (dashboard.pages.length > 0 && !activePageId) {\n setActivePageId(dashboard.pages[0].id);\n }\n\n // Register dashboard name for breadcrumb display\n if (dashboard.name) {\n registerBreadcrumbName(dashboardId, dashboard.name);\n }\n } else {\n // Dashboard not found, redirect\n navigate('/dashboards');\n }\n });\n\n // Cleanup: unregister breadcrumb name on unmount\n return () => {\n if (dashboardId) {\n unregisterBreadcrumbName(dashboardId);\n }\n };\n }, [dashboardId, navigate, setCurrentDashboard, fetchDashboard, activePageId]);\n\n // Handle paste widget from clipboard\n const handlePaste = useCallback(async () => {\n const pageId = activePageId ?? currentDashboard?.pages?.[0]?.id;\n if (!clipboardWidgetId || !dashboardId || !pageId) {\n return;\n }\n\n const sourceWidget = widgets.get(clipboardWidgetId);\n if (!sourceWidget) {\n clearClipboard();\n return;\n }\n\n // Use smart placement algorithm - horizontal first, then next row\n const widgetsArray = Array.from(widgets.values()).filter((widget) => widget.pageId === pageId);\n const widgetSize = {\n width: sourceWidget.positionWidth ?? 4,\n height: sourceWidget.positionHeight ?? 4,\n };\n\n const { x, y } = findNextAvailablePosition(widgetsArray, widgetSize, { cols: 12 });\n\n const newPosition = {\n x,\n y,\n width: widgetSize.width,\n height: widgetSize.height,\n };\n\n const newTitle = clipboardAction === 'copy' ? `${sourceWidget.title} (Copy)` : sourceWidget.title;\n\n // Try duplicateWidget API first. A null result intentionally falls through\n // to the create mutation for older backends without duplicate support.\n let newWidget = await duplicateWidgetMutation(clipboardWidgetId, newPosition, newTitle).catch(() => null);\n\n // Fallback: Create new widget with source widget's data\n if (!newWidget) {\n newWidget = await createWidgetMutation({\n pageId,\n dashboardId,\n type: sourceWidget.type,\n title: newTitle,\n description: sourceWidget.description,\n // v2.0: Use dataSinkId and parserId instead of dataSourceId/dataPath\n dataSinkId: sourceWidget.dataSinkId ?? undefined,\n datasetId: sourceWidget.datasetId ?? undefined,\n parserId: sourceWidget.parserId ?? undefined,\n parserRules: sourceWidget.parserRules ?? undefined,\n config: sourceWidget.config || {},\n position: newPosition,\n refreshInterval: sourceWidget.refreshInterval ?? undefined,\n metadata: sourceWidget.metadata,\n });\n }\n\n if (newWidget) {\n selectWidget(newWidget.id);\n\n // If cut action, delete the original widget from backend\n if (clipboardAction === 'cut') {\n await deleteWidgetMutation(clipboardWidgetId);\n }\n }\n\n clearClipboard();\n }, [\n clipboardWidgetId,\n clipboardAction,\n dashboardId,\n activePageId,\n currentDashboard,\n widgets,\n duplicateWidgetMutation,\n createWidgetMutation,\n deleteWidgetMutation,\n selectWidget,\n clearClipboard,\n ]);\n\n // Keyboard shortcuts for canvas editing. Each shortcut is backed by the exact\n // same operation the on-screen controls use, so no new state path is\n // introduced — Delete/⌫ removes the selected widget, ⌘/Ctrl+C/X copy/cut it,\n // ⌘/Ctrl+V pastes, Esc deselects. State is read fresh from the stores at\n // event time (not via closure) so a keypress right after selecting is never\n // stale. Typing in form fields and dialog/sheet surfaces is never hijacked.\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (useDashboardStore.getState().viewMode !== 'edit') return;\n\n // Never hijack text entry (including fields inside the docked panel).\n const target = e.target as HTMLElement | null;\n if (target?.closest('input, textarea, select, [contenteditable=\"true\"]')) {\n return;\n }\n // Don't act while a modal dialog is open (e.g. the Add Widget wizard,\n // which renders an overlay). The properties panel is a non-modal Sheet\n // with no overlay, so canvas shortcuts stay live while it is focused.\n if (document.querySelector('[data-slot=\"dialog-overlay\"]')) {\n return;\n }\n\n const widgetStore = useWidgetStore.getState();\n const selectedId = widgetStore.selectedWidgetId;\n\n if (e.key === 'Escape') {\n widgetStore.selectWidget(null);\n return;\n }\n\n if ((e.key === 'Delete' || e.key === 'Backspace') && selectedId) {\n e.preventDefault();\n void deleteWidgetMutation(selectedId);\n widgetStore.selectWidget(null);\n return;\n }\n\n if (!(e.metaKey || e.ctrlKey)) return;\n\n if (e.key === 'c' && selectedId) {\n widgetStore.copyWidget(selectedId);\n } else if (e.key === 'x' && selectedId) {\n widgetStore.cutWidget(selectedId);\n } else if (e.key === 'v') {\n e.preventDefault();\n void handlePaste();\n }\n };\n\n // Capture phase: the app shell and Radix also register document keydown\n // listeners; intercepting here keeps the builder's editing keys reliable.\n // Only the keys we handle are preventDefault-ed, so global shortcuts\n // (e.g. \"g a\" navigation) are left untouched.\n document.addEventListener('keydown', handleKeyDown, true);\n return () => document.removeEventListener('keydown', handleKeyDown, true);\n }, [deleteWidgetMutation, handlePaste]);\n\n // Navigate back to the dashboards list\n const handleBack = useCallback(() => {\n navigate('/dashboards');\n }, [navigate]);\n\n // Handle save - save dirty widgets and batch update positions\n const handleSave = useCallback(async () => {\n if (!dashboardId) return;\n\n setIsSaving(true);\n setSaveError(null);\n try {\n // Get the LATEST state directly from store (avoid stale closure)\n const storeState = useWidgetStore.getState();\n const currentWidgets = Array.from(storeState.widgets.values());\n const currentDirtyIds = storeState.dirtyWidgetIds;\n\n // Debug: Log dirty widget IDs\n\n // 1. Save dirty widgets (property changes like title, config, etc.)\n const dirtyWidgets = currentWidgets.filter((w) => currentDirtyIds.has(w.id));\n\n if (dirtyWidgets.length > 0) {\n // Save each dirty widget\n const updatePromises = dirtyWidgets.map((widget) => {\n const updateInput = {\n id: widget.id,\n title: widget.title,\n config: widget.config,\n refreshInterval: widget.refreshInterval ?? undefined,\n // v2.0: Use dataSinkId and parserId instead of dataSourceId/dataPath\n dataSinkId: widget.dataSinkId ?? undefined,\n datasetId: widget.datasetId ?? undefined,\n parserId: widget.parserId ?? undefined,\n parserRules: widget.parserRules ?? undefined,\n };\n\n return updateWidgetMutation(updateInput);\n });\n\n await Promise.all(updatePromises);\n\n // Clear dirty flags after successful save\n storeState.clearAllDirty();\n }\n\n // 2. Batch update widget positions\n if (currentWidgets.length > 0) {\n const positionUpdates = currentWidgets.map((widget) => ({\n id: widget.id,\n position: widget.position || {\n x: widget.positionX ?? 0,\n y: widget.positionY ?? 0,\n width: widget.positionWidth ?? 4,\n height: widget.positionHeight ?? 4,\n },\n }));\n\n await batchUpdatePositions(positionUpdates);\n }\n\n // 3. Update dashboard metadata if needed\n if (currentDashboard) {\n await updateDashboard({\n id: dashboardId,\n name: currentDashboard.title,\n description: currentDashboard.description,\n });\n }\n } catch (error) {\n setSaveError(error instanceof Error ? error.message : 'Failed to save dashboard');\n } finally {\n setIsSaving(false);\n }\n }, [dashboardId, currentDashboard, updateWidgetMutation, batchUpdatePositions, updateDashboard]);\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n const handleExportClose = useCallback(() => {\n setIsExportDialogOpen(false);\n }, []);\n\n // ============================================================================\n // Page Management Callbacks\n // ============================================================================\n\n // Handle page selection\n const handlePageChange = useCallback((pageId: string) => {\n setActivePageId(pageId);\n }, []);\n\n // Handle adding a new page\n const handlePageAdd = useCallback(async () => {\n if (!currentDashboard || !dashboardId) return;\n\n const newPageCount = currentDashboard.pages.length + 1;\n const newPageName = `Page ${newPageCount}`;\n\n // Call GraphQL mutation to create page\n // Note: 'order' is not available for create - backend auto-assigns order\n const newPage = await createPageMutation({\n dashboardId,\n name: newPageName,\n });\n\n if (newPage) {\n // Update local state with the created page\n setCurrentDashboard({\n ...currentDashboard,\n pages: [\n ...currentDashboard.pages,\n {\n id: newPage.id,\n title: newPage.name,\n index: newPage.order,\n layoutConfig: newPage.layoutConfig || {},\n },\n ],\n });\n\n // Switch to the new page\n setActivePageId(newPage.id);\n }\n }, [currentDashboard, dashboardId, setCurrentDashboard, createPageMutation]);\n\n // Handle renaming a page\n const handlePageRename = useCallback(\n async (pageId: string, newName: string) => {\n if (!currentDashboard) return;\n\n // Call GraphQL mutation to update page\n const updatedPage = await updatePageMutation({\n id: pageId,\n name: newName,\n });\n\n if (updatedPage) {\n setCurrentDashboard({\n ...currentDashboard,\n pages: currentDashboard.pages.map((page) => (page.id === pageId ? { ...page, title: newName } : page)),\n });\n }\n },\n [currentDashboard, setCurrentDashboard, updatePageMutation]\n );\n\n // Handle duplicating a page\n const handlePageDuplicate = useCallback(\n async (pageId: string) => {\n if (!currentDashboard || !dashboardId) return;\n\n const sourcePage = currentDashboard.pages.find((p) => p.id === pageId);\n if (!sourcePage) return;\n\n // Call GraphQL mutation to create a copy of the page\n // Note: 'order' is not available for create - backend auto-assigns order\n // Note: 'layout' should be PageLayout enum ('GRID', 'FLEX', 'FREEFORM'), not layoutConfig object\n const newPage = await createPageMutation({\n dashboardId,\n name: `${sourcePage.title} (Copy)`,\n });\n\n if (newPage) {\n setCurrentDashboard({\n ...currentDashboard,\n pages: [\n ...currentDashboard.pages,\n {\n id: newPage.id,\n title: newPage.name,\n index: newPage.order,\n layoutConfig: newPage.layoutConfig || {},\n },\n ],\n });\n\n setActivePageId(newPage.id);\n }\n },\n [currentDashboard, dashboardId, setCurrentDashboard, createPageMutation]\n );\n\n // Handle deleting a page\n const handlePageDelete = useCallback(\n async (pageId: string) => {\n if (!currentDashboard || currentDashboard.pages.length <= 1) return;\n\n // Call GraphQL mutation to delete page\n const success = await deletePageMutation(pageId);\n\n if (success) {\n const updatedPages = currentDashboard.pages.filter((p) => p.id !== pageId).map((p, index) => ({ ...p, index }));\n\n setCurrentDashboard({\n ...currentDashboard,\n pages: updatedPages,\n });\n\n // Switch to first page if current page was deleted\n if (activePageId === pageId) {\n setActivePageId(updatedPages[0]?.id || null);\n }\n }\n },\n [currentDashboard, setCurrentDashboard, activePageId, deletePageMutation]\n );\n\n // Handle reordering pages\n const handlePagesReorder = useCallback(\n async (reorderedPages: DashboardPage[]) => {\n if (!currentDashboard || !dashboardId) return;\n\n // Update local state optimistically\n setCurrentDashboard({\n ...currentDashboard,\n pages: reorderedPages.map((page, index) => ({\n ...page,\n index,\n })),\n });\n\n // Call GraphQL mutation to persist the reorder\n const pageIds = reorderedPages.map((p) => p.id);\n await reorderPagesMutation(dashboardId, pageIds);\n },\n [currentDashboard, dashboardId, setCurrentDashboard, reorderPagesMutation]\n );\n\n // Permission read gate — resolve the provider first, then block the builder\n // for callers without dashboard read access. Kept ahead of error/loading\n // returns so an unauthorized caller never reaches the edit surface.\n if (permissionsLoading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (!canRead) {\n return <AccessDenied title=\"Access Denied\" description=\"You don't have permission to view this dashboard.\" />;\n }\n\n // Show error state\n if (dashboardError) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full p-6\">\n <div className=\"bg-status-error-bg border border-status-error-border rounded-lg p-4 max-w-md\">\n <h3 className=\"text-sm font-medium text-status-error-text\">Failed to load dashboard</h3>\n <p className=\"text-sm text-text-secondary mt-1\">{dashboardError.message}</p>\n <button\n type=\"button\"\n onClick={() => navigate('/dashboards')}\n className=\"mt-3 px-3 py-1.5 text-sm font-medium text-action-primary-fg bg-action-primary-bg hover:bg-action-primary-bgHover rounded-md\"\n >\n Back to Dashboards\n </button>\n </div>\n </div>\n );\n }\n\n // Show loading state\n if (dashboardLoading || !currentDashboard) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n return (\n <DashboardEditorShell\n topbar={\n <BuilderTopbar\n title={currentDashboard.title || 'Untitled dashboard'}\n onBack={handleBack}\n actions={\n <div className=\"flex items-center gap-2\">\n {saveError ? (\n <span className=\"text-xs font-medium text-status-error-text\" role=\"alert\">\n {saveError}\n </span>\n ) : null}\n {/* AI custom-widget authoring creates a widget → gate on WIDGET_CREATE. */}\n {canCreateWidget ? (\n <button\n type=\"button\"\n data-testid=\"open-custom-widget-builder\"\n onClick={() => setIsCustomWidgetBuilderOpen(true)}\n className=\"rounded-md border border-border-default px-2 py-1 text-xs font-medium text-text-primary hover:bg-bg-sunken focus:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg\"\n >\n AI widget\n </button>\n ) : null}\n </div>\n }\n />\n }\n >\n <BuilderCanvas\n canvas={\n <DashboardCanvas\n dashboardId={currentDashboard.id}\n pageId={activePageId || currentDashboard.pages?.[0]?.id}\n title={currentDashboard.title}\n onPaste={handlePaste}\n // Gate action controls: DashboardToolbar renders each button only\n // when its handler is defined, so passing undefined hides the\n // control for callers lacking the permission.\n onSave={canUpdate ? handleSave : undefined}\n isSaving={isSaving}\n onExport={canExport ? handleExport : undefined}\n // Page tabs props\n pages={currentDashboard.pages.map((page) => ({\n id: page.id,\n name: page.title || `Page ${(page.index ?? 0) + 1}`,\n order: page.index ?? 0,\n }))}\n activePageId={activePageId || currentDashboard.pages?.[0]?.id}\n onPageChange={handlePageChange}\n onPageAdd={handlePageAdd}\n onPageRename={handlePageRename}\n onPageDuplicate={handlePageDuplicate}\n onPageDelete={handlePageDelete}\n onPagesReorder={handlePagesReorder}\n showPageTabs={true}\n />\n }\n />\n\n <ExportDashboardDialog\n isOpen={isExportDialogOpen}\n dashboardId={currentDashboard.id}\n dashboardName={currentDashboard.title}\n onClose={handleExportClose}\n />\n\n <CustomWidgetBuilderSheet\n isOpen={isCustomWidgetBuilderOpen}\n onClose={() => setIsCustomWidgetBuilderOpen(false)}\n dashboardId={currentDashboard.id}\n pageId={activePageId || currentDashboard.pages?.[0]?.id || ''}\n />\n </DashboardEditorShell>\n );\n});\n\nexport default DashboardBuilderPage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardBuilderPage.js","names":[],"sources":["../../../src/bigconsole/pages/DashboardBuilderPage.tsx"],"sourcesContent":["/**\n * DashboardBuilderPage\n *\n * Full dashboard builder experience with side panels.\n */\n\nimport { type FC, memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\nimport { useBigConsoleNavigate } from '../context';\nimport { DashboardCanvas, ExportDashboardDialog } from '../components/dashboard';\nimport { BuilderCanvas, BuilderTopbar, DashboardEditorShell } from '../components/builder';\nimport { useDashboardStore, useWidgetStore } from '../store';\nimport { useDashboardOperations, useWidgetOperations, usePageOperations } from '../hooks';\nimport { CustomWidgetBuilderSheet } from '../customWidget';\nimport { SECTION_FOCUS_RING, Section } from '../components/common';\nimport type { Dashboard, DashboardPage } from '../types';\nimport { findNextAvailablePosition } from '../utils/widgetPositioning';\n\n// Breadcrumb name registration helpers (get from shell at call time to avoid timing issues)\nfunction getBreadcrumbRegistry() {\n try {\n return (\n window as unknown as {\n __breadcrumbRegistry?: { register: (id: string, name: string) => void; unregister: (id: string) => void };\n }\n ).__breadcrumbRegistry;\n } catch {\n return undefined;\n }\n}\n\nfunction registerBreadcrumbName(id: string, name: string) {\n const registry = getBreadcrumbRegistry();\n if (registry?.register) {\n registry.register(id, name);\n }\n}\n\nfunction unregisterBreadcrumbName(id: string) {\n const registry = getBreadcrumbRegistry();\n if (registry?.unregister) {\n registry.unregister(id);\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardBuilderPage: FC = memo(function DashboardBuilderPage() {\n const { dashboardId } = useParams<{ dashboardId: string }>();\n const navigate = useBigConsoleNavigate();\n const { t } = useI18n();\n\n // RBAC gating. The builder is the edit surface: a read gate blocks it for\n // callers without dashboard read access, and action gates hide Save/Export and\n // the AI-widget (widget-create) control for callers lacking those permissions.\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_READ);\n const canUpdate = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_UPDATE);\n const canExport = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_EXPORT);\n const canCreateWidget = hasPermission(BIGCONSOLE_PERMISSIONS.WIDGET_CREATE);\n\n const [isSaving, setIsSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);\n // BOFF-5532: AI custom-widget authoring/review surface.\n const [isCustomWidgetBuilderOpen, setIsCustomWidgetBuilderOpen] = useState(false);\n const [activePageId, setActivePageId] = useState<string | null>(null);\n\n // Dashboard operations from GraphQL\n const {\n fetchDashboard,\n updateDashboard,\n loading: dashboardLoading,\n error: dashboardError,\n } = useDashboardOperations();\n\n // Widget operations from GraphQL\n const {\n createWidget: createWidgetMutation,\n updateWidget: updateWidgetMutation,\n deleteWidget: deleteWidgetMutation,\n duplicateWidget: duplicateWidgetMutation,\n batchUpdatePositions,\n } = useWidgetOperations(undefined, dashboardId);\n\n // Page operations from GraphQL\n const {\n createPage: createPageMutation,\n updatePage: updatePageMutation,\n deletePage: deletePageMutation,\n reorderPages: reorderPagesMutation,\n } = usePageOperations();\n\n // Store actions\n const setCurrentDashboard = useDashboardStore((state) => state.setCurrentDashboard);\n const currentDashboard = useDashboardStore((state) => state.currentDashboard);\n const setViewMode = useDashboardStore((state) => state.setViewMode);\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n const widgets = useWidgetStore((state) => state.widgets);\n const clipboardWidgetId = useWidgetStore((state) => state.clipboardWidgetId);\n const clipboardAction = useWidgetStore((state) => state.clipboardAction);\n const clearClipboard = useWidgetStore((state) => state.clearClipboard);\n\n // Set edit mode on mount\n useEffect(() => {\n setViewMode('edit');\n return () => setViewMode('view');\n }, [setViewMode]);\n\n // Load dashboard data from API\n useEffect(() => {\n if (!dashboardId) {\n navigate('/dashboards');\n return;\n }\n\n // Fetch dashboard from API\n fetchDashboard(dashboardId).then((dashboard) => {\n if (dashboard) {\n const builderDashboard: Dashboard = {\n ...dashboard,\n title: dashboard.name,\n description: dashboard.description ?? '',\n pages: dashboard.pages.map((page, index) => ({\n ...page,\n id: page.id,\n title: page.name ?? '',\n index,\n layoutConfig: page.layoutConfig ?? {},\n })),\n };\n setCurrentDashboard(builderDashboard);\n // Set active page to first page\n if (dashboard.pages.length > 0 && !activePageId) {\n setActivePageId(dashboard.pages[0].id);\n }\n\n // Register dashboard name for breadcrumb display\n if (dashboard.name) {\n registerBreadcrumbName(dashboardId, dashboard.name);\n }\n } else {\n // Dashboard not found, redirect\n navigate('/dashboards');\n }\n });\n\n // Cleanup: unregister breadcrumb name on unmount\n return () => {\n if (dashboardId) {\n unregisterBreadcrumbName(dashboardId);\n }\n };\n }, [dashboardId, navigate, setCurrentDashboard, fetchDashboard, activePageId]);\n\n // Handle paste widget from clipboard\n const handlePaste = useCallback(async () => {\n const pageId = activePageId ?? currentDashboard?.pages?.[0]?.id;\n if (!clipboardWidgetId || !dashboardId || !pageId) {\n return;\n }\n\n const sourceWidget = widgets.get(clipboardWidgetId);\n if (!sourceWidget) {\n clearClipboard();\n return;\n }\n\n // Use smart placement algorithm - horizontal first, then next row\n const widgetsArray = Array.from(widgets.values()).filter((widget) => widget.pageId === pageId);\n const widgetSize = {\n width: sourceWidget.positionWidth ?? 4,\n height: sourceWidget.positionHeight ?? 4,\n };\n\n const { x, y } = findNextAvailablePosition(widgetsArray, widgetSize, { cols: 12 });\n\n const newPosition = {\n x,\n y,\n width: widgetSize.width,\n height: widgetSize.height,\n };\n\n const newTitle =\n clipboardAction === 'copy'\n ? t('bigconsole.dashboards.builder.actions.copySuffix', '{{title}} (Copy)', { title: sourceWidget.title })\n : sourceWidget.title;\n\n // Try duplicateWidget API first. A null result intentionally falls through\n // to the create mutation for older backends without duplicate support.\n let newWidget = await duplicateWidgetMutation(clipboardWidgetId, newPosition, newTitle).catch(() => null);\n\n // Fallback: Create new widget with source widget's data\n if (!newWidget) {\n newWidget = await createWidgetMutation({\n pageId,\n dashboardId,\n type: sourceWidget.type,\n title: newTitle,\n description: sourceWidget.description,\n // v2.0: Use dataSinkId and parserId instead of dataSourceId/dataPath\n dataSinkId: sourceWidget.dataSinkId ?? undefined,\n datasetId: sourceWidget.datasetId ?? undefined,\n parserId: sourceWidget.parserId ?? undefined,\n parserRules: sourceWidget.parserRules ?? undefined,\n config: sourceWidget.config || {},\n position: newPosition,\n refreshInterval: sourceWidget.refreshInterval ?? undefined,\n metadata: sourceWidget.metadata,\n });\n }\n\n if (newWidget) {\n selectWidget(newWidget.id);\n\n // If cut action, delete the original widget from backend\n if (clipboardAction === 'cut') {\n await deleteWidgetMutation(clipboardWidgetId);\n }\n }\n\n clearClipboard();\n }, [\n clipboardWidgetId,\n clipboardAction,\n dashboardId,\n activePageId,\n currentDashboard,\n widgets,\n duplicateWidgetMutation,\n createWidgetMutation,\n deleteWidgetMutation,\n selectWidget,\n clearClipboard,\n t,\n ]);\n\n // Keyboard shortcuts for canvas editing. Each shortcut is backed by the exact\n // same operation the on-screen controls use, so no new state path is\n // introduced — Delete/⌫ removes the selected widget, ⌘/Ctrl+C/X copy/cut it,\n // ⌘/Ctrl+V pastes, Esc deselects. State is read fresh from the stores at\n // event time (not via closure) so a keypress right after selecting is never\n // stale. Typing in form fields and dialog/sheet surfaces is never hijacked.\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (useDashboardStore.getState().viewMode !== 'edit') return;\n\n // Never hijack text entry (including fields inside the docked panel).\n const target = e.target as HTMLElement | null;\n if (target?.closest('input, textarea, select, [contenteditable=\"true\"]')) {\n return;\n }\n // Don't act while a modal dialog is open (e.g. the Add Widget wizard,\n // which renders an overlay). The properties panel is a non-modal Sheet\n // with no overlay, so canvas shortcuts stay live while it is focused.\n if (document.querySelector('[data-slot=\"dialog-overlay\"]')) {\n return;\n }\n\n const widgetStore = useWidgetStore.getState();\n const selectedId = widgetStore.selectedWidgetId;\n\n if (e.key === 'Escape') {\n widgetStore.selectWidget(null);\n return;\n }\n\n if ((e.key === 'Delete' || e.key === 'Backspace') && selectedId) {\n e.preventDefault();\n void deleteWidgetMutation(selectedId);\n widgetStore.selectWidget(null);\n return;\n }\n\n if (!(e.metaKey || e.ctrlKey)) return;\n\n if (e.key === 'c' && selectedId) {\n widgetStore.copyWidget(selectedId);\n } else if (e.key === 'x' && selectedId) {\n widgetStore.cutWidget(selectedId);\n } else if (e.key === 'v') {\n e.preventDefault();\n void handlePaste();\n }\n };\n\n // Capture phase: the app shell and Radix also register document keydown\n // listeners; intercepting here keeps the builder's editing keys reliable.\n // Only the keys we handle are preventDefault-ed, so global shortcuts\n // (e.g. \"g a\" navigation) are left untouched.\n document.addEventListener('keydown', handleKeyDown, true);\n return () => document.removeEventListener('keydown', handleKeyDown, true);\n }, [deleteWidgetMutation, handlePaste]);\n\n // Navigate back to the dashboards list\n const handleBack = useCallback(() => {\n navigate('/dashboards');\n }, [navigate]);\n\n // Handle save - save dirty widgets and batch update positions\n const handleSave = useCallback(async () => {\n if (!dashboardId) return;\n\n setIsSaving(true);\n setSaveError(null);\n try {\n // Get the LATEST state directly from store (avoid stale closure)\n const storeState = useWidgetStore.getState();\n const currentWidgets = Array.from(storeState.widgets.values());\n const currentDirtyIds = storeState.dirtyWidgetIds;\n\n // Debug: Log dirty widget IDs\n\n // 1. Save dirty widgets (property changes like title, config, etc.)\n const dirtyWidgets = currentWidgets.filter((w) => currentDirtyIds.has(w.id));\n\n if (dirtyWidgets.length > 0) {\n // Save each dirty widget\n const updatePromises = dirtyWidgets.map((widget) => {\n const updateInput = {\n id: widget.id,\n title: widget.title,\n config: widget.config,\n refreshInterval: widget.refreshInterval ?? undefined,\n // v2.0: Use dataSinkId and parserId instead of dataSourceId/dataPath\n dataSinkId: widget.dataSinkId ?? undefined,\n datasetId: widget.datasetId ?? undefined,\n parserId: widget.parserId ?? undefined,\n parserRules: widget.parserRules ?? undefined,\n };\n\n return updateWidgetMutation(updateInput);\n });\n\n await Promise.all(updatePromises);\n\n // Clear dirty flags after successful save\n storeState.clearAllDirty();\n }\n\n // 2. Batch update widget positions\n if (currentWidgets.length > 0) {\n const positionUpdates = currentWidgets.map((widget) => ({\n id: widget.id,\n position: widget.position || {\n x: widget.positionX ?? 0,\n y: widget.positionY ?? 0,\n width: widget.positionWidth ?? 4,\n height: widget.positionHeight ?? 4,\n },\n }));\n\n await batchUpdatePositions(positionUpdates);\n }\n\n // 3. Update dashboard metadata if needed\n if (currentDashboard) {\n await updateDashboard({\n id: dashboardId,\n name: currentDashboard.title,\n description: currentDashboard.description,\n });\n }\n } catch (error) {\n setSaveError(\n error instanceof Error\n ? error.message\n : t('bigconsole.dashboards.builder.errors.saveFailed', 'Failed to save dashboard')\n );\n } finally {\n setIsSaving(false);\n }\n }, [dashboardId, currentDashboard, updateWidgetMutation, batchUpdatePositions, updateDashboard, t]);\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n const handleExportClose = useCallback(() => {\n setIsExportDialogOpen(false);\n }, []);\n\n // ============================================================================\n // Page Management Callbacks\n // ============================================================================\n\n // Handle page selection\n const handlePageChange = useCallback((pageId: string) => {\n setActivePageId(pageId);\n }, []);\n\n // Handle adding a new page\n const handlePageAdd = useCallback(async () => {\n if (!currentDashboard || !dashboardId) return;\n\n const newPageCount = currentDashboard.pages.length + 1;\n const newPageName = t('bigconsole.dashboards.builder.actions.newPageName', 'Page {{count}}', {\n count: newPageCount,\n });\n\n // Call GraphQL mutation to create page\n // Note: 'order' is not available for create - backend auto-assigns order\n const newPage = await createPageMutation({\n dashboardId,\n name: newPageName,\n });\n\n if (newPage) {\n // Update local state with the created page\n setCurrentDashboard({\n ...currentDashboard,\n pages: [\n ...currentDashboard.pages,\n {\n id: newPage.id,\n title: newPage.name,\n index: newPage.order,\n layoutConfig: newPage.layoutConfig || {},\n },\n ],\n });\n\n // Switch to the new page\n setActivePageId(newPage.id);\n }\n }, [currentDashboard, dashboardId, setCurrentDashboard, createPageMutation, t]);\n\n // Handle renaming a page\n const handlePageRename = useCallback(\n async (pageId: string, newName: string) => {\n if (!currentDashboard) return;\n\n // Call GraphQL mutation to update page\n const updatedPage = await updatePageMutation({\n id: pageId,\n name: newName,\n });\n\n if (updatedPage) {\n setCurrentDashboard({\n ...currentDashboard,\n pages: currentDashboard.pages.map((page) => (page.id === pageId ? { ...page, title: newName } : page)),\n });\n }\n },\n [currentDashboard, setCurrentDashboard, updatePageMutation]\n );\n\n // Handle duplicating a page\n const handlePageDuplicate = useCallback(\n async (pageId: string) => {\n if (!currentDashboard || !dashboardId) return;\n\n const sourcePage = currentDashboard.pages.find((p) => p.id === pageId);\n if (!sourcePage) return;\n\n // Call GraphQL mutation to create a copy of the page\n // Note: 'order' is not available for create - backend auto-assigns order\n // Note: 'layout' should be PageLayout enum ('GRID', 'FLEX', 'FREEFORM'), not layoutConfig object\n const newPage = await createPageMutation({\n dashboardId,\n name: t('bigconsole.dashboards.builder.actions.copySuffix', '{{title}} (Copy)', {\n title: sourcePage.title ?? '',\n }),\n });\n\n if (newPage) {\n setCurrentDashboard({\n ...currentDashboard,\n pages: [\n ...currentDashboard.pages,\n {\n id: newPage.id,\n title: newPage.name,\n index: newPage.order,\n layoutConfig: newPage.layoutConfig || {},\n },\n ],\n });\n\n setActivePageId(newPage.id);\n }\n },\n [currentDashboard, dashboardId, setCurrentDashboard, createPageMutation, t]\n );\n\n // Handle deleting a page\n const handlePageDelete = useCallback(\n async (pageId: string) => {\n if (!currentDashboard || currentDashboard.pages.length <= 1) return;\n\n // Call GraphQL mutation to delete page\n const success = await deletePageMutation(pageId);\n\n if (success) {\n const updatedPages = currentDashboard.pages.filter((p) => p.id !== pageId).map((p, index) => ({ ...p, index }));\n\n setCurrentDashboard({\n ...currentDashboard,\n pages: updatedPages,\n });\n\n // Switch to first page if current page was deleted\n if (activePageId === pageId) {\n setActivePageId(updatedPages[0]?.id || null);\n }\n }\n },\n [currentDashboard, setCurrentDashboard, activePageId, deletePageMutation]\n );\n\n // Handle reordering pages\n const handlePagesReorder = useCallback(\n async (reorderedPages: DashboardPage[]) => {\n if (!currentDashboard || !dashboardId) return;\n\n // Update local state optimistically\n setCurrentDashboard({\n ...currentDashboard,\n pages: reorderedPages.map((page, index) => ({\n ...page,\n index,\n })),\n });\n\n // Call GraphQL mutation to persist the reorder\n const pageIds = reorderedPages.map((p) => p.id);\n await reorderPagesMutation(dashboardId, pageIds);\n },\n [currentDashboard, dashboardId, setCurrentDashboard, reorderPagesMutation]\n );\n\n // Permission read gate — resolve the provider first, then block the builder\n // for callers without dashboard read access. Kept ahead of error/loading\n // returns so an unauthorized caller never reaches the edit surface.\n if (permissionsLoading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (!canRead) {\n return (\n <AccessDenied\n title={t('bigconsole.dashboards.builder.errors.accessDeniedTitle', 'Access Denied')}\n description={t(\n 'bigconsole.dashboards.builder.errors.accessDeniedDescription',\n \"You don't have permission to view this dashboard.\"\n )}\n />\n );\n }\n\n // Show error state\n if (dashboardError) {\n return (\n <div className=\"flex h-full flex-col items-center justify-center p-6\">\n <Section\n headingLevel={2}\n tone=\"danger\"\n title={t('bigconsole.dashboards.builder.errors.loadFailed', 'Failed to load dashboard')}\n description={<span className=\"break-words\">{dashboardError.message}</span>}\n className=\"w-full max-w-md\"\n data-testid=\"dashboard-builder-error\"\n actions={\n <Button size=\"sm\" onClick={() => navigate('/dashboards')}>\n {t('bigconsole.dashboards.builder.actions.backToDashboards', 'Back to Dashboards')}\n </Button>\n }\n />\n </div>\n );\n }\n\n // Show loading state\n if (dashboardLoading || !currentDashboard) {\n return (\n <div className=\"flex items-center justify-center h-full\" role=\"status\" aria-live=\"polite\">\n <div className=\"animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full\" />\n <span className=\"sr-only\">Loading dashboard…</span>\n </div>\n );\n }\n\n return (\n <DashboardEditorShell\n topbar={\n <BuilderTopbar\n title={\n currentDashboard.title || t('bigconsole.dashboards.builder.header.untitledDashboard', 'Untitled dashboard')\n }\n onBack={handleBack}\n actions={\n <div className=\"flex items-center gap-2\">\n {saveError ? (\n <span className=\"text-xs font-medium text-status-error-text\" role=\"alert\">\n {saveError}\n </span>\n ) : null}\n {/* AI custom-widget authoring creates a widget → gate on WIDGET_CREATE. */}\n {canCreateWidget ? (\n <button\n type=\"button\"\n data-testid=\"open-custom-widget-builder\"\n onClick={() => setIsCustomWidgetBuilderOpen(true)}\n className={`inline-flex min-h-control-md items-center rounded-button border border-[var(--color-border-subtle)] px-2 py-1 text-xs font-medium text-text-primary motion-safe:transition-colors hover:bg-bg-sunken ${SECTION_FOCUS_RING}`}\n >\n {t('bigconsole.dashboards.builder.toolbar.aiWidget', 'AI widget')}\n </button>\n ) : null}\n </div>\n }\n />\n }\n >\n <BuilderCanvas\n canvas={\n <DashboardCanvas\n dashboardId={currentDashboard.id}\n pageId={activePageId || currentDashboard.pages?.[0]?.id}\n title={currentDashboard.title}\n onPaste={handlePaste}\n // Gate action controls: DashboardToolbar renders each button only\n // when its handler is defined, so passing undefined hides the\n // control for callers lacking the permission.\n onSave={canUpdate ? handleSave : undefined}\n isSaving={isSaving}\n onExport={canExport ? handleExport : undefined}\n // Page tabs props\n pages={currentDashboard.pages.map((page) => ({\n id: page.id,\n name:\n page.title ||\n t('bigconsole.dashboards.builder.actions.newPageName', 'Page {{count}}', {\n count: (page.index ?? 0) + 1,\n }),\n order: page.index ?? 0,\n }))}\n activePageId={activePageId || currentDashboard.pages?.[0]?.id}\n onPageChange={handlePageChange}\n onPageAdd={handlePageAdd}\n onPageRename={handlePageRename}\n onPageDuplicate={handlePageDuplicate}\n onPageDelete={handlePageDelete}\n onPagesReorder={handlePagesReorder}\n showPageTabs={true}\n />\n }\n />\n\n <ExportDashboardDialog\n isOpen={isExportDialogOpen}\n dashboardId={currentDashboard.id}\n dashboardName={currentDashboard.title}\n onClose={handleExportClose}\n />\n\n <CustomWidgetBuilderSheet\n isOpen={isCustomWidgetBuilderOpen}\n onClose={() => setIsCustomWidgetBuilderOpen(false)}\n dashboardId={currentDashboard.id}\n pageId={activePageId || currentDashboard.pages?.[0]?.id || ''}\n />\n </DashboardEditorShell>\n );\n});\n\nexport default DashboardBuilderPage;\n"],"mappings":"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"}