@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 +1 @@
1
- {"version":3,"file":"EmbedTab.js","names":[],"sources":["../../../../../src/bigconsole/components/dashboard/ShareDialog/EmbedTab.tsx"],"sourcesContent":["/**\n * EmbedTab — \"Publish to web\" section of the dashboard Share dialog.\n *\n * Owner-facing UI to publish a dashboard as an anonymous-link embed serving LIVE\n * data, manage the allowed parent domains, copy the iframe snippet, rotate the\n * link key, and unpublish. Backed by {@link useDashboardEmbedPolicy}; all\n * mutations are gateway-enforced by `@rbac(manage_embed)` (owner-only).\n *\n * Self-contained (inline styles driven by the dialog's `colors`) so it slots into\n * the existing hand-styled ShareDialog without a broader refactor.\n */\n\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useDashboardEmbedPolicy, themeChoiceFromPolicy } from '../../../hooks/useDashboardEmbedPolicy';\nimport { BIGCONSOLE_PERMISSIONS } from '../../../../constants/permissions';\n\ninterface DialogColors {\n bgPrimary: string;\n bgSecondary: string;\n bgTertiary: string;\n textPrimary: string;\n textSecondary: string;\n textTertiary: string;\n borderDefault: string;\n actionPrimaryBg: string;\n actionPrimaryBgHover: string;\n statusSuccessBg: string;\n statusErrorText: string;\n}\n\nexport interface EmbedTabProps {\n dashboardId: string;\n colors: DialogColors;\n}\n\n/** Embed color-mode baked into the iframe URL (BOFF-5734). */\ntype EmbedSnippetTheme = 'light' | 'dark' | 'auto';\n\nconst THEME_OPTIONS: ReadonlyArray<{ value: EmbedSnippetTheme; label: string }> = [\n { value: 'light', label: 'Light' },\n { value: 'dark', label: 'Dark' },\n { value: 'auto', label: 'Auto (match viewer)' },\n];\n\n/**\n * Bake the chosen color mode into an iframe snippet by adding `?theme=` to its\n * `src` URL. `light` is the default and is left implicit so existing/plain\n * snippets stay unchanged. The secret `#key=` fragment is preserved untouched.\n *\n * Inserted via string splicing (before the fragment) rather than `URL`/\n * `URLSearchParams`, which re-serialize ALL params and could subtly re-encode\n * existing ones (e.g. `%20` → `+`). `theme` is a fixed enum, so no encoding is\n * needed; every other part of the URL is passed through byte-for-byte.\n */\nexport function withThemeInSnippet(snippet: string, theme: EmbedSnippetTheme): string {\n if (theme === 'light') return snippet;\n // Require whitespace before `src` so we match the real attribute, never a\n // substring like `data-src`/`srcdoc` (the snippet is backend-generated with\n // each attribute on its own indented line).\n return snippet.replace(/(?<=\\s)src=\"([^\"]*)\"/, (_match, url: string) => {\n const hashIdx = url.indexOf('#');\n const base = hashIdx === -1 ? url : url.slice(0, hashIdx);\n const fragment = hashIdx === -1 ? '' : url.slice(hashIdx);\n const sep = base.includes('?') ? '&' : '?';\n return `src=\"${base}${sep}theme=${theme}${fragment}\"`;\n });\n}\n\n/** Validate an exact https origin (matches the backend allowlist rules). */\nfunction normalizeOrigin(raw: string): string | null {\n const trimmed = raw.trim();\n if (!trimmed) return null;\n try {\n const url = new URL(trimmed);\n if (url.protocol !== 'https:') return null;\n if ((url.pathname !== '' && url.pathname !== '/') || url.search || url.hash) return null;\n return url.origin;\n } catch {\n return null;\n }\n}\n\nexport function EmbedTab({ dashboardId, colors }: EmbedTabProps) {\n const {\n policy,\n secret,\n loading,\n error,\n fetchPolicy,\n publishToWeb,\n applyPresentation,\n setAllowedOrigins,\n rotateKey,\n unpublish,\n } = useDashboardEmbedPolicy();\n\n // All embed policy mutations (publish, rotate, revoke, set-origins, apply\n // presentation) are gateway-enforced by `@rbac(manage_embed)`. Gate the UI on\n // the same permission so the controls degrade gracefully instead of erroring.\n const { hasPermission } = usePermissions();\n const canManageEmbed = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_MANAGE_EMBED);\n\n const [domainInput, setDomainInput] = useState('');\n const [pendingDomains, setPendingDomains] = useState<string[]>([]);\n const [attested, setAttested] = useState(false);\n const [copied, setCopied] = useState(false);\n // Presentation options (BOFF-5735). Defaults mirror the backend column defaults\n // so a fresh publish captures a sensible look without extra clicks.\n const [theme, setTheme] = useState<EmbedSnippetTheme>('light');\n const [showTitle, setShowTitle] = useState(false);\n const [showBranding, setShowBranding] = useState(true);\n const [presentationSaved, setPresentationSaved] = useState(false);\n const [localError, setLocalError] = useState<string | null>(null);\n // Only seed the presentation controls once per policy identity — NOT on every\n // `policy` object change. Otherwise an unrelated mutation (e.g. adding a domain\n // via setAllowedOrigins) would return a fresh policy object and clobber the\n // owner's in-progress, not-yet-applied theme/title/branding edits.\n const syncedPolicyIdRef = useRef<string | null>(null);\n const savedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (dashboardId) fetchPolicy(dashboardId);\n }, [dashboardId, fetchPolicy]);\n\n // Reflect the persisted presentation the first time each policy loads.\n useEffect(() => {\n if (!policy || syncedPolicyIdRef.current === policy.id) return;\n syncedPolicyIdRef.current = policy.id;\n setTheme(themeChoiceFromPolicy(policy.presentationTheme));\n setShowTitle(policy.showDashboardTitle);\n setShowBranding(policy.showBrandingFooter);\n }, [policy]);\n\n // Clear any pending \"Updated ✓\" timer on unmount.\n useEffect(\n () => () => {\n if (savedTimerRef.current) clearTimeout(savedTimerRef.current);\n },\n []\n );\n\n const isPublished = policy?.status === 'ACTIVE';\n const domains = isPublished ? (policy?.allowedParentOrigins ?? []) : pendingDomains;\n\n // The copyable iframe snippet: from the one-time secret if present (fresh\n // publish/rotate), else reconstructed from the locator for an already-published\n // policy (the fragment key is only available in the one-time secret).\n const iframeSnippet = useMemo(() => {\n if (secret?.iframeSnippet) return withThemeInSnippet(secret.iframeSnippet, theme);\n return null;\n }, [secret, theme]);\n\n const addDomain = () => {\n setLocalError(null);\n const origin = normalizeOrigin(domainInput);\n if (!origin) {\n setLocalError('Enter a valid https origin, e.g. https://example.com');\n return;\n }\n if (domains.includes(origin)) {\n setDomainInput('');\n return;\n }\n if (isPublished && policy) {\n void setAllowedOrigins(policy.id, [...domains, origin]);\n } else {\n setPendingDomains((prev) => [...prev, origin]);\n }\n setDomainInput('');\n };\n\n const removeDomain = (origin: string) => {\n if (isPublished && policy) {\n void setAllowedOrigins(\n policy.id,\n domains.filter((d) => d !== origin)\n );\n } else {\n setPendingDomains((prev) => prev.filter((d) => d !== origin));\n }\n };\n\n const handlePublish = async () => {\n setLocalError(null);\n if (pendingDomains.length === 0) {\n setLocalError('Add at least one allowed website domain before publishing.');\n return;\n }\n if (!attested) {\n setLocalError('Please confirm this dashboard is safe to be public.');\n return;\n }\n await publishToWeb(dashboardId, pendingDomains, true, { theme, showTitle, showBranding });\n };\n\n const handleApplyPresentation = async () => {\n if (!policy) return;\n setLocalError(null);\n const updated = await applyPresentation(policy.id, { theme, showTitle, showBranding });\n if (updated) {\n setPresentationSaved(true);\n // Clear any in-flight timer so rapid re-clicks don't hide the confirmation early.\n if (savedTimerRef.current) clearTimeout(savedTimerRef.current);\n savedTimerRef.current = setTimeout(() => setPresentationSaved(false), 1500);\n }\n };\n\n const handleCopy = async (text: string) => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n } catch {\n setLocalError('Could not copy — select and copy manually.');\n }\n };\n\n const primaryBtn: React.CSSProperties = {\n padding: '8px 14px',\n borderRadius: '8px',\n border: 'none',\n background: colors.actionPrimaryBg,\n color: '#fff',\n fontSize: '13px',\n fontWeight: 600,\n cursor: 'pointer',\n };\n const ghostBtn: React.CSSProperties = {\n padding: '8px 14px',\n borderRadius: '8px',\n border: `1px solid ${colors.borderDefault}`,\n background: 'transparent',\n color: colors.textPrimary,\n fontSize: '13px',\n cursor: 'pointer',\n };\n const inputStyle: React.CSSProperties = {\n flex: 1,\n padding: '8px 10px',\n borderRadius: '8px',\n border: `1px solid ${colors.borderDefault}`,\n background: colors.bgSecondary,\n color: colors.textPrimary,\n fontSize: '13px',\n };\n const selectStyle: React.CSSProperties = {\n padding: '4px 8px',\n borderRadius: '6px',\n border: `1px solid ${colors.borderDefault}`,\n background: colors.bgSecondary,\n color: colors.textPrimary,\n fontSize: '12px',\n cursor: 'pointer',\n };\n const toggleRow: React.CSSProperties = {\n display: 'flex',\n gap: '8px',\n alignItems: 'flex-start',\n fontSize: '12px',\n color: colors.textSecondary,\n };\n\n // Presentation controls (BOFF-5735) — shared by the pre-publish and published\n // states. Local state drives both the persisted policy and the snippet `?theme=`.\n const presentationControls = (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>\n <label style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '8px' }}>\n <span style={{ fontSize: '12px', color: colors.textSecondary }}>Theme</span>\n <select\n value={theme}\n onChange={(e) => {\n // The options are fixed, but narrow instead of casting so an\n // unexpected value can never slip into state.\n const value = e.target.value;\n if (value === 'light' || value === 'dark' || value === 'auto') {\n setTheme(value);\n }\n }}\n style={selectStyle}\n >\n {THEME_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n </label>\n <label style={toggleRow}>\n <input type=\"checkbox\" checked={showTitle} onChange={(e) => setShowTitle(e.target.checked)} />\n <span>Show the dashboard title as a header</span>\n </label>\n <label style={toggleRow}>\n <input type=\"checkbox\" checked={showBranding} onChange={(e) => setShowBranding(e.target.checked)} />\n <span>Show a subtle “Powered by BigConsole” footer</span>\n </label>\n </div>\n );\n\n return (\n <div style={{ padding: '20px 24px' }}>\n {/* Status banner */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '12px 14px',\n borderRadius: '10px',\n background: colors.bgSecondary,\n marginBottom: '16px',\n }}\n >\n <div>\n <div style={{ fontSize: '14px', fontWeight: 600, color: colors.textPrimary }}>Publish to the web</div>\n <div style={{ fontSize: '12px', color: colors.textSecondary, marginTop: '2px' }}>\n {isPublished\n ? 'This dashboard is live. Anyone with the link on an allowed website sees its live data.'\n : 'Embed this dashboard on your website. Viewers see live data — no login required.'}\n </div>\n </div>\n <span\n style={{\n fontSize: '11px',\n fontWeight: 700,\n padding: '3px 8px',\n borderRadius: '999px',\n color: '#fff',\n background: isPublished ? colors.statusSuccessBg : colors.textTertiary,\n }}\n >\n {isPublished ? 'PUBLISHED' : 'PRIVATE'}\n </span>\n </div>\n\n {/* Allowed domains */}\n <div style={{ marginBottom: '16px' }}>\n <div style={{ fontSize: '12px', fontWeight: 600, color: colors.textSecondary, marginBottom: '6px' }}>\n Allowed website domains\n </div>\n <div style={{ display: 'flex', gap: '8px', marginBottom: '8px' }}>\n <input\n style={inputStyle}\n placeholder=\"https://yoursite.com\"\n value={domainInput}\n onChange={(e) => setDomainInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') addDomain();\n }}\n />\n <button style={ghostBtn} onClick={addDomain} type=\"button\" disabled={!canManageEmbed}>\n Add\n </button>\n </div>\n {domains.length === 0 ? (\n <div style={{ fontSize: '12px', color: colors.textTertiary }}>\n No domains yet. Only listed https domains may frame this dashboard.\n </div>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>\n {domains.map((d) => (\n <div\n key={d}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '6px 10px',\n borderRadius: '8px',\n background: colors.bgTertiary,\n }}\n >\n <span style={{ fontSize: '13px', color: colors.textPrimary }}>{d}</span>\n {canManageEmbed && (\n <button\n style={{ ...ghostBtn, padding: '2px 8px', color: colors.statusErrorText }}\n onClick={() => removeDomain(d)}\n type=\"button\"\n >\n Remove\n </button>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Presentation (unpublished — captured into the publish) */}\n {!isPublished && (\n <div style={{ marginBottom: '16px' }}>\n <div style={{ fontSize: '12px', fontWeight: 600, color: colors.textSecondary, marginBottom: '8px' }}>\n Presentation\n </div>\n {presentationControls}\n </div>\n )}\n\n {/* Publish CTA (unpublished) */}\n {!isPublished && (\n <div style={{ marginBottom: '8px' }}>\n <label style={{ display: 'flex', gap: '8px', alignItems: 'flex-start', marginBottom: '12px' }}>\n <input type=\"checkbox\" checked={attested} onChange={(e) => setAttested(e.target.checked)} />\n <span style={{ fontSize: '12px', color: colors.textSecondary }}>\n I understand this makes the dashboard&apos;s live data visible to anyone with the link, and I confirm it\n is safe to be public.\n </span>\n </label>\n <button style={primaryBtn} onClick={handlePublish} disabled={loading || !canManageEmbed} type=\"button\">\n {loading ? 'Publishing…' : 'Publish to web'}\n </button>\n </div>\n )}\n\n {/* Embed snippet + management (published) */}\n {isPublished && (\n <>\n {iframeSnippet ? (\n <div style={{ marginBottom: '16px' }}>\n <div style={{ marginBottom: '6px' }}>\n <span style={{ fontSize: '12px', fontWeight: 600, color: colors.textSecondary }}>Embed snippet</span>\n </div>\n <textarea\n readOnly\n value={iframeSnippet}\n style={{\n width: '100%',\n minHeight: '96px',\n padding: '10px',\n borderRadius: '8px',\n border: `1px solid ${colors.borderDefault}`,\n background: colors.bgSecondary,\n color: colors.textPrimary,\n fontFamily: 'monospace',\n fontSize: '12px',\n resize: 'vertical',\n }}\n />\n <button style={{ ...ghostBtn, marginTop: '8px' }} onClick={() => handleCopy(iframeSnippet)} type=\"button\">\n {copied ? 'Copied ✓' : 'Copy snippet'}\n </button>\n </div>\n ) : (\n <div\n style={{\n fontSize: '12px',\n color: colors.textSecondary,\n background: colors.bgSecondary,\n borderRadius: '8px',\n padding: '10px 12px',\n marginBottom: '16px',\n }}\n >\n The embed link is already active. For security the snippet (which contains the secret key) is only shown\n once at publish time — use <strong>Rotate link</strong> to generate a fresh snippet.\n </div>\n )}\n\n {/* Presentation (published — persisted + re-published to take effect) */}\n <div style={{ marginBottom: '16px' }}>\n <div style={{ fontSize: '12px', fontWeight: 600, color: colors.textSecondary, marginBottom: '8px' }}>\n Presentation\n </div>\n {presentationControls}\n <div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginTop: '10px' }}>\n <button\n style={ghostBtn}\n onClick={handleApplyPresentation}\n disabled={loading || !canManageEmbed}\n type=\"button\"\n >\n {loading ? 'Applying…' : 'Apply changes'}\n </button>\n {presentationSaved && <span style={{ fontSize: '12px', color: colors.textTertiary }}>Updated ✓</span>}\n </div>\n <div style={{ fontSize: '11px', color: colors.textTertiary, marginTop: '6px' }}>\n Changes apply to live embeds within a few seconds — no re-publish needed, and the embed link stays the\n same.\n </div>\n </div>\n\n {/* Rotate/Unpublish are destructive management actions — hide entirely\n when the caller can't manage the embed. */}\n {canManageEmbed && (\n <div style={{ display: 'flex', gap: '8px' }}>\n <button style={ghostBtn} onClick={() => policy && rotateKey(policy.id)} disabled={loading} type=\"button\">\n Rotate link\n </button>\n <button\n style={{ ...ghostBtn, color: colors.statusErrorText, borderColor: colors.statusErrorText }}\n onClick={() => {\n if (\n policy &&\n window.confirm(\n 'Unpublish this dashboard? The embed will stop working immediately for anyone using the link.'\n )\n ) {\n void unpublish(policy.id);\n }\n }}\n disabled={loading}\n type=\"button\"\n >\n Unpublish\n </button>\n </div>\n )}\n </>\n )}\n\n {(localError || error) && (\n <div style={{ marginTop: '12px', fontSize: '12px', color: colors.statusErrorText }}>\n {localError ?? error?.message}\n </div>\n )}\n </div>\n );\n}\n\nexport default EmbedTab;\n"],"mappings":";;;;;;AAuCA,IAAM,IAA4E;CAChF;EAAE,OAAO;EAAS,OAAO;EAAS;CAClC;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAQ,OAAO;EAAuB;CAChD;AAYD,SAAgB,GAAmB,GAAiB,GAAkC;AAKpF,QAJI,MAAU,UAAgB,IAIvB,EAAQ,QAAQ,yBAAyB,GAAQ,MAAgB;EACtE,IAAM,IAAU,EAAI,QAAQ,IAAI,EAC1B,IAAO,MAAY,KAAK,IAAM,EAAI,MAAM,GAAG,EAAQ,EACnD,IAAW,MAAY,KAAK,KAAK,EAAI,MAAM,EAAQ;AAEzD,SAAO,QAAQ,IADH,EAAK,SAAS,IAAI,GAAG,MAAM,IACb,QAAQ,IAAQ,EAAS;GACnD;;AAIJ,SAAS,GAAgB,GAA4B;CACnD,IAAM,IAAU,EAAI,MAAM;AAC1B,KAAI,CAAC,EAAS,QAAO;AACrB,KAAI;EACF,IAAM,IAAM,IAAI,IAAI,EAAQ;AAG5B,SAFI,EAAI,aAAa,YAChB,EAAI,aAAa,MAAM,EAAI,aAAa,OAAQ,EAAI,UAAU,EAAI,OAAa,OAC7E,EAAI;SACL;AACN,SAAO;;;AAIX,SAAgB,EAAS,EAAE,gBAAa,aAAyB;CAC/D,IAAM,EACJ,WACA,WACA,YACA,UACA,gBACA,iBACA,sBACA,sBACA,cACA,kBACE,GAAyB,EAKvB,EAAE,qBAAkB,GAAgB,EACpC,IAAiB,EAAc,EAAuB,uBAAuB,EAE7E,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAgB,KAAqB,EAAmB,EAAE,CAAC,EAC5D,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAQ,KAAa,EAAS,GAAM,EAGrC,CAAC,GAAO,KAAY,EAA4B,QAAQ,EACxD,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,CAAC,GAAc,KAAmB,EAAS,GAAK,EAChD,CAAC,IAAmB,KAAwB,EAAS,GAAM,EAC3D,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAK3D,IAAoB,EAAsB,KAAK,EAC/C,IAAgB,EAA6C,KAAK;AAgBxE,CAdA,QAAgB;AACd,EAAI,KAAa,EAAY,EAAY;IACxC,CAAC,GAAa,EAAY,CAAC,EAG9B,QAAgB;AACV,GAAC,KAAU,EAAkB,YAAY,EAAO,OACpD,EAAkB,UAAU,EAAO,IACnC,EAAS,EAAsB,EAAO,kBAAkB,CAAC,EACzD,EAAa,EAAO,mBAAmB,EACvC,EAAgB,EAAO,mBAAmB;IACzC,CAAC,EAAO,CAAC,EAGZ,cACc;AACV,EAAI,EAAc,WAAS,aAAa,EAAc,QAAQ;IAEhE,EAAE,CACH;CAED,IAAM,IAAc,GAAQ,WAAW,UACjC,IAAU,IAAe,GAAQ,wBAAwB,EAAE,GAAI,GAK/D,IAAgB,QAChB,GAAQ,gBAAsB,GAAmB,EAAO,eAAe,EAAM,GAC1E,MACN,CAAC,GAAQ,EAAM,CAAC,EAEb,UAAkB;AACtB,IAAc,KAAK;EACnB,IAAM,IAAS,GAAgB,EAAY;AAC3C,MAAI,CAAC,GAAQ;AACX,KAAc,uDAAuD;AACrE;;AAEF,MAAI,EAAQ,SAAS,EAAO,EAAE;AAC5B,KAAe,GAAG;AAClB;;AAOF,EALI,KAAe,IACZ,EAAkB,EAAO,IAAI,CAAC,GAAG,GAAS,EAAO,CAAC,GAEvD,GAAmB,MAAS,CAAC,GAAG,GAAM,EAAO,CAAC,EAEhD,EAAe,GAAG;IAGd,KAAgB,MAAmB;AACvC,EAAI,KAAe,IACZ,EACH,EAAO,IACP,EAAQ,QAAQ,MAAM,MAAM,EAAO,CACpC,GAED,GAAmB,MAAS,EAAK,QAAQ,MAAM,MAAM,EAAO,CAAC;IAI3D,IAAgB,YAAY;AAEhC,MADA,EAAc,KAAK,EACf,EAAe,WAAW,GAAG;AAC/B,KAAc,6DAA6D;AAC3E;;AAEF,MAAI,CAAC,GAAU;AACb,KAAc,sDAAsD;AACpE;;AAEF,QAAM,EAAa,GAAa,GAAgB,IAAM;GAAE;GAAO;GAAW;GAAc,CAAC;IAGrF,KAA0B,YAAY;AACrC,QACL,EAAc,KAAK,EACH,MAAM,EAAkB,EAAO,IAAI;GAAE;GAAO;GAAW;GAAc,CAAC,KAEpF,EAAqB,GAAK,EAEtB,EAAc,WAAS,aAAa,EAAc,QAAQ,EAC9D,EAAc,UAAU,iBAAiB,EAAqB,GAAM,EAAE,KAAK;IAIzE,IAAa,OAAO,MAAiB;AACzC,MAAI;AAGF,GAFA,MAAM,UAAU,UAAU,UAAU,EAAK,EACzC,EAAU,GAAK,EACf,iBAAiB,EAAU,GAAM,EAAE,KAAK;UAClC;AACN,KAAc,6CAA6C;;IAIzD,KAAkC;EACtC,SAAS;EACT,cAAc;EACd,QAAQ;EACR,YAAY,EAAO;EACnB,OAAO;EACP,UAAU;EACV,YAAY;EACZ,QAAQ;EACT,EACK,IAAgC;EACpC,SAAS;EACT,cAAc;EACd,QAAQ,aAAa,EAAO;EAC5B,YAAY;EACZ,OAAO,EAAO;EACd,UAAU;EACV,QAAQ;EACT,EACK,KAAkC;EACtC,MAAM;EACN,SAAS;EACT,cAAc;EACd,QAAQ,aAAa,EAAO;EAC5B,YAAY,EAAO;EACnB,OAAO,EAAO;EACd,UAAU;EACX,EACK,KAAmC;EACvC,SAAS;EACT,cAAc;EACd,QAAQ,aAAa,EAAO;EAC5B,YAAY,EAAO;EACnB,OAAO,EAAO;EACd,UAAU;EACV,QAAQ;EACT,EACK,IAAiC;EACrC,SAAS;EACT,KAAK;EACL,YAAY;EACZ,UAAU;EACV,OAAO,EAAO;EACf,EAIK,IACJ,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;GAAQ;YAArE;GACE,kBAAC,SAAD;IAAO,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,gBAAgB;KAAiB,KAAK;KAAO;cAApG,CACE,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAQ,OAAO,EAAO;MAAe;eAAE;KAAY,CAAA,EAC5E,kBAAC,UAAD;KACE,OAAO;KACP,WAAW,MAAM;MAGf,IAAM,IAAQ,EAAE,OAAO;AACvB,OAAI,MAAU,WAAW,MAAU,UAAU,MAAU,WACrD,EAAS,EAAM;;KAGnB,OAAO;eAEN,EAAc,KAAK,MAClB,kBAAC,UAAD;MAAwB,OAAO,EAAI;gBAChC,EAAI;MACE,EAFI,EAAI,MAER,CACT;KACK,CAAA,CACH;;GACR,kBAAC,SAAD;IAAO,OAAO;cAAd,CACE,kBAAC,SAAD;KAAO,MAAK;KAAW,SAAS;KAAW,WAAW,MAAM,EAAa,EAAE,OAAO,QAAQ;KAAI,CAAA,EAC9F,kBAAC,QAAD,EAAA,UAAM,wCAA2C,CAAA,CAC3C;;GACR,kBAAC,SAAD;IAAO,OAAO;cAAd,CACE,kBAAC,SAAD;KAAO,MAAK;KAAW,SAAS;KAAc,WAAW,MAAM,EAAgB,EAAE,OAAO,QAAQ;KAAI,CAAA,EACpG,kBAAC,QAAD,EAAA,UAAM,gDAAmD,CAAA,CACnD;;GACJ;;AAGR,QACE,kBAAC,OAAD;EAAK,OAAO,EAAE,SAAS,aAAa;YAApC;GAEE,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,SAAS;KACT,cAAc;KACd,YAAY,EAAO;KACnB,cAAc;KACf;cATH,CAWE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAQ,YAAY;MAAK,OAAO,EAAO;MAAa;eAAE;KAAwB,CAAA,EACtG,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAQ,OAAO,EAAO;MAAe,WAAW;MAAO;eAC5E,IACG,2FACA;KACA,CAAA,CACF,EAAA,CAAA,EACN,kBAAC,QAAD;KACE,OAAO;MACL,UAAU;MACV,YAAY;MACZ,SAAS;MACT,cAAc;MACd,OAAO;MACP,YAAY,IAAc,EAAO,kBAAkB,EAAO;MAC3D;eAEA,IAAc,cAAc;KACxB,CAAA,CACH;;GAGN,kBAAC,OAAD;IAAK,OAAO,EAAE,cAAc,QAAQ;cAApC;KACE,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAQ,YAAY;OAAK,OAAO,EAAO;OAAe,cAAc;OAAO;gBAAE;MAE/F,CAAA;KACN,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,KAAK;OAAO,cAAc;OAAO;gBAAhE,CACE,kBAAC,SAAD;OACE,OAAO;OACP,aAAY;OACZ,OAAO;OACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;OAC/C,YAAY,MAAM;AAChB,QAAI,EAAE,QAAQ,WAAS,GAAW;;OAEpC,CAAA,EACF,kBAAC,UAAD;OAAQ,OAAO;OAAU,SAAS;OAAW,MAAK;OAAS,UAAU,CAAC;iBAAgB;OAE7E,CAAA,CACL;;KACL,EAAQ,WAAW,IAClB,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAQ,OAAO,EAAO;OAAc;gBAAE;MAExD,CAAA,GAEN,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,eAAe;OAAU,KAAK;OAAO;gBACjE,EAAQ,KAAK,MACZ,kBAAC,OAAD;OAEE,OAAO;QACL,SAAS;QACT,YAAY;QACZ,gBAAgB;QAChB,SAAS;QACT,cAAc;QACd,YAAY,EAAO;QACpB;iBATH,CAWE,kBAAC,QAAD;QAAM,OAAO;SAAE,UAAU;SAAQ,OAAO,EAAO;SAAa;kBAAG;QAAS,CAAA,EACvE,KACC,kBAAC,UAAD;QACE,OAAO;SAAE,GAAG;SAAU,SAAS;SAAW,OAAO,EAAO;SAAiB;QACzE,eAAe,EAAa,EAAE;QAC9B,MAAK;kBACN;QAEQ,CAAA,CAEP;SApBC,EAoBD,CACN;MACE,CAAA;KAEJ;;GAGL,CAAC,KACA,kBAAC,OAAD;IAAK,OAAO,EAAE,cAAc,QAAQ;cAApC,CACE,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAQ,YAAY;MAAK,OAAO,EAAO;MAAe,cAAc;MAAO;eAAE;KAE/F,CAAA,EACL,EACG;;GAIP,CAAC,KACA,kBAAC,OAAD;IAAK,OAAO,EAAE,cAAc,OAAO;cAAnC,CACE,kBAAC,SAAD;KAAO,OAAO;MAAE,SAAS;MAAQ,KAAK;MAAO,YAAY;MAAc,cAAc;MAAQ;eAA7F,CACE,kBAAC,SAAD;MAAO,MAAK;MAAW,SAAS;MAAU,WAAW,MAAM,EAAY,EAAE,OAAO,QAAQ;MAAI,CAAA,EAC5F,kBAAC,QAAD;MAAM,OAAO;OAAE,UAAU;OAAQ,OAAO,EAAO;OAAe;gBAAE;MAGzD,CAAA,CACD;QACR,kBAAC,UAAD;KAAQ,OAAO;KAAY,SAAS;KAAe,UAAU,KAAW,CAAC;KAAgB,MAAK;eAC3F,IAAU,gBAAgB;KACpB,CAAA,CACL;;GAIP,KACC,kBAAA,GAAA,EAAA,UAAA;IACG,IACC,kBAAC,OAAD;KAAK,OAAO,EAAE,cAAc,QAAQ;eAApC;MACE,kBAAC,OAAD;OAAK,OAAO,EAAE,cAAc,OAAO;iBACjC,kBAAC,QAAD;QAAM,OAAO;SAAE,UAAU;SAAQ,YAAY;SAAK,OAAO,EAAO;SAAe;kBAAE;QAAoB,CAAA;OACjG,CAAA;MACN,kBAAC,YAAD;OACE,UAAA;OACA,OAAO;OACP,OAAO;QACL,OAAO;QACP,WAAW;QACX,SAAS;QACT,cAAc;QACd,QAAQ,aAAa,EAAO;QAC5B,YAAY,EAAO;QACnB,OAAO,EAAO;QACd,YAAY;QACZ,UAAU;QACV,QAAQ;QACT;OACD,CAAA;MACF,kBAAC,UAAD;OAAQ,OAAO;QAAE,GAAG;QAAU,WAAW;QAAO;OAAE,eAAe,EAAW,EAAc;OAAE,MAAK;iBAC9F,IAAS,aAAa;OAChB,CAAA;MACL;SAEN,kBAAC,OAAD;KACE,OAAO;MACL,UAAU;MACV,OAAO,EAAO;MACd,YAAY,EAAO;MACnB,cAAc;MACd,SAAS;MACT,cAAc;MACf;eARH;MASC;MAE4B,kBAAC,UAAD,EAAA,UAAQ,eAAoB,CAAA;;MACnD;;IAIR,kBAAC,OAAD;KAAK,OAAO,EAAE,cAAc,QAAQ;eAApC;MACE,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAQ,YAAY;QAAK,OAAO,EAAO;QAAe,cAAc;QAAO;iBAAE;OAE/F,CAAA;MACL;MACD,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAQ,WAAW;QAAQ;iBAArF,CACE,kBAAC,UAAD;QACE,OAAO;QACP,SAAS;QACT,UAAU,KAAW,CAAC;QACtB,MAAK;kBAEJ,IAAU,cAAc;QAClB,CAAA,EACR,MAAqB,kBAAC,QAAD;QAAM,OAAO;SAAE,UAAU;SAAQ,OAAO,EAAO;SAAc;kBAAE;QAAgB,CAAA,CACjG;;MACN,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAQ,OAAO,EAAO;QAAc,WAAW;QAAO;iBAAE;OAG1E,CAAA;MACF;;IAIL,KACC,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,KAAK;MAAO;eAA3C,CACE,kBAAC,UAAD;MAAQ,OAAO;MAAU,eAAe,KAAU,EAAU,EAAO,GAAG;MAAE,UAAU;MAAS,MAAK;gBAAS;MAEhG,CAAA,EACT,kBAAC,UAAD;MACE,OAAO;OAAE,GAAG;OAAU,OAAO,EAAO;OAAiB,aAAa,EAAO;OAAiB;MAC1F,eAAe;AACb,OACE,KACA,OAAO,QACL,+FACD,IAEI,GAAU,EAAO,GAAG;;MAG7B,UAAU;MACV,MAAK;gBACN;MAEQ,CAAA,CACL;;IAEP,EAAA,CAAA;IAGH,KAAc,MACd,kBAAC,OAAD;IAAK,OAAO;KAAE,WAAW;KAAQ,UAAU;KAAQ,OAAO,EAAO;KAAiB;cAC/E,KAAc,GAAO;IAClB,CAAA;GAEJ"}
1
+ {"version":3,"file":"EmbedTab.js","names":[],"sources":["../../../../../src/bigconsole/components/dashboard/ShareDialog/EmbedTab.tsx"],"sourcesContent":["/**\n * EmbedTab — \"Publish to web\" section of the dashboard Share dialog.\n *\n * Owner-facing UI to publish a dashboard as an anonymous-link embed serving LIVE\n * data, manage the allowed parent domains, copy the iframe snippet, rotate the\n * link key, and unpublish. Backed by {@link useDashboardEmbedPolicy}; all\n * mutations are gateway-enforced by `@rbac(manage_embed)` (owner-only).\n *\n * Self-contained (inline styles driven by the dialog's `colors`) so it slots into\n * the existing hand-styled ShareDialog without a broader refactor.\n */\n\nimport React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useDashboardEmbedPolicy, themeChoiceFromPolicy } from '../../../hooks/useDashboardEmbedPolicy';\nimport { BIGCONSOLE_PERMISSIONS } from '../../../../constants/permissions';\n\ninterface DialogColors {\n bgPrimary: string;\n bgSecondary: string;\n bgTertiary: string;\n textPrimary: string;\n textSecondary: string;\n textTertiary: string;\n borderDefault: string;\n actionPrimaryBg: string;\n actionPrimaryBgHover: string;\n statusSuccessBg: string;\n statusErrorText: string;\n}\n\nexport interface EmbedTabProps {\n dashboardId: string;\n colors: DialogColors;\n}\n\n/** Embed color-mode baked into the iframe URL (BOFF-5734). */\ntype EmbedSnippetTheme = 'light' | 'dark' | 'auto';\n\nconst THEME_OPTION_VALUES: ReadonlyArray<EmbedSnippetTheme> = ['light', 'dark', 'auto'];\n\n/**\n * Bake the chosen color mode into an iframe snippet by adding `?theme=` to its\n * `src` URL. `light` is the default and is left implicit so existing/plain\n * snippets stay unchanged. The secret `#key=` fragment is preserved untouched.\n *\n * Inserted via string splicing (before the fragment) rather than `URL`/\n * `URLSearchParams`, which re-serialize ALL params and could subtly re-encode\n * existing ones (e.g. `%20` → `+`). `theme` is a fixed enum, so no encoding is\n * needed; every other part of the URL is passed through byte-for-byte.\n */\nexport function withThemeInSnippet(snippet: string, theme: EmbedSnippetTheme): string {\n if (theme === 'light') return snippet;\n // Require whitespace before `src` so we match the real attribute, never a\n // substring like `data-src`/`srcdoc` (the snippet is backend-generated with\n // each attribute on its own indented line).\n return snippet.replace(/(?<=\\s)src=\"([^\"]*)\"/, (_match, url: string) => {\n const hashIdx = url.indexOf('#');\n const base = hashIdx === -1 ? url : url.slice(0, hashIdx);\n const fragment = hashIdx === -1 ? '' : url.slice(hashIdx);\n const sep = base.includes('?') ? '&' : '?';\n return `src=\"${base}${sep}theme=${theme}${fragment}\"`;\n });\n}\n\n/** Validate an exact https origin (matches the backend allowlist rules). */\nfunction normalizeOrigin(raw: string): string | null {\n const trimmed = raw.trim();\n if (!trimmed) return null;\n try {\n const url = new URL(trimmed);\n if (url.protocol !== 'https:') return null;\n if ((url.pathname !== '' && url.pathname !== '/') || url.search || url.hash) return null;\n return url.origin;\n } catch {\n return null;\n }\n}\n\nexport function EmbedTab({ dashboardId, colors }: EmbedTabProps) {\n const { t } = useI18n();\n const {\n policy,\n secret,\n loading,\n error,\n fetchPolicy,\n publishToWeb,\n applyPresentation,\n setAllowedOrigins,\n rotateKey,\n unpublish,\n } = useDashboardEmbedPolicy();\n\n // All embed policy mutations (publish, rotate, revoke, set-origins, apply\n // presentation) are gateway-enforced by `@rbac(manage_embed)`. Gate the UI on\n // the same permission so the controls degrade gracefully instead of erroring.\n const { hasPermission } = usePermissions();\n const canManageEmbed = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_MANAGE_EMBED);\n\n const [domainInput, setDomainInput] = useState('');\n const [pendingDomains, setPendingDomains] = useState<string[]>([]);\n const [attested, setAttested] = useState(false);\n const [copied, setCopied] = useState(false);\n // Presentation options (BOFF-5735). Defaults mirror the backend column defaults\n // so a fresh publish captures a sensible look without extra clicks.\n const [theme, setTheme] = useState<EmbedSnippetTheme>('light');\n const [showTitle, setShowTitle] = useState(false);\n const [showBranding, setShowBranding] = useState(true);\n const [presentationSaved, setPresentationSaved] = useState(false);\n const [localError, setLocalError] = useState<string | null>(null);\n // Only seed the presentation controls once per policy identity — NOT on every\n // `policy` object change. Otherwise an unrelated mutation (e.g. adding a domain\n // via setAllowedOrigins) would return a fresh policy object and clobber the\n // owner's in-progress, not-yet-applied theme/title/branding edits.\n const syncedPolicyIdRef = useRef<string | null>(null);\n const savedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (dashboardId) fetchPolicy(dashboardId);\n }, [dashboardId, fetchPolicy]);\n\n // Reflect the persisted presentation the first time each policy loads.\n useEffect(() => {\n if (!policy || syncedPolicyIdRef.current === policy.id) return;\n syncedPolicyIdRef.current = policy.id;\n setTheme(themeChoiceFromPolicy(policy.presentationTheme));\n setShowTitle(policy.showDashboardTitle);\n setShowBranding(policy.showBrandingFooter);\n }, [policy]);\n\n // Clear any pending \"Updated ✓\" timer on unmount.\n useEffect(\n () => () => {\n if (savedTimerRef.current) clearTimeout(savedTimerRef.current);\n },\n []\n );\n\n const isPublished = policy?.status === 'ACTIVE';\n const domains = isPublished ? (policy?.allowedParentOrigins ?? []) : pendingDomains;\n\n // The copyable iframe snippet: from the one-time secret if present (fresh\n // publish/rotate), else reconstructed from the locator for an already-published\n // policy (the fragment key is only available in the one-time secret).\n const iframeSnippet = useMemo(() => {\n if (secret?.iframeSnippet) return withThemeInSnippet(secret.iframeSnippet, theme);\n return null;\n }, [secret, theme]);\n\n const addDomain = () => {\n setLocalError(null);\n const origin = normalizeOrigin(domainInput);\n if (!origin) {\n setLocalError(\n t('bigconsole.dashboards.embed.error.invalidOrigin', 'Enter a valid https origin, e.g. https://example.com')\n );\n return;\n }\n if (domains.includes(origin)) {\n setDomainInput('');\n return;\n }\n if (isPublished && policy) {\n void setAllowedOrigins(policy.id, [...domains, origin]);\n } else {\n setPendingDomains((prev) => [...prev, origin]);\n }\n setDomainInput('');\n };\n\n const removeDomain = (origin: string) => {\n if (isPublished && policy) {\n void setAllowedOrigins(\n policy.id,\n domains.filter((d) => d !== origin)\n );\n } else {\n setPendingDomains((prev) => prev.filter((d) => d !== origin));\n }\n };\n\n const handlePublish = async () => {\n setLocalError(null);\n if (pendingDomains.length === 0) {\n setLocalError(\n t('bigconsole.dashboards.embed.error.noDomains', 'Add at least one allowed website domain before publishing.')\n );\n return;\n }\n if (!attested) {\n setLocalError(\n t('bigconsole.dashboards.embed.error.notAttested', 'Please confirm this dashboard is safe to be public.')\n );\n return;\n }\n await publishToWeb(dashboardId, pendingDomains, true, { theme, showTitle, showBranding });\n };\n\n const handleApplyPresentation = async () => {\n if (!policy) return;\n setLocalError(null);\n const updated = await applyPresentation(policy.id, { theme, showTitle, showBranding });\n if (updated) {\n setPresentationSaved(true);\n // Clear any in-flight timer so rapid re-clicks don't hide the confirmation early.\n if (savedTimerRef.current) clearTimeout(savedTimerRef.current);\n savedTimerRef.current = setTimeout(() => setPresentationSaved(false), 1500);\n }\n };\n\n const handleCopy = async (text: string) => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n } catch {\n setLocalError(t('bigconsole.dashboards.embed.error.copyFailed', 'Could not copy — select and copy manually.'));\n }\n };\n\n const primaryBtn: React.CSSProperties = {\n padding: '8px 14px',\n borderRadius: '8px',\n border: 'none',\n background: colors.actionPrimaryBg,\n color: '#fff',\n fontSize: '13px',\n fontWeight: 600,\n cursor: 'pointer',\n };\n const ghostBtn: React.CSSProperties = {\n padding: '8px 14px',\n borderRadius: '8px',\n border: `1px solid ${colors.borderDefault}`,\n background: 'transparent',\n color: colors.textPrimary,\n fontSize: '13px',\n cursor: 'pointer',\n };\n const inputStyle: React.CSSProperties = {\n flex: 1,\n padding: '8px 10px',\n borderRadius: '8px',\n border: `1px solid ${colors.borderDefault}`,\n background: colors.bgSecondary,\n color: colors.textPrimary,\n fontSize: '13px',\n };\n const selectStyle: React.CSSProperties = {\n padding: '4px 8px',\n borderRadius: '6px',\n border: `1px solid ${colors.borderDefault}`,\n background: colors.bgSecondary,\n color: colors.textPrimary,\n fontSize: '12px',\n cursor: 'pointer',\n };\n const toggleRow: React.CSSProperties = {\n display: 'flex',\n gap: '8px',\n alignItems: 'flex-start',\n fontSize: '12px',\n color: colors.textSecondary,\n };\n\n const themeOptions = useMemo(\n () =>\n THEME_OPTION_VALUES.map((value) => ({\n value,\n label:\n value === 'light'\n ? t('bigconsole.dashboards.embed.theme.option.light', 'Light')\n : value === 'dark'\n ? t('bigconsole.dashboards.embed.theme.option.dark', 'Dark')\n : t('bigconsole.dashboards.embed.theme.option.auto', 'Auto (match viewer)'),\n })),\n [t]\n );\n\n // Presentation controls (BOFF-5735) — shared by the pre-publish and published\n // states. Local state drives both the persisted policy and the snippet `?theme=`.\n const presentationControls = (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>\n <label style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '8px' }}>\n <span style={{ fontSize: '12px', color: colors.textSecondary }}>\n {t('bigconsole.dashboards.embed.theme.label', 'Theme')}\n </span>\n <select\n value={theme}\n onChange={(e) => {\n // The options are fixed, but narrow instead of casting so an\n // unexpected value can never slip into state.\n const value = e.target.value;\n if (value === 'light' || value === 'dark' || value === 'auto') {\n setTheme(value);\n }\n }}\n style={selectStyle}\n >\n {themeOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n </label>\n <label style={toggleRow}>\n <input type=\"checkbox\" checked={showTitle} onChange={(e) => setShowTitle(e.target.checked)} />\n <span>{t('bigconsole.dashboards.embed.presentation.showTitle', 'Show the dashboard title as a header')}</span>\n </label>\n <label style={toggleRow}>\n <input type=\"checkbox\" checked={showBranding} onChange={(e) => setShowBranding(e.target.checked)} />\n <span>\n {t('bigconsole.dashboards.embed.presentation.showBranding', 'Show a subtle “Powered by BigConsole” footer')}\n </span>\n </label>\n </div>\n );\n\n return (\n <div style={{ padding: '20px 24px' }}>\n {/* Status banner */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '12px 14px',\n borderRadius: '10px',\n background: colors.bgSecondary,\n marginBottom: '16px',\n }}\n >\n <div>\n <div style={{ fontSize: '14px', fontWeight: 600, color: colors.textPrimary }}>\n {t('bigconsole.dashboards.embed.heading', 'Publish to the web')}\n </div>\n <div style={{ fontSize: '12px', color: colors.textSecondary, marginTop: '2px' }}>\n {isPublished\n ? t(\n 'bigconsole.dashboards.embed.status.liveDescription',\n 'This dashboard is live. Anyone with the link on an allowed website sees its live data.'\n )\n : t(\n 'bigconsole.dashboards.embed.status.privateDescription',\n 'Embed this dashboard on your website. Viewers see live data — no login required.'\n )}\n </div>\n </div>\n <span\n style={{\n fontSize: '11px',\n fontWeight: 700,\n padding: '3px 8px',\n borderRadius: '999px',\n color: '#fff',\n background: isPublished ? colors.statusSuccessBg : colors.textTertiary,\n }}\n >\n {isPublished\n ? t('bigconsole.dashboards.embed.badge.published', 'PUBLISHED')\n : t('bigconsole.dashboards.embed.badge.private', 'PRIVATE')}\n </span>\n </div>\n\n {/* Allowed domains */}\n <div style={{ marginBottom: '16px' }}>\n <div style={{ fontSize: '12px', fontWeight: 600, color: colors.textSecondary, marginBottom: '6px' }}>\n {t('bigconsole.dashboards.embed.domains.label', 'Allowed website domains')}\n </div>\n <div style={{ display: 'flex', gap: '8px', marginBottom: '8px' }}>\n <input\n style={inputStyle}\n placeholder={t('bigconsole.dashboards.embed.domains.placeholder', 'https://yoursite.com')}\n value={domainInput}\n onChange={(e) => setDomainInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') addDomain();\n }}\n />\n <button style={ghostBtn} onClick={addDomain} type=\"button\" disabled={!canManageEmbed}>\n {t('bigconsole.dashboards.embed.domains.add', 'Add')}\n </button>\n </div>\n {domains.length === 0 ? (\n <div style={{ fontSize: '12px', color: colors.textTertiary }}>\n {t(\n 'bigconsole.dashboards.embed.domains.empty',\n 'No domains yet. Only listed https domains may frame this dashboard.'\n )}\n </div>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>\n {domains.map((d) => (\n <div\n key={d}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '6px 10px',\n borderRadius: '8px',\n background: colors.bgTertiary,\n }}\n >\n <span style={{ fontSize: '13px', color: colors.textPrimary }}>{d}</span>\n {canManageEmbed && (\n <button\n style={{ ...ghostBtn, padding: '2px 8px', color: colors.statusErrorText }}\n onClick={() => removeDomain(d)}\n type=\"button\"\n >\n {t('bigconsole.dashboards.embed.domains.remove', 'Remove')}\n </button>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Presentation (unpublished — captured into the publish) */}\n {!isPublished && (\n <div style={{ marginBottom: '16px' }}>\n <div style={{ fontSize: '12px', fontWeight: 600, color: colors.textSecondary, marginBottom: '8px' }}>\n {t('bigconsole.dashboards.embed.presentation.heading', 'Presentation')}\n </div>\n {presentationControls}\n </div>\n )}\n\n {/* Publish CTA (unpublished) */}\n {!isPublished && (\n <div style={{ marginBottom: '8px' }}>\n <label style={{ display: 'flex', gap: '8px', alignItems: 'flex-start', marginBottom: '12px' }}>\n <input type=\"checkbox\" checked={attested} onChange={(e) => setAttested(e.target.checked)} />\n <span style={{ fontSize: '12px', color: colors.textSecondary }}>\n {t(\n 'bigconsole.dashboards.embed.attestation',\n 'I understand this makes the dashboard’s live data visible to anyone with the link, and I confirm it is safe to be public.'\n )}\n </span>\n </label>\n <button style={primaryBtn} onClick={handlePublish} disabled={loading || !canManageEmbed} type=\"button\">\n {loading\n ? t('bigconsole.dashboards.embed.publishing', 'Publishing…')\n : t('bigconsole.dashboards.embed.publish', 'Publish to web')}\n </button>\n </div>\n )}\n\n {/* Embed snippet + management (published) */}\n {isPublished && (\n <>\n {iframeSnippet ? (\n <div style={{ marginBottom: '16px' }}>\n <div style={{ marginBottom: '6px' }}>\n <span style={{ fontSize: '12px', fontWeight: 600, color: colors.textSecondary }}>\n {t('bigconsole.dashboards.embed.snippet.label', 'Embed snippet')}\n </span>\n </div>\n <textarea\n readOnly\n value={iframeSnippet}\n style={{\n width: '100%',\n minHeight: '96px',\n padding: '10px',\n borderRadius: '8px',\n border: `1px solid ${colors.borderDefault}`,\n background: colors.bgSecondary,\n color: colors.textPrimary,\n fontFamily: 'monospace',\n fontSize: '12px',\n resize: 'vertical',\n }}\n />\n <button style={{ ...ghostBtn, marginTop: '8px' }} onClick={() => handleCopy(iframeSnippet)} type=\"button\">\n {copied\n ? t('bigconsole.dashboards.embed.snippet.copied', 'Copied ✓')\n : t('bigconsole.dashboards.embed.snippet.copy', 'Copy snippet')}\n </button>\n </div>\n ) : (\n <div\n style={{\n fontSize: '12px',\n color: colors.textSecondary,\n background: colors.bgSecondary,\n borderRadius: '8px',\n padding: '10px 12px',\n marginBottom: '16px',\n }}\n >\n {t(\n 'bigconsole.dashboards.embed.snippet.alreadyActive',\n 'The embed link is already active. For security the snippet (which contains the secret key) is only shown once at publish time — use {{rotateLabel}} to generate a fresh snippet.',\n { rotateLabel: t('bigconsole.dashboards.embed.rotate', 'Rotate link') }\n )}\n </div>\n )}\n\n {/* Presentation (published — persisted + re-published to take effect) */}\n <div style={{ marginBottom: '16px' }}>\n <div style={{ fontSize: '12px', fontWeight: 600, color: colors.textSecondary, marginBottom: '8px' }}>\n {t('bigconsole.dashboards.embed.presentation.heading', 'Presentation')}\n </div>\n {presentationControls}\n <div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginTop: '10px' }}>\n <button\n style={ghostBtn}\n onClick={handleApplyPresentation}\n disabled={loading || !canManageEmbed}\n type=\"button\"\n >\n {loading\n ? t('bigconsole.dashboards.embed.applying', 'Applying…')\n : t('bigconsole.dashboards.embed.apply', 'Apply changes')}\n </button>\n {presentationSaved && (\n <span style={{ fontSize: '12px', color: colors.textTertiary }}>\n {t('bigconsole.dashboards.embed.updated', 'Updated ✓')}\n </span>\n )}\n </div>\n <div style={{ fontSize: '11px', color: colors.textTertiary, marginTop: '6px' }}>\n {t(\n 'bigconsole.dashboards.embed.presentation.applyHelp',\n 'Changes apply to live embeds within a few seconds — no re-publish needed, and the embed link stays the same.'\n )}\n </div>\n </div>\n\n {/* Rotate/Unpublish are destructive management actions — hide entirely\n when the caller can't manage the embed. */}\n {canManageEmbed && (\n <div style={{ display: 'flex', gap: '8px' }}>\n <button style={ghostBtn} onClick={() => policy && rotateKey(policy.id)} disabled={loading} type=\"button\">\n {t('bigconsole.dashboards.embed.rotate', 'Rotate link')}\n </button>\n <button\n style={{ ...ghostBtn, color: colors.statusErrorText, borderColor: colors.statusErrorText }}\n onClick={() => {\n if (\n policy &&\n window.confirm(\n t(\n 'bigconsole.dashboards.embed.unpublishConfirm',\n 'Unpublish this dashboard? The embed will stop working immediately for anyone using the link.'\n )\n )\n ) {\n void unpublish(policy.id);\n }\n }}\n disabled={loading}\n type=\"button\"\n >\n {t('bigconsole.dashboards.embed.unpublish', 'Unpublish')}\n </button>\n </div>\n )}\n </>\n )}\n\n {(localError || error) && (\n <div style={{ marginTop: '12px', fontSize: '12px', color: colors.statusErrorText }}>\n {localError ?? error?.message}\n </div>\n )}\n </div>\n );\n}\n\nexport default EmbedTab;\n"],"mappings":";;;;;;;AAwCA,IAAM,IAAwD;CAAC;CAAS;CAAQ;CAAO;AAYvF,SAAgB,EAAmB,GAAiB,GAAkC;AAKpF,QAJI,MAAU,UAAgB,IAIvB,EAAQ,QAAQ,yBAAyB,GAAQ,MAAgB;EACtE,IAAM,IAAU,EAAI,QAAQ,IAAI,EAC1B,IAAO,MAAY,KAAK,IAAM,EAAI,MAAM,GAAG,EAAQ,EACnD,IAAW,MAAY,KAAK,KAAK,EAAI,MAAM,EAAQ;AAEzD,SAAO,QAAQ,IADH,EAAK,SAAS,IAAI,GAAG,MAAM,IACb,QAAQ,IAAQ,EAAS;GACnD;;AAIJ,SAAS,EAAgB,GAA4B;CACnD,IAAM,IAAU,EAAI,MAAM;AAC1B,KAAI,CAAC,EAAS,QAAO;AACrB,KAAI;EACF,IAAM,IAAM,IAAI,IAAI,EAAQ;AAG5B,SAFI,EAAI,aAAa,YAChB,EAAI,aAAa,MAAM,EAAI,aAAa,OAAQ,EAAI,UAAU,EAAI,OAAa,OAC7E,EAAI;SACL;AACN,SAAO;;;AAIX,SAAgB,EAAS,EAAE,gBAAa,aAAyB;CAC/D,IAAM,EAAE,SAAM,GAAS,EACjB,EACJ,WACA,WACA,YACA,UACA,gBACA,iBACA,sBACA,sBACA,cACA,kBACE,GAAyB,EAKvB,EAAE,sBAAkB,GAAgB,EACpC,IAAiB,GAAc,EAAuB,uBAAuB,EAE7E,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAgB,KAAqB,EAAmB,EAAE,CAAC,EAC5D,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,IAAQ,KAAa,EAAS,GAAM,EAGrC,CAAC,GAAO,KAAY,EAA4B,QAAQ,EACxD,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,CAAC,GAAc,KAAmB,EAAS,GAAK,EAChD,CAAC,GAAmB,KAAwB,EAAS,GAAM,EAC3D,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAK3D,IAAoB,EAAsB,KAAK,EAC/C,IAAgB,EAA6C,KAAK;AAgBxE,CAdA,QAAgB;AACd,EAAI,KAAa,EAAY,EAAY;IACxC,CAAC,GAAa,EAAY,CAAC,EAG9B,QAAgB;AACV,GAAC,KAAU,EAAkB,YAAY,EAAO,OACpD,EAAkB,UAAU,EAAO,IACnC,EAAS,EAAsB,EAAO,kBAAkB,CAAC,EACzD,EAAa,EAAO,mBAAmB,EACvC,EAAgB,EAAO,mBAAmB;IACzC,CAAC,EAAO,CAAC,EAGZ,cACc;AACV,EAAI,EAAc,WAAS,aAAa,EAAc,QAAQ;IAEhE,EAAE,CACH;CAED,IAAM,IAAc,GAAQ,WAAW,UACjC,IAAU,IAAe,GAAQ,wBAAwB,EAAE,GAAI,GAK/D,IAAgB,QAChB,GAAQ,gBAAsB,EAAmB,EAAO,eAAe,EAAM,GAC1E,MACN,CAAC,GAAQ,EAAM,CAAC,EAEb,UAAkB;AACtB,IAAc,KAAK;EACnB,IAAM,IAAS,EAAgB,EAAY;AAC3C,MAAI,CAAC,GAAQ;AACX,KACE,EAAE,mDAAmD,uDAAuD,CAC7G;AACD;;AAEF,MAAI,EAAQ,SAAS,EAAO,EAAE;AAC5B,KAAe,GAAG;AAClB;;AAOF,EALI,KAAe,IACZ,EAAkB,EAAO,IAAI,CAAC,GAAG,GAAS,EAAO,CAAC,GAEvD,GAAmB,MAAS,CAAC,GAAG,GAAM,EAAO,CAAC,EAEhD,EAAe,GAAG;IAGd,MAAgB,MAAmB;AACvC,EAAI,KAAe,IACZ,EACH,EAAO,IACP,EAAQ,QAAQ,MAAM,MAAM,EAAO,CACpC,GAED,GAAmB,MAAS,EAAK,QAAQ,MAAM,MAAM,EAAO,CAAC;IAI3D,KAAgB,YAAY;AAEhC,MADA,EAAc,KAAK,EACf,EAAe,WAAW,GAAG;AAC/B,KACE,EAAE,+CAA+C,6DAA6D,CAC/G;AACD;;AAEF,MAAI,CAAC,GAAU;AACb,KACE,EAAE,iDAAiD,sDAAsD,CAC1G;AACD;;AAEF,QAAM,EAAa,GAAa,GAAgB,IAAM;GAAE;GAAO;GAAW;GAAc,CAAC;IAGrF,KAA0B,YAAY;AACrC,QACL,EAAc,KAAK,EACH,MAAM,EAAkB,EAAO,IAAI;GAAE;GAAO;GAAW;GAAc,CAAC,KAEpF,EAAqB,GAAK,EAEtB,EAAc,WAAS,aAAa,EAAc,QAAQ,EAC9D,EAAc,UAAU,iBAAiB,EAAqB,GAAM,EAAE,KAAK;IAIzE,KAAa,OAAO,MAAiB;AACzC,MAAI;AAGF,GAFA,MAAM,UAAU,UAAU,UAAU,EAAK,EACzC,EAAU,GAAK,EACf,iBAAiB,EAAU,GAAM,EAAE,KAAK;UAClC;AACN,KAAc,EAAE,gDAAgD,6CAA6C,CAAC;;IAI5G,KAAkC;EACtC,SAAS;EACT,cAAc;EACd,QAAQ;EACR,YAAY,EAAO;EACnB,OAAO;EACP,UAAU;EACV,YAAY;EACZ,QAAQ;EACT,EACK,IAAgC;EACpC,SAAS;EACT,cAAc;EACd,QAAQ,aAAa,EAAO;EAC5B,YAAY;EACZ,OAAO,EAAO;EACd,UAAU;EACV,QAAQ;EACT,EACK,KAAkC;EACtC,MAAM;EACN,SAAS;EACT,cAAc;EACd,QAAQ,aAAa,EAAO;EAC5B,YAAY,EAAO;EACnB,OAAO,EAAO;EACd,UAAU;EACX,EACK,KAAmC;EACvC,SAAS;EACT,cAAc;EACd,QAAQ,aAAa,EAAO;EAC5B,YAAY,EAAO;EACnB,OAAO,EAAO;EACd,UAAU;EACV,QAAQ;EACT,EACK,IAAiC;EACrC,SAAS;EACT,KAAK;EACL,YAAY;EACZ,UAAU;EACV,OAAO,EAAO;EACf,EAEK,KAAe,QAEjB,EAAoB,KAAK,OAAW;EAClC;EACA,OACE,MAAU,UACN,EAAE,kDAAkD,QAAQ,GAC5D,MAAU,SACR,EAAE,iDAAiD,OAAO,GAC1D,EAAE,iDAAiD,sBAAsB;EAClF,EAAE,EACL,CAAC,EAAE,CACJ,EAIK,IACJ,kBAAC,OAAD;EAAK,OAAO;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;GAAQ;YAArE;GACE,kBAAC,SAAD;IAAO,OAAO;KAAE,SAAS;KAAQ,YAAY;KAAU,gBAAgB;KAAiB,KAAK;KAAO;cAApG,CACE,kBAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAQ,OAAO,EAAO;MAAe;eAC3D,EAAE,2CAA2C,QAAQ;KACjD,CAAA,EACP,kBAAC,UAAD;KACE,OAAO;KACP,WAAW,MAAM;MAGf,IAAM,IAAQ,EAAE,OAAO;AACvB,OAAI,MAAU,WAAW,MAAU,UAAU,MAAU,WACrD,EAAS,EAAM;;KAGnB,OAAO;eAEN,GAAa,KAAK,MACjB,kBAAC,UAAD;MAAwB,OAAO,EAAI;gBAChC,EAAI;MACE,EAFI,EAAI,MAER,CACT;KACK,CAAA,CACH;;GACR,kBAAC,SAAD;IAAO,OAAO;cAAd,CACE,kBAAC,SAAD;KAAO,MAAK;KAAW,SAAS;KAAW,WAAW,MAAM,EAAa,EAAE,OAAO,QAAQ;KAAI,CAAA,EAC9F,kBAAC,QAAD,EAAA,UAAO,EAAE,sDAAsD,uCAAuC,EAAQ,CAAA,CACxG;;GACR,kBAAC,SAAD;IAAO,OAAO;cAAd,CACE,kBAAC,SAAD;KAAO,MAAK;KAAW,SAAS;KAAc,WAAW,MAAM,EAAgB,EAAE,OAAO,QAAQ;KAAI,CAAA,EACpG,kBAAC,QAAD,EAAA,UACG,EAAE,yDAAyD,+CAA+C,EACtG,CAAA,CACD;;GACJ;;AAGR,QACE,kBAAC,OAAD;EAAK,OAAO,EAAE,SAAS,aAAa;YAApC;GAEE,kBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,YAAY;KACZ,gBAAgB;KAChB,SAAS;KACT,cAAc;KACd,YAAY,EAAO;KACnB,cAAc;KACf;cATH,CAWE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAQ,YAAY;MAAK,OAAO,EAAO;MAAa;eACzE,EAAE,uCAAuC,qBAAqB;KAC3D,CAAA,EACN,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAQ,OAAO,EAAO;MAAe,WAAW;MAAO;eAC5E,IACG,EACE,sDACA,yFACD,GACD,EACE,yDACA,mFACD;KACD,CAAA,CACF,EAAA,CAAA,EACN,kBAAC,QAAD;KACE,OAAO;MACL,UAAU;MACV,YAAY;MACZ,SAAS;MACT,cAAc;MACd,OAAO;MACP,YAAY,IAAc,EAAO,kBAAkB,EAAO;MAC3D;eAEA,IACG,EAAE,+CAA+C,YAAY,GAC7D,EAAE,6CAA6C,UAAU;KACxD,CAAA,CACH;;GAGN,kBAAC,OAAD;IAAK,OAAO,EAAE,cAAc,QAAQ;cAApC;KACE,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAQ,YAAY;OAAK,OAAO,EAAO;OAAe,cAAc;OAAO;gBAChG,EAAE,6CAA6C,0BAA0B;MACtE,CAAA;KACN,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,KAAK;OAAO,cAAc;OAAO;gBAAhE,CACE,kBAAC,SAAD;OACE,OAAO;OACP,aAAa,EAAE,mDAAmD,uBAAuB;OACzF,OAAO;OACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;OAC/C,YAAY,MAAM;AAChB,QAAI,EAAE,QAAQ,WAAS,GAAW;;OAEpC,CAAA,EACF,kBAAC,UAAD;OAAQ,OAAO;OAAU,SAAS;OAAW,MAAK;OAAS,UAAU,CAAC;iBACnE,EAAE,2CAA2C,MAAM;OAC7C,CAAA,CACL;;KACL,EAAQ,WAAW,IAClB,kBAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAQ,OAAO,EAAO;OAAc;gBACzD,EACC,6CACA,sEACD;MACG,CAAA,GAEN,kBAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,eAAe;OAAU,KAAK;OAAO;gBACjE,EAAQ,KAAK,MACZ,kBAAC,OAAD;OAEE,OAAO;QACL,SAAS;QACT,YAAY;QACZ,gBAAgB;QAChB,SAAS;QACT,cAAc;QACd,YAAY,EAAO;QACpB;iBATH,CAWE,kBAAC,QAAD;QAAM,OAAO;SAAE,UAAU;SAAQ,OAAO,EAAO;SAAa;kBAAG;QAAS,CAAA,EACvE,KACC,kBAAC,UAAD;QACE,OAAO;SAAE,GAAG;SAAU,SAAS;SAAW,OAAO,EAAO;SAAiB;QACzE,eAAe,GAAa,EAAE;QAC9B,MAAK;kBAEJ,EAAE,8CAA8C,SAAS;QACnD,CAAA,CAEP;SApBC,EAoBD,CACN;MACE,CAAA;KAEJ;;GAGL,CAAC,KACA,kBAAC,OAAD;IAAK,OAAO,EAAE,cAAc,QAAQ;cAApC,CACE,kBAAC,OAAD;KAAK,OAAO;MAAE,UAAU;MAAQ,YAAY;MAAK,OAAO,EAAO;MAAe,cAAc;MAAO;eAChG,EAAE,oDAAoD,eAAe;KAClE,CAAA,EACL,EACG;;GAIP,CAAC,KACA,kBAAC,OAAD;IAAK,OAAO,EAAE,cAAc,OAAO;cAAnC,CACE,kBAAC,SAAD;KAAO,OAAO;MAAE,SAAS;MAAQ,KAAK;MAAO,YAAY;MAAc,cAAc;MAAQ;eAA7F,CACE,kBAAC,SAAD;MAAO,MAAK;MAAW,SAAS;MAAU,WAAW,MAAM,EAAY,EAAE,OAAO,QAAQ;MAAI,CAAA,EAC5F,kBAAC,QAAD;MAAM,OAAO;OAAE,UAAU;OAAQ,OAAO,EAAO;OAAe;gBAC3D,EACC,2CACA,4HACD;MACI,CAAA,CACD;QACR,kBAAC,UAAD;KAAQ,OAAO;KAAY,SAAS;KAAe,UAAU,KAAW,CAAC;KAAgB,MAAK;eAC3F,IACG,EAAE,0CAA0C,cAAc,GAC1D,EAAE,uCAAuC,iBAAiB;KACvD,CAAA,CACL;;GAIP,KACC,kBAAA,GAAA,EAAA,UAAA;IACG,IACC,kBAAC,OAAD;KAAK,OAAO,EAAE,cAAc,QAAQ;eAApC;MACE,kBAAC,OAAD;OAAK,OAAO,EAAE,cAAc,OAAO;iBACjC,kBAAC,QAAD;QAAM,OAAO;SAAE,UAAU;SAAQ,YAAY;SAAK,OAAO,EAAO;SAAe;kBAC5E,EAAE,6CAA6C,gBAAgB;QAC3D,CAAA;OACH,CAAA;MACN,kBAAC,YAAD;OACE,UAAA;OACA,OAAO;OACP,OAAO;QACL,OAAO;QACP,WAAW;QACX,SAAS;QACT,cAAc;QACd,QAAQ,aAAa,EAAO;QAC5B,YAAY,EAAO;QACnB,OAAO,EAAO;QACd,YAAY;QACZ,UAAU;QACV,QAAQ;QACT;OACD,CAAA;MACF,kBAAC,UAAD;OAAQ,OAAO;QAAE,GAAG;QAAU,WAAW;QAAO;OAAE,eAAe,GAAW,EAAc;OAAE,MAAK;iBAC9F,KACG,EAAE,8CAA8C,WAAW,GAC3D,EAAE,4CAA4C,eAAe;OAC1D,CAAA;MACL;SAEN,kBAAC,OAAD;KACE,OAAO;MACL,UAAU;MACV,OAAO,EAAO;MACd,YAAY,EAAO;MACnB,cAAc;MACd,SAAS;MACT,cAAc;MACf;eAEA,EACC,qDACA,oLACA,EAAE,aAAa,EAAE,sCAAsC,cAAc,EAAE,CACxE;KACG,CAAA;IAIR,kBAAC,OAAD;KAAK,OAAO,EAAE,cAAc,QAAQ;eAApC;MACE,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAQ,YAAY;QAAK,OAAO,EAAO;QAAe,cAAc;QAAO;iBAChG,EAAE,oDAAoD,eAAe;OAClE,CAAA;MACL;MACD,kBAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAQ,WAAW;QAAQ;iBAArF,CACE,kBAAC,UAAD;QACE,OAAO;QACP,SAAS;QACT,UAAU,KAAW,CAAC;QACtB,MAAK;kBAEJ,IACG,EAAE,wCAAwC,YAAY,GACtD,EAAE,qCAAqC,gBAAgB;QACpD,CAAA,EACR,KACC,kBAAC,QAAD;QAAM,OAAO;SAAE,UAAU;SAAQ,OAAO,EAAO;SAAc;kBAC1D,EAAE,uCAAuC,YAAY;QACjD,CAAA,CAEL;;MACN,kBAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAQ,OAAO,EAAO;QAAc,WAAW;QAAO;iBAC3E,EACC,sDACA,+GACD;OACG,CAAA;MACF;;IAIL,KACC,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,KAAK;MAAO;eAA3C,CACE,kBAAC,UAAD;MAAQ,OAAO;MAAU,eAAe,KAAU,EAAU,EAAO,GAAG;MAAE,UAAU;MAAS,MAAK;gBAC7F,EAAE,sCAAsC,cAAc;MAChD,CAAA,EACT,kBAAC,UAAD;MACE,OAAO;OAAE,GAAG;OAAU,OAAO,EAAO;OAAiB,aAAa,EAAO;OAAiB;MAC1F,eAAe;AACb,OACE,KACA,OAAO,QACL,EACE,gDACA,+FACD,CACF,IAEI,GAAU,EAAO,GAAG;;MAG7B,UAAU;MACV,MAAK;gBAEJ,EAAE,yCAAyC,YAAY;MACjD,CAAA,CACL;;IAEP,EAAA,CAAA;IAGH,KAAc,MACd,kBAAC,OAAD;IAAK,OAAO;KAAE,WAAW;KAAQ,UAAU;KAAQ,OAAO,EAAO;KAAiB;cAC/E,KAAc,GAAO;IAClB,CAAA;GAEJ"}
@@ -100,21 +100,30 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
100
100
  left: 0,
101
101
  right: 0,
102
102
  bottom: 0,
103
- backgroundColor: "rgba(0, 0, 0, 0.6)",
103
+ backgroundColor: "var(--color-overlay-scrim)",
104
104
  display: "flex",
105
105
  alignItems: "center",
106
106
  justifyContent: "center",
107
+ padding: "var(--space-formGap)",
107
108
  zIndex: 99999,
108
109
  isolation: "isolate"
109
110
  },
110
111
  onClick: g,
112
+ role: "presentation",
111
113
  children: /* @__PURE__ */ c("div", {
114
+ role: "dialog",
115
+ "aria-modal": "true",
116
+ "aria-labelledby": "share-dialog-title",
117
+ "data-testid": "share-dialog",
112
118
  style: {
113
- width: "520px",
114
- maxHeight: "80vh",
119
+ width: "100%",
120
+ maxWidth: "520px",
121
+ maxHeight: "85vh",
122
+ display: "flex",
123
+ flexDirection: "column",
115
124
  backgroundColor: G.bgPrimary,
116
- borderRadius: "12px",
117
- boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3)",
125
+ borderRadius: "var(--radius-card)",
126
+ boxShadow: "var(--shadow-elevation-3)",
118
127
  overflow: "hidden",
119
128
  position: "relative",
120
129
  zIndex: 1e5,
@@ -124,6 +133,7 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
124
133
  children: [
125
134
  /* @__PURE__ */ c("div", {
126
135
  style: {
136
+ flexShrink: 0,
127
137
  padding: "20px 24px",
128
138
  borderBottom: `1px solid ${G.borderDefault}`,
129
139
  display: "flex",
@@ -131,7 +141,8 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
131
141
  justifyContent: "space-between",
132
142
  backgroundColor: G.bgPrimary
133
143
  },
134
- children: [/* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("h3", {
144
+ children: [/* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("h2", {
145
+ id: "share-dialog-title",
135
146
  style: {
136
147
  margin: 0,
137
148
  fontSize: "18px",
@@ -161,7 +172,10 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
161
172
  })]
162
173
  }),
163
174
  /* @__PURE__ */ s("div", {
175
+ role: "tablist",
176
+ "aria-label": "Share options",
164
177
  style: {
178
+ flexShrink: 0,
165
179
  display: "flex",
166
180
  gap: "4px",
167
181
  padding: "0 24px",
@@ -170,6 +184,8 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
170
184
  },
171
185
  children: ["people", "embed"].map((e) => /* @__PURE__ */ s("button", {
172
186
  type: "button",
187
+ role: "tab",
188
+ "aria-selected": I === e,
173
189
  onClick: () => L(e),
174
190
  style: {
175
191
  padding: "10px 12px",
@@ -185,39 +201,29 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
185
201
  }, e))
186
202
  }),
187
203
  I === "embed" && /* @__PURE__ */ s("div", {
188
- style: {
189
- overflowY: "auto",
190
- maxHeight: "calc(80vh - 160px)",
191
- backgroundColor: G.bgPrimary
192
- },
204
+ className: "min-h-0 flex-1 overflow-y-auto",
205
+ style: { backgroundColor: G.bgPrimary },
193
206
  children: /* @__PURE__ */ s(n, {
194
207
  dashboardId: f,
195
208
  colors: G
196
209
  })
197
210
  }),
198
211
  I === "people" && /* @__PURE__ */ c("div", {
199
- style: {
200
- padding: "24px",
201
- overflowY: "auto",
202
- maxHeight: "calc(80vh - 200px)",
203
- backgroundColor: G.bgPrimary
204
- },
212
+ className: "flex min-h-0 flex-1 flex-col gap-section-gap overflow-y-auto p-card-padding",
213
+ style: { backgroundColor: G.bgPrimary },
205
214
  children: [
206
- /* @__PURE__ */ c("div", {
207
- style: { marginBottom: "24px" },
208
- children: [/* @__PURE__ */ s("label", {
209
- style: {
210
- display: "block",
211
- marginBottom: "8px",
212
- fontSize: "13px",
213
- fontWeight: 600,
214
- color: G.textPrimary
215
- },
216
- children: "Share Link"
215
+ /* @__PURE__ */ c("section", {
216
+ "aria-labelledby": "share-link-heading",
217
+ children: [/* @__PURE__ */ s("h3", {
218
+ id: "share-link-heading",
219
+ className: "mb-form-gap text-sm font-semibold",
220
+ style: { color: G.textPrimary },
221
+ children: "Share link"
217
222
  }), /* @__PURE__ */ c("div", {
218
223
  style: {
219
224
  display: "flex",
220
- gap: "8px"
225
+ gap: "8px",
226
+ flexWrap: "wrap"
221
227
  },
222
228
  children: [/* @__PURE__ */ s("input", {
223
229
  type: "text",
@@ -237,7 +243,7 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
237
243
  style: {
238
244
  padding: "10px 16px",
239
245
  backgroundColor: P ? G.statusSuccessBg : G.actionPrimaryBg,
240
- color: "white",
246
+ color: "var(--color-action-primary-text)",
241
247
  border: "none",
242
248
  borderRadius: "6px",
243
249
  cursor: "pointer",
@@ -249,22 +255,19 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
249
255
  })]
250
256
  })]
251
257
  }),
252
- /* @__PURE__ */ c("div", {
253
- style: { marginBottom: "24px" },
254
- children: [/* @__PURE__ */ s("label", {
255
- style: {
256
- display: "block",
257
- marginBottom: "8px",
258
- fontSize: "13px",
259
- fontWeight: 600,
260
- color: G.textPrimary
261
- },
262
- children: "Add People"
258
+ /* @__PURE__ */ c("section", {
259
+ "aria-labelledby": "share-invite-heading",
260
+ children: [/* @__PURE__ */ s("h3", {
261
+ id: "share-invite-heading",
262
+ className: "mb-form-gap text-sm font-semibold",
263
+ style: { color: G.textPrimary },
264
+ children: "Invite people"
263
265
  }), /* @__PURE__ */ c("div", {
264
266
  style: {
265
267
  display: "flex",
266
268
  gap: "8px",
267
- marginBottom: "8px"
269
+ marginBottom: "8px",
270
+ flexWrap: "wrap"
268
271
  },
269
272
  children: [
270
273
  /* @__PURE__ */ c("div", {
@@ -298,7 +301,7 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
298
301
  backgroundColor: G.bgPrimary,
299
302
  border: `1px solid ${G.borderDefault}`,
300
303
  borderRadius: "6px",
301
- boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
304
+ boxShadow: "var(--shadow-elevation-3)",
302
305
  zIndex: 100001,
303
306
  maxHeight: "200px",
304
307
  overflowY: "auto"
@@ -365,7 +368,7 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
365
368
  style: {
366
369
  padding: "10px 16px",
367
370
  backgroundColor: O ? G.actionPrimaryBg : G.bgTertiary,
368
- color: O ? "white" : G.textSecondary,
371
+ color: O ? "var(--color-action-primary-text)" : G.textSecondary,
369
372
  border: "none",
370
373
  borderRadius: "6px",
371
374
  cursor: O ? "pointer" : "not-allowed",
@@ -377,61 +380,64 @@ function f({ dashboardId: f, dashboardName: m, isOpen: h, onClose: g }) {
377
380
  ]
378
381
  })]
379
382
  }),
380
- /* @__PURE__ */ c("div", { children: [/* @__PURE__ */ c("div", {
381
- style: {
382
- display: "flex",
383
- alignItems: "center",
384
- justifyContent: "space-between",
385
- marginBottom: "12px"
386
- },
387
- children: [/* @__PURE__ */ c("label", {
383
+ /* @__PURE__ */ c("section", {
384
+ "aria-labelledby": "share-access-heading",
385
+ children: [/* @__PURE__ */ c("div", {
388
386
  style: {
389
- fontSize: "13px",
390
- fontWeight: 600,
391
- color: G.textPrimary
387
+ display: "flex",
388
+ alignItems: "center",
389
+ justifyContent: "space-between",
390
+ flexWrap: "wrap",
391
+ gap: "8px",
392
+ marginBottom: "12px"
392
393
  },
393
- children: [
394
- "People with Access (",
395
- _.length,
396
- ")"
397
- ]
398
- }), _.length > 0 && /* @__PURE__ */ s("button", {
399
- onClick: V,
394
+ children: [/* @__PURE__ */ c("h3", {
395
+ id: "share-access-heading",
396
+ className: "text-sm font-semibold",
397
+ style: { color: G.textPrimary },
398
+ children: [
399
+ "People with access (",
400
+ _.length,
401
+ ")"
402
+ ]
403
+ }), _.length > 0 && /* @__PURE__ */ s("button", {
404
+ onClick: V,
405
+ style: {
406
+ padding: "4px 8px",
407
+ backgroundColor: "transparent",
408
+ color: G.statusErrorText,
409
+ border: "none",
410
+ cursor: "pointer",
411
+ fontSize: "12px"
412
+ },
413
+ children: "Revoke All"
414
+ })]
415
+ }), v && _.length === 0 ? /* @__PURE__ */ s("p", {
416
+ style: {
417
+ color: G.textSecondary,
418
+ fontSize: "13px"
419
+ },
420
+ children: "Loading..."
421
+ }) : _.length === 0 ? /* @__PURE__ */ s("p", {
400
422
  style: {
401
- padding: "4px 8px",
402
- backgroundColor: "transparent",
403
- color: G.statusErrorText,
404
- border: "none",
405
- cursor: "pointer",
406
- fontSize: "12px"
423
+ color: G.textSecondary,
424
+ fontSize: "13px"
425
+ },
426
+ children: "No one has access to this dashboard yet."
427
+ }) : /* @__PURE__ */ s("div", {
428
+ style: {
429
+ display: "flex",
430
+ flexDirection: "column",
431
+ gap: "8px"
407
432
  },
408
- children: "Revoke All"
433
+ children: _.map((e) => /* @__PURE__ */ s(p, {
434
+ share: e,
435
+ onUpdatePermission: z,
436
+ onRevoke: B,
437
+ colors: G
438
+ }, e.id))
409
439
  })]
410
- }), v && _.length === 0 ? /* @__PURE__ */ s("p", {
411
- style: {
412
- color: G.textSecondary,
413
- fontSize: "13px"
414
- },
415
- children: "Loading..."
416
- }) : _.length === 0 ? /* @__PURE__ */ s("p", {
417
- style: {
418
- color: G.textSecondary,
419
- fontSize: "13px"
420
- },
421
- children: "No one has access to this dashboard yet."
422
- }) : /* @__PURE__ */ s("div", {
423
- style: {
424
- display: "flex",
425
- flexDirection: "column",
426
- gap: "8px"
427
- },
428
- children: _.map((e) => /* @__PURE__ */ s(p, {
429
- share: e,
430
- onUpdatePermission: z,
431
- onRevoke: B,
432
- colors: G
433
- }, e.id))
434
- })] })
440
+ })
435
441
  ]
436
442
  })
437
443
  ]
@@ -463,7 +469,7 @@ function p({ share: e, onUpdatePermission: t, onRevoke: n, colors: r }) {
463
469
  display: "flex",
464
470
  alignItems: "center",
465
471
  justifyContent: "center",
466
- color: "white",
472
+ color: "var(--color-action-primary-text)",
467
473
  fontWeight: 600,
468
474
  fontSize: "14px"
469
475
  },
@@ -1 +1 @@
1
- {"version":3,"file":"ShareDialog.js","names":[],"sources":["../../../../../src/bigconsole/components/dashboard/ShareDialog/ShareDialog.tsx"],"sourcesContent":["/**\n * ShareDialog Component\n *\n * Dialog for sharing dashboards with users and roles.\n */\n\nimport React, { useState, useEffect, useCallback, useMemo } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n useDashboardSharing,\n type DashboardShare,\n type SharePermission,\n type ShareRecipientType,\n} from '../../../hooks';\nimport { useBigConsoleAnalytics } from '../../../hooks/useBigConsoleAnalytics';\nimport { EmbedTab } from './EmbedTab';\n\n// Dark mode aware colors\nconst getColors = (isDark: boolean) => ({\n bgPrimary: isDark ? '#1a1a2e' : '#ffffff',\n bgSecondary: isDark ? '#252542' : '#f4f4f5',\n bgTertiary: isDark ? '#2d2d4a' : '#e4e4e7',\n textPrimary: isDark ? '#f4f4f5' : '#18181b',\n textSecondary: isDark ? '#a1a1aa' : '#71717a',\n textTertiary: isDark ? '#71717a' : '#a1a1aa',\n borderDefault: isDark ? '#3f3f5c' : '#e4e4e7',\n actionPrimaryBg: '#6366f1',\n actionPrimaryBgHover: '#4f46e5',\n statusSuccessBg: '#22c55e',\n statusErrorText: '#ef4444',\n});\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ShareDialogProps {\n dashboardId: string;\n dashboardName: string;\n isOpen: boolean;\n onClose: () => void;\n}\n\ninterface ShareRecipient {\n id: string;\n name: string;\n email?: string;\n type: ShareRecipientType;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * Permission options as per spec (03-graphql-contract.md)\n * Spec values: view, edit\n */\nconst PERMISSION_OPTIONS: { value: SharePermission; label: string; description: string }[] = [\n { value: 'VIEW', label: 'View', description: 'Can view the dashboard' },\n { value: 'EDIT', label: 'Edit', description: 'Can edit widgets, layout, and share' },\n];\n\n// Use shared semantic tokens for portal rendering\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function ShareDialog({ dashboardId, dashboardName, isOpen, onClose }: ShareDialogProps) {\n const { shares, loading, fetchShares, shareWithUser, shareWithGroup, updateShare, revokeShare, revokeAllShares } =\n useDashboardSharing();\n const emitBigConsoleEvent = useBigConsoleAnalytics();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedRecipient, setSelectedRecipient] = useState<ShareRecipient | null>(null);\n const [selectedPermission, setSelectedPermission] = useState<SharePermission>('VIEW');\n const [shareLink, setShareLink] = useState('');\n const [copySuccess, setCopySuccess] = useState(false);\n const [activeTab, setActiveTab] = useState<'people' | 'embed'>('people');\n\n // Fetch shares when dialog opens\n useEffect(() => {\n if (isOpen && dashboardId) {\n fetchShares(dashboardId);\n // Generate share link\n setShareLink(`${window.location.origin}/dashboard/${dashboardId}?shared=true`);\n }\n }, [isOpen, dashboardId, fetchShares]);\n\n // Handle share - per spec: only USER and GROUP recipient types\n const handleShare = useCallback(async () => {\n if (!selectedRecipient) return;\n\n emitBigConsoleEvent('bigconsole.dashboard.share_clicked', {\n dashboardId,\n permission: selectedPermission,\n recipientType: selectedRecipient.type,\n });\n\n let share: DashboardShare | null = null;\n if (selectedRecipient.type === 'USER') {\n share = await shareWithUser(dashboardId, selectedRecipient.id, selectedPermission);\n } else if (selectedRecipient.type === 'GROUP') {\n share = await shareWithGroup(dashboardId, selectedRecipient.id, selectedPermission);\n }\n\n if (share) {\n emitBigConsoleEvent('bigconsole.dashboard.shared', {\n dashboardId,\n permission: selectedPermission,\n recipientType: selectedRecipient.type,\n });\n }\n\n setSelectedRecipient(null);\n setSearchQuery('');\n }, [dashboardId, emitBigConsoleEvent, selectedRecipient, selectedPermission, shareWithGroup, shareWithUser]);\n\n // Handle update permission\n const handleUpdatePermission = useCallback(\n async (shareId: string, newPermission: SharePermission) => {\n await updateShare({ id: shareId, permission: newPermission });\n },\n [updateShare]\n );\n\n // Handle revoke\n const handleRevoke = useCallback(\n async (shareId: string) => {\n if (window.confirm('Are you sure you want to revoke this access?')) {\n await revokeShare(shareId);\n }\n },\n [revokeShare]\n );\n\n // Handle revoke all\n const handleRevokeAll = useCallback(async () => {\n if (window.confirm('Are you sure you want to revoke all shared access?')) {\n await revokeAllShares(dashboardId);\n }\n }, [dashboardId, revokeAllShares]);\n\n // Copy link to clipboard\n const handleCopyLink = useCallback(async () => {\n try {\n await navigator.clipboard.writeText(shareLink);\n setCopySuccess(true);\n setTimeout(() => setCopySuccess(false), 2000);\n } catch (err) {}\n }, [shareLink]);\n\n // Mock search results (in real app, this would come from an API)\n // Per spec: ShareRecipientType is 'user' or 'group' only\n const searchResults: ShareRecipient[] = searchQuery\n ? (\n [\n { id: 'user-1', name: 'John Doe', email: 'john@example.com', type: 'USER' as ShareRecipientType },\n { id: 'user-2', name: 'Jane Smith', email: 'jane@example.com', type: 'USER' as ShareRecipientType },\n { id: 'group-admin', name: 'Administrators', type: 'GROUP' as ShareRecipientType },\n { id: 'group-editor', name: 'Editors', type: 'GROUP' as ShareRecipientType },\n ] as ShareRecipient[]\n ).filter(\n (r) =>\n r.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n r.email?.toLowerCase().includes(searchQuery.toLowerCase())\n )\n : [];\n\n // Check if dark mode is enabled\n const isDarkMode = typeof document !== 'undefined' && document.documentElement.classList.contains('dark');\n const colors = useMemo(() => getColors(isDarkMode), [isDarkMode]);\n\n if (!isOpen) return null;\n\n // Use portal to render at document.body level to avoid z-index/stacking context issues\n const dialogContent = (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.6)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 99999,\n isolation: 'isolate',\n }}\n onClick={onClose}\n >\n <div\n style={{\n width: '520px',\n maxHeight: '80vh',\n backgroundColor: colors.bgPrimary,\n borderRadius: '12px',\n boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',\n overflow: 'hidden',\n position: 'relative',\n zIndex: 100000,\n color: colors.textPrimary,\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div\n style={{\n padding: '20px 24px',\n borderBottom: `1px solid ${colors.borderDefault}`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n backgroundColor: colors.bgPrimary,\n }}\n >\n <div>\n <h3 style={{ margin: 0, fontSize: '18px', fontWeight: 600, color: colors.textPrimary }}>Share Dashboard</h3>\n <p style={{ margin: '4px 0 0', fontSize: '13px', color: colors.textSecondary }}>{dashboardName}</p>\n </div>\n <button\n onClick={onClose}\n style={{\n background: 'none',\n border: 'none',\n fontSize: '24px',\n cursor: 'pointer',\n color: colors.textSecondary,\n padding: '4px',\n lineHeight: 1,\n }}\n >\n ×\n </button>\n </div>\n\n {/* Tabs */}\n <div\n style={{\n display: 'flex',\n gap: '4px',\n padding: '0 24px',\n borderBottom: `1px solid ${colors.borderDefault}`,\n backgroundColor: colors.bgPrimary,\n }}\n >\n {(['people', 'embed'] as const).map((tab) => (\n <button\n key={tab}\n type=\"button\"\n onClick={() => setActiveTab(tab)}\n style={{\n padding: '10px 12px',\n background: 'none',\n border: 'none',\n borderBottom: `2px solid ${activeTab === tab ? colors.actionPrimaryBg : 'transparent'}`,\n color: activeTab === tab ? colors.textPrimary : colors.textSecondary,\n fontSize: '13px',\n fontWeight: 600,\n cursor: 'pointer',\n }}\n >\n {tab === 'people' ? 'People' : 'Publish to web'}\n </button>\n ))}\n </div>\n\n {/* Publish-to-web tab */}\n {activeTab === 'embed' && (\n <div style={{ overflowY: 'auto', maxHeight: 'calc(80vh - 160px)', backgroundColor: colors.bgPrimary }}>\n <EmbedTab dashboardId={dashboardId} colors={colors} />\n </div>\n )}\n\n {/* People tab (existing content) */}\n {activeTab === 'people' && (\n <div\n style={{\n padding: '24px',\n overflowY: 'auto',\n maxHeight: 'calc(80vh - 200px)',\n backgroundColor: colors.bgPrimary,\n }}\n >\n {/* Share Link Section */}\n <div style={{ marginBottom: '24px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '8px',\n fontSize: '13px',\n fontWeight: 600,\n color: colors.textPrimary,\n }}\n >\n Share Link\n </label>\n <div style={{ display: 'flex', gap: '8px' }}>\n <input\n type=\"text\"\n value={shareLink}\n readOnly\n style={{\n flex: 1,\n padding: '10px 12px',\n border: `1px solid ${colors.borderDefault}`,\n borderRadius: '6px',\n fontSize: '13px',\n backgroundColor: colors.bgSecondary,\n color: colors.textPrimary,\n }}\n />\n <button\n onClick={handleCopyLink}\n style={{\n padding: '10px 16px',\n backgroundColor: copySuccess ? colors.statusSuccessBg : colors.actionPrimaryBg,\n color: 'white',\n border: 'none',\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 500,\n minWidth: '80px',\n }}\n >\n {copySuccess ? 'Copied!' : 'Copy'}\n </button>\n </div>\n </div>\n\n {/* Add People Section */}\n <div style={{ marginBottom: '24px' }}>\n <label\n style={{\n display: 'block',\n marginBottom: '8px',\n fontSize: '13px',\n fontWeight: 600,\n color: colors.textPrimary,\n }}\n >\n Add People\n </label>\n <div style={{ display: 'flex', gap: '8px', marginBottom: '8px' }}>\n <div style={{ flex: 1, position: 'relative' }}>\n <input\n type=\"text\"\n value={selectedRecipient ? selectedRecipient.name : searchQuery}\n onChange={(e) => {\n setSearchQuery(e.target.value);\n setSelectedRecipient(null);\n }}\n placeholder=\"Search by name or email...\"\n style={{\n width: '100%',\n padding: '10px 12px',\n border: `1px solid ${colors.borderDefault}`,\n borderRadius: '6px',\n fontSize: '13px',\n backgroundColor: colors.bgPrimary,\n color: colors.textPrimary,\n }}\n />\n {/* Search Results Dropdown */}\n {searchQuery && !selectedRecipient && searchResults.length > 0 && (\n <div\n style={{\n position: 'absolute',\n top: '100%',\n left: 0,\n right: 0,\n marginTop: '4px',\n backgroundColor: colors.bgPrimary,\n border: `1px solid ${colors.borderDefault}`,\n borderRadius: '6px',\n boxShadow: '0 8px 24px rgba(0,0,0,0.25)',\n zIndex: 100001,\n maxHeight: '200px',\n overflowY: 'auto',\n }}\n >\n {searchResults.map((result) => (\n <button\n key={result.id}\n onClick={() => setSelectedRecipient(result)}\n style={{\n display: 'block',\n width: '100%',\n padding: '10px 12px',\n textAlign: 'left',\n backgroundColor: 'transparent',\n border: 'none',\n cursor: 'pointer',\n borderBottom: `1px solid ${colors.borderDefault}`,\n }}\n >\n <div style={{ fontSize: '13px', fontWeight: 500, color: colors.textPrimary }}>\n {result.name}\n </div>\n {result.email && (\n <div style={{ fontSize: '11px', color: colors.textSecondary }}>{result.email}</div>\n )}\n <span\n style={{\n fontSize: '10px',\n color: colors.textTertiary,\n textTransform: 'uppercase',\n }}\n >\n {result.type}\n </span>\n </button>\n ))}\n </div>\n )}\n </div>\n <select\n value={selectedPermission}\n onChange={(e) => setSelectedPermission(e.target.value as SharePermission)}\n style={{\n padding: '10px 12px',\n border: `1px solid ${colors.borderDefault}`,\n borderRadius: '6px',\n fontSize: '13px',\n backgroundColor: colors.bgPrimary,\n color: colors.textPrimary,\n }}\n >\n {PERMISSION_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <button\n onClick={handleShare}\n disabled={!selectedRecipient || loading}\n style={{\n padding: '10px 16px',\n backgroundColor: selectedRecipient ? colors.actionPrimaryBg : colors.bgTertiary,\n color: selectedRecipient ? 'white' : colors.textSecondary,\n border: 'none',\n borderRadius: '6px',\n cursor: selectedRecipient ? 'pointer' : 'not-allowed',\n fontSize: '13px',\n fontWeight: 500,\n }}\n >\n Share\n </button>\n </div>\n </div>\n\n {/* Current Shares */}\n <div>\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: '12px',\n }}\n >\n <label\n style={{\n fontSize: '13px',\n fontWeight: 600,\n color: colors.textPrimary,\n }}\n >\n People with Access ({shares.length})\n </label>\n {shares.length > 0 && (\n <button\n onClick={handleRevokeAll}\n style={{\n padding: '4px 8px',\n backgroundColor: 'transparent',\n color: colors.statusErrorText,\n border: 'none',\n cursor: 'pointer',\n fontSize: '12px',\n }}\n >\n Revoke All\n </button>\n )}\n </div>\n\n {loading && shares.length === 0 ? (\n <p style={{ color: colors.textSecondary, fontSize: '13px' }}>Loading...</p>\n ) : shares.length === 0 ? (\n <p style={{ color: colors.textSecondary, fontSize: '13px' }}>\n No one has access to this dashboard yet.\n </p>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n {shares.map((share) => (\n <ShareItem\n key={share.id}\n share={share}\n onUpdatePermission={handleUpdatePermission}\n onRevoke={handleRevoke}\n colors={colors}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n );\n\n // Render via portal to document.body to escape any stacking context issues\n return createPortal(dialogContent, document.body);\n}\n\n// ============================================================================\n// ShareItem Sub-component\n// ============================================================================\n\ninterface ShareItemProps {\n share: DashboardShare;\n onUpdatePermission: (shareId: string, permission: SharePermission) => void;\n onRevoke: (shareId: string) => void;\n colors: ReturnType<typeof getColors>;\n}\n\nfunction ShareItem({ share, onUpdatePermission, onRevoke, colors }: ShareItemProps) {\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '10px 12px',\n backgroundColor: colors.bgSecondary,\n borderRadius: '6px',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>\n <div\n style={{\n width: '36px',\n height: '36px',\n borderRadius: '50%',\n backgroundColor: colors.actionPrimaryBg,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: 'white',\n fontWeight: 600,\n fontSize: '14px',\n }}\n >\n {share.recipientType === 'USER' ? 'U' : 'R'}\n </div>\n <div>\n <div style={{ fontSize: '13px', fontWeight: 500, color: colors.textPrimary }}>{share.recipientId}</div>\n <div style={{ fontSize: '11px', color: colors.textSecondary }}>\n {share.recipientType} - Shared {new Date(share.createdAt).toLocaleDateString()}\n </div>\n </div>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <select\n value={share.permission}\n onChange={(e) => onUpdatePermission(share.id, e.target.value as SharePermission)}\n style={{\n padding: '6px 10px',\n border: `1px solid ${colors.borderDefault}`,\n borderRadius: '4px',\n fontSize: '12px',\n backgroundColor: colors.bgPrimary,\n color: colors.textPrimary,\n }}\n >\n {PERMISSION_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <button\n onClick={() => onRevoke(share.id)}\n style={{\n padding: '6px 10px',\n backgroundColor: 'transparent',\n color: colors.statusErrorText,\n border: `1px solid ${colors.statusErrorText}`,\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n }}\n >\n Revoke\n </button>\n </div>\n </div>\n );\n}\n\nexport default ShareDialog;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ShareDialog.js","names":[],"sources":["../../../../../src/bigconsole/components/dashboard/ShareDialog/ShareDialog.tsx"],"sourcesContent":["/**\n * ShareDialog Component\n *\n * Dialog for sharing dashboards with users and roles.\n */\n\nimport React, { useState, useEffect, useCallback, useMemo } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n useDashboardSharing,\n type DashboardShare,\n type SharePermission,\n type ShareRecipientType,\n} from '../../../hooks';\nimport { useBigConsoleAnalytics } from '../../../hooks/useBigConsoleAnalytics';\nimport { EmbedTab } from './EmbedTab';\n\n// Dark mode aware colors\nconst getColors = (isDark: boolean) => ({\n bgPrimary: isDark ? '#1a1a2e' : '#ffffff',\n bgSecondary: isDark ? '#252542' : '#f4f4f5',\n bgTertiary: isDark ? '#2d2d4a' : '#e4e4e7',\n textPrimary: isDark ? '#f4f4f5' : '#18181b',\n textSecondary: isDark ? '#a1a1aa' : '#71717a',\n textTertiary: isDark ? '#71717a' : '#a1a1aa',\n borderDefault: isDark ? '#3f3f5c' : '#e4e4e7',\n actionPrimaryBg: '#6366f1',\n actionPrimaryBgHover: '#4f46e5',\n statusSuccessBg: '#22c55e',\n statusErrorText: '#ef4444',\n});\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ShareDialogProps {\n dashboardId: string;\n dashboardName: string;\n isOpen: boolean;\n onClose: () => void;\n}\n\ninterface ShareRecipient {\n id: string;\n name: string;\n email?: string;\n type: ShareRecipientType;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * Permission options as per spec (03-graphql-contract.md)\n * Spec values: view, edit\n */\nconst PERMISSION_OPTIONS: { value: SharePermission; label: string; description: string }[] = [\n { value: 'VIEW', label: 'View', description: 'Can view the dashboard' },\n { value: 'EDIT', label: 'Edit', description: 'Can edit widgets, layout, and share' },\n];\n\n// Use shared semantic tokens for portal rendering\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function ShareDialog({ dashboardId, dashboardName, isOpen, onClose }: ShareDialogProps) {\n const { shares, loading, fetchShares, shareWithUser, shareWithGroup, updateShare, revokeShare, revokeAllShares } =\n useDashboardSharing();\n const emitBigConsoleEvent = useBigConsoleAnalytics();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedRecipient, setSelectedRecipient] = useState<ShareRecipient | null>(null);\n const [selectedPermission, setSelectedPermission] = useState<SharePermission>('VIEW');\n const [shareLink, setShareLink] = useState('');\n const [copySuccess, setCopySuccess] = useState(false);\n const [activeTab, setActiveTab] = useState<'people' | 'embed'>('people');\n\n // Fetch shares when dialog opens\n useEffect(() => {\n if (isOpen && dashboardId) {\n fetchShares(dashboardId);\n // Generate share link\n setShareLink(`${window.location.origin}/dashboard/${dashboardId}?shared=true`);\n }\n }, [isOpen, dashboardId, fetchShares]);\n\n // Handle share - per spec: only USER and GROUP recipient types\n const handleShare = useCallback(async () => {\n if (!selectedRecipient) return;\n\n emitBigConsoleEvent('bigconsole.dashboard.share_clicked', {\n dashboardId,\n permission: selectedPermission,\n recipientType: selectedRecipient.type,\n });\n\n let share: DashboardShare | null = null;\n if (selectedRecipient.type === 'USER') {\n share = await shareWithUser(dashboardId, selectedRecipient.id, selectedPermission);\n } else if (selectedRecipient.type === 'GROUP') {\n share = await shareWithGroup(dashboardId, selectedRecipient.id, selectedPermission);\n }\n\n if (share) {\n emitBigConsoleEvent('bigconsole.dashboard.shared', {\n dashboardId,\n permission: selectedPermission,\n recipientType: selectedRecipient.type,\n });\n }\n\n setSelectedRecipient(null);\n setSearchQuery('');\n }, [dashboardId, emitBigConsoleEvent, selectedRecipient, selectedPermission, shareWithGroup, shareWithUser]);\n\n // Handle update permission\n const handleUpdatePermission = useCallback(\n async (shareId: string, newPermission: SharePermission) => {\n await updateShare({ id: shareId, permission: newPermission });\n },\n [updateShare]\n );\n\n // Handle revoke\n const handleRevoke = useCallback(\n async (shareId: string) => {\n if (window.confirm('Are you sure you want to revoke this access?')) {\n await revokeShare(shareId);\n }\n },\n [revokeShare]\n );\n\n // Handle revoke all\n const handleRevokeAll = useCallback(async () => {\n if (window.confirm('Are you sure you want to revoke all shared access?')) {\n await revokeAllShares(dashboardId);\n }\n }, [dashboardId, revokeAllShares]);\n\n // Copy link to clipboard\n const handleCopyLink = useCallback(async () => {\n try {\n await navigator.clipboard.writeText(shareLink);\n setCopySuccess(true);\n setTimeout(() => setCopySuccess(false), 2000);\n } catch (err) {}\n }, [shareLink]);\n\n // Mock search results (in real app, this would come from an API)\n // Per spec: ShareRecipientType is 'user' or 'group' only\n const searchResults: ShareRecipient[] = searchQuery\n ? (\n [\n { id: 'user-1', name: 'John Doe', email: 'john@example.com', type: 'USER' as ShareRecipientType },\n { id: 'user-2', name: 'Jane Smith', email: 'jane@example.com', type: 'USER' as ShareRecipientType },\n { id: 'group-admin', name: 'Administrators', type: 'GROUP' as ShareRecipientType },\n { id: 'group-editor', name: 'Editors', type: 'GROUP' as ShareRecipientType },\n ] as ShareRecipient[]\n ).filter(\n (r) =>\n r.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n r.email?.toLowerCase().includes(searchQuery.toLowerCase())\n )\n : [];\n\n // Check if dark mode is enabled\n const isDarkMode = typeof document !== 'undefined' && document.documentElement.classList.contains('dark');\n const colors = useMemo(() => getColors(isDarkMode), [isDarkMode]);\n\n if (!isOpen) return null;\n\n // Use portal to render at document.body level to avoid z-index/stacking context issues\n const dialogContent = (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'var(--color-overlay-scrim)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 'var(--space-formGap)',\n zIndex: 99999,\n isolation: 'isolate',\n }}\n onClick={onClose}\n role=\"presentation\"\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"share-dialog-title\"\n data-testid=\"share-dialog\"\n style={{\n // was a hard 520px, which overflowed every phone viewport\n width: '100%',\n maxWidth: '520px',\n maxHeight: '85vh',\n display: 'flex',\n flexDirection: 'column',\n backgroundColor: colors.bgPrimary,\n borderRadius: 'var(--radius-card)',\n boxShadow: 'var(--shadow-elevation-3)',\n overflow: 'hidden',\n position: 'relative',\n zIndex: 100000,\n color: colors.textPrimary,\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div\n style={{\n flexShrink: 0,\n padding: '20px 24px',\n borderBottom: `1px solid ${colors.borderDefault}`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n backgroundColor: colors.bgPrimary,\n }}\n >\n <div>\n <h2\n id=\"share-dialog-title\"\n style={{ margin: 0, fontSize: '18px', fontWeight: 600, color: colors.textPrimary }}\n >\n Share Dashboard\n </h2>\n <p style={{ margin: '4px 0 0', fontSize: '13px', color: colors.textSecondary }}>{dashboardName}</p>\n </div>\n <button\n onClick={onClose}\n style={{\n background: 'none',\n border: 'none',\n fontSize: '24px',\n cursor: 'pointer',\n color: colors.textSecondary,\n padding: '4px',\n lineHeight: 1,\n }}\n >\n ×\n </button>\n </div>\n\n {/* Tabs */}\n <div\n role=\"tablist\"\n aria-label=\"Share options\"\n style={{\n flexShrink: 0,\n display: 'flex',\n gap: '4px',\n padding: '0 24px',\n borderBottom: `1px solid ${colors.borderDefault}`,\n backgroundColor: colors.bgPrimary,\n }}\n >\n {(['people', 'embed'] as const).map((tab) => (\n <button\n key={tab}\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === tab}\n onClick={() => setActiveTab(tab)}\n style={{\n padding: '10px 12px',\n background: 'none',\n border: 'none',\n borderBottom: `2px solid ${activeTab === tab ? colors.actionPrimaryBg : 'transparent'}`,\n color: activeTab === tab ? colors.textPrimary : colors.textSecondary,\n fontSize: '13px',\n fontWeight: 600,\n cursor: 'pointer',\n }}\n >\n {tab === 'people' ? 'People' : 'Publish to web'}\n </button>\n ))}\n </div>\n\n {/* Publish-to-web tab */}\n {activeTab === 'embed' && (\n <div className=\"min-h-0 flex-1 overflow-y-auto\" style={{ backgroundColor: colors.bgPrimary }}>\n <EmbedTab dashboardId={dashboardId} colors={colors} />\n </div>\n )}\n\n {/* People tab (existing content) */}\n {activeTab === 'people' && (\n <div\n className=\"flex min-h-0 flex-1 flex-col gap-section-gap overflow-y-auto p-card-padding\"\n style={{ backgroundColor: colors.bgPrimary }}\n >\n {/* Share Link Section — the fastest way to share, so it leads */}\n <section aria-labelledby=\"share-link-heading\">\n <h3\n id=\"share-link-heading\"\n className=\"mb-form-gap text-sm font-semibold\"\n style={{ color: colors.textPrimary }}\n >\n Share link\n </h3>\n <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>\n <input\n type=\"text\"\n value={shareLink}\n readOnly\n style={{\n flex: 1,\n padding: '10px 12px',\n border: `1px solid ${colors.borderDefault}`,\n borderRadius: '6px',\n fontSize: '13px',\n backgroundColor: colors.bgSecondary,\n color: colors.textPrimary,\n }}\n />\n <button\n onClick={handleCopyLink}\n style={{\n padding: '10px 16px',\n backgroundColor: copySuccess ? colors.statusSuccessBg : colors.actionPrimaryBg,\n color: 'var(--color-action-primary-text)',\n border: 'none',\n borderRadius: '6px',\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 500,\n minWidth: '80px',\n }}\n >\n {copySuccess ? 'Copied!' : 'Copy'}\n </button>\n </div>\n </section>\n\n {/* Add People Section */}\n <section aria-labelledby=\"share-invite-heading\">\n <h3\n id=\"share-invite-heading\"\n className=\"mb-form-gap text-sm font-semibold\"\n style={{ color: colors.textPrimary }}\n >\n Invite people\n </h3>\n <div style={{ display: 'flex', gap: '8px', marginBottom: '8px', flexWrap: 'wrap' }}>\n <div style={{ flex: 1, position: 'relative' }}>\n <input\n type=\"text\"\n value={selectedRecipient ? selectedRecipient.name : searchQuery}\n onChange={(e) => {\n setSearchQuery(e.target.value);\n setSelectedRecipient(null);\n }}\n placeholder=\"Search by name or email...\"\n style={{\n width: '100%',\n padding: '10px 12px',\n border: `1px solid ${colors.borderDefault}`,\n borderRadius: '6px',\n fontSize: '13px',\n backgroundColor: colors.bgPrimary,\n color: colors.textPrimary,\n }}\n />\n {/* Search Results Dropdown */}\n {searchQuery && !selectedRecipient && searchResults.length > 0 && (\n <div\n style={{\n position: 'absolute',\n top: '100%',\n left: 0,\n right: 0,\n marginTop: '4px',\n backgroundColor: colors.bgPrimary,\n border: `1px solid ${colors.borderDefault}`,\n borderRadius: '6px',\n boxShadow: 'var(--shadow-elevation-3)',\n zIndex: 100001,\n maxHeight: '200px',\n overflowY: 'auto',\n }}\n >\n {searchResults.map((result) => (\n <button\n key={result.id}\n onClick={() => setSelectedRecipient(result)}\n style={{\n display: 'block',\n width: '100%',\n padding: '10px 12px',\n textAlign: 'left',\n backgroundColor: 'transparent',\n border: 'none',\n cursor: 'pointer',\n borderBottom: `1px solid ${colors.borderDefault}`,\n }}\n >\n <div style={{ fontSize: '13px', fontWeight: 500, color: colors.textPrimary }}>\n {result.name}\n </div>\n {result.email && (\n <div style={{ fontSize: '11px', color: colors.textSecondary }}>{result.email}</div>\n )}\n <span\n style={{\n fontSize: '10px',\n color: colors.textTertiary,\n textTransform: 'uppercase',\n }}\n >\n {result.type}\n </span>\n </button>\n ))}\n </div>\n )}\n </div>\n <select\n value={selectedPermission}\n onChange={(e) => setSelectedPermission(e.target.value as SharePermission)}\n style={{\n padding: '10px 12px',\n border: `1px solid ${colors.borderDefault}`,\n borderRadius: '6px',\n fontSize: '13px',\n backgroundColor: colors.bgPrimary,\n color: colors.textPrimary,\n }}\n >\n {PERMISSION_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <button\n onClick={handleShare}\n disabled={!selectedRecipient || loading}\n style={{\n padding: '10px 16px',\n backgroundColor: selectedRecipient ? colors.actionPrimaryBg : colors.bgTertiary,\n color: selectedRecipient ? 'var(--color-action-primary-text)' : colors.textSecondary,\n border: 'none',\n borderRadius: '6px',\n cursor: selectedRecipient ? 'pointer' : 'not-allowed',\n fontSize: '13px',\n fontWeight: 500,\n }}\n >\n Share\n </button>\n </div>\n </section>\n\n {/* Current Shares */}\n <section aria-labelledby=\"share-access-heading\">\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n flexWrap: 'wrap',\n gap: '8px',\n marginBottom: '12px',\n }}\n >\n <h3 id=\"share-access-heading\" className=\"text-sm font-semibold\" style={{ color: colors.textPrimary }}>\n People with access ({shares.length})\n </h3>\n {shares.length > 0 && (\n <button\n onClick={handleRevokeAll}\n style={{\n padding: '4px 8px',\n backgroundColor: 'transparent',\n color: colors.statusErrorText,\n border: 'none',\n cursor: 'pointer',\n fontSize: '12px',\n }}\n >\n Revoke All\n </button>\n )}\n </div>\n\n {loading && shares.length === 0 ? (\n <p style={{ color: colors.textSecondary, fontSize: '13px' }}>Loading...</p>\n ) : shares.length === 0 ? (\n <p style={{ color: colors.textSecondary, fontSize: '13px' }}>\n No one has access to this dashboard yet.\n </p>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n {shares.map((share) => (\n <ShareItem\n key={share.id}\n share={share}\n onUpdatePermission={handleUpdatePermission}\n onRevoke={handleRevoke}\n colors={colors}\n />\n ))}\n </div>\n )}\n </section>\n </div>\n )}\n </div>\n </div>\n );\n\n // Render via portal to document.body to escape any stacking context issues\n return createPortal(dialogContent, document.body);\n}\n\n// ============================================================================\n// ShareItem Sub-component\n// ============================================================================\n\ninterface ShareItemProps {\n share: DashboardShare;\n onUpdatePermission: (shareId: string, permission: SharePermission) => void;\n onRevoke: (shareId: string) => void;\n colors: ReturnType<typeof getColors>;\n}\n\nfunction ShareItem({ share, onUpdatePermission, onRevoke, colors }: ShareItemProps) {\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '10px 12px',\n backgroundColor: colors.bgSecondary,\n borderRadius: '6px',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>\n <div\n style={{\n width: '36px',\n height: '36px',\n borderRadius: '50%',\n backgroundColor: colors.actionPrimaryBg,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: 'var(--color-action-primary-text)',\n fontWeight: 600,\n fontSize: '14px',\n }}\n >\n {share.recipientType === 'USER' ? 'U' : 'R'}\n </div>\n <div>\n <div style={{ fontSize: '13px', fontWeight: 500, color: colors.textPrimary }}>{share.recipientId}</div>\n <div style={{ fontSize: '11px', color: colors.textSecondary }}>\n {share.recipientType} - Shared {new Date(share.createdAt).toLocaleDateString()}\n </div>\n </div>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <select\n value={share.permission}\n onChange={(e) => onUpdatePermission(share.id, e.target.value as SharePermission)}\n style={{\n padding: '6px 10px',\n border: `1px solid ${colors.borderDefault}`,\n borderRadius: '4px',\n fontSize: '12px',\n backgroundColor: colors.bgPrimary,\n color: colors.textPrimary,\n }}\n >\n {PERMISSION_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <button\n onClick={() => onRevoke(share.id)}\n style={{\n padding: '6px 10px',\n backgroundColor: 'transparent',\n color: colors.statusErrorText,\n border: `1px solid ${colors.statusErrorText}`,\n borderRadius: '4px',\n cursor: 'pointer',\n fontSize: '12px',\n }}\n >\n Revoke\n </button>\n </div>\n </div>\n );\n}\n\nexport default ShareDialog;\n"],"mappings":"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"}