@burdenoff/microfe-bigconsole 2026.1004.3 → 2026.1004.5

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 (157) hide show
  1. package/dist/bigconsole/BigConsoleRoutes.d.ts +1 -1
  2. package/dist/bigconsole/BigConsoleRoutes.d.ts.map +1 -1
  3. package/dist/bigconsole/BigConsoleRoutes.js +15 -1
  4. package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
  5. package/dist/bigconsole/components/DrilldownDashboardRenderer.d.ts.map +1 -1
  6. package/dist/bigconsole/components/DrilldownDashboardRenderer.js +57 -56
  7. package/dist/bigconsole/components/DrilldownDashboardRenderer.js.map +1 -1
  8. package/dist/bigconsole/components/DrilldownModal.d.ts.map +1 -1
  9. package/dist/bigconsole/components/DrilldownModal.js +55 -53
  10. package/dist/bigconsole/components/DrilldownModal.js.map +1 -1
  11. package/dist/bigconsole/components/builder/inspector/DataBindingSection.d.ts +3 -1
  12. package/dist/bigconsole/components/builder/inspector/DataBindingSection.d.ts.map +1 -1
  13. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +40 -39
  14. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js.map +1 -1
  15. package/dist/bigconsole/components/dashboard/DashboardCanvas.d.ts.map +1 -1
  16. package/dist/bigconsole/components/dashboard/DashboardCanvas.js +184 -177
  17. package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
  18. package/dist/bigconsole/components/dashboard/DashboardGrid.d.ts.map +1 -1
  19. package/dist/bigconsole/components/dashboard/DashboardGrid.js +40 -22
  20. package/dist/bigconsole/components/dashboard/DashboardGrid.js.map +1 -1
  21. package/dist/bigconsole/components/dashboard/ExportDashboardDialog.d.ts.map +1 -1
  22. package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js +31 -25
  23. package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js.map +1 -1
  24. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.d.ts +1 -1
  25. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.d.ts.map +1 -1
  26. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +115 -80
  27. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js.map +1 -1
  28. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.d.ts.map +1 -1
  29. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +177 -146
  30. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
  31. package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.d.ts +1 -1
  32. package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.d.ts.map +1 -1
  33. package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.js +375 -325
  34. package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.js.map +1 -1
  35. package/dist/bigconsole/components/datasink/CacheTTLSelector.d.ts.map +1 -1
  36. package/dist/bigconsole/components/datasink/CacheTTLSelector.js +7 -1
  37. package/dist/bigconsole/components/datasink/CacheTTLSelector.js.map +1 -1
  38. package/dist/bigconsole/components/parser/ParserSelector.d.ts +3 -1
  39. package/dist/bigconsole/components/parser/ParserSelector.d.ts.map +1 -1
  40. package/dist/bigconsole/components/parser/ParserSelector.js +94 -94
  41. package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
  42. package/dist/bigconsole/components/pipeline/PipelineCard.d.ts.map +1 -1
  43. package/dist/bigconsole/components/pipeline/PipelineCard.js +111 -89
  44. package/dist/bigconsole/components/pipeline/PipelineCard.js.map +1 -1
  45. package/dist/bigconsole/components/pipeline/PipelineScopePicker.d.ts +8 -0
  46. package/dist/bigconsole/components/pipeline/PipelineScopePicker.d.ts.map +1 -0
  47. package/dist/bigconsole/components/pipeline/PipelineScopePicker.js +52 -0
  48. package/dist/bigconsole/components/pipeline/PipelineScopePicker.js.map +1 -0
  49. package/dist/bigconsole/components/widgets/PropertiesPanel.d.ts.map +1 -1
  50. package/dist/bigconsole/components/widgets/PropertiesPanel.js +257 -221
  51. package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
  52. package/dist/bigconsole/components/widgets/WidgetHeader.d.ts +1 -0
  53. package/dist/bigconsole/components/widgets/WidgetHeader.d.ts.map +1 -1
  54. package/dist/bigconsole/components/widgets/WidgetHeader.js +29 -29
  55. package/dist/bigconsole/components/widgets/WidgetHeader.js.map +1 -1
  56. package/dist/bigconsole/components/widgets/WidgetWrapper.d.ts +2 -0
  57. package/dist/bigconsole/components/widgets/WidgetWrapper.d.ts.map +1 -1
  58. package/dist/bigconsole/components/widgets/WidgetWrapper.js +247 -239
  59. package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
  60. package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.d.ts.map +1 -1
  61. package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +6 -4
  62. package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js.map +1 -1
  63. package/dist/bigconsole/components/widgets/states/EmptyState.d.ts +1 -0
  64. package/dist/bigconsole/components/widgets/states/EmptyState.d.ts.map +1 -1
  65. package/dist/bigconsole/components/widgets/states/EmptyState.js +4 -4
  66. package/dist/bigconsole/components/widgets/states/EmptyState.js.map +1 -1
  67. package/dist/bigconsole/components/workflow/WorkflowDesignPreview.d.ts +7 -0
  68. package/dist/bigconsole/components/workflow/WorkflowDesignPreview.d.ts.map +1 -0
  69. package/dist/bigconsole/components/workflow/WorkflowDesignPreview.js +94 -0
  70. package/dist/bigconsole/components/workflow/WorkflowDesignPreview.js.map +1 -0
  71. package/dist/bigconsole/components/workflow/WorkflowSinkLinkForm.d.ts +7 -0
  72. package/dist/bigconsole/components/workflow/WorkflowSinkLinkForm.d.ts.map +1 -0
  73. package/dist/bigconsole/components/workflow/WorkflowSinkLinkForm.js +101 -0
  74. package/dist/bigconsole/components/workflow/WorkflowSinkLinkForm.js.map +1 -0
  75. package/dist/bigconsole/hooks/useBigConsoleSettings.d.ts +16 -0
  76. package/dist/bigconsole/hooks/useBigConsoleSettings.d.ts.map +1 -0
  77. package/dist/bigconsole/hooks/useBigConsoleSettings.js +57 -0
  78. package/dist/bigconsole/hooks/useBigConsoleSettings.js.map +1 -0
  79. package/dist/bigconsole/hooks/useDashboardOperations.d.ts.map +1 -1
  80. package/dist/bigconsole/hooks/useDashboardOperations.js +102 -79
  81. package/dist/bigconsole/hooks/useDashboardOperations.js.map +1 -1
  82. package/dist/bigconsole/hooks/useExportDashboard.d.ts +3 -1
  83. package/dist/bigconsole/hooks/useExportDashboard.d.ts.map +1 -1
  84. package/dist/bigconsole/hooks/useExportDashboard.js +23 -13
  85. package/dist/bigconsole/hooks/useExportDashboard.js.map +1 -1
  86. package/dist/bigconsole/hooks/useFilterUrlSync.d.ts +18 -0
  87. package/dist/bigconsole/hooks/useFilterUrlSync.d.ts.map +1 -1
  88. package/dist/bigconsole/hooks/useFilterUrlSync.js +87 -57
  89. package/dist/bigconsole/hooks/useFilterUrlSync.js.map +1 -1
  90. package/dist/bigconsole/hooks/useGlobalFilters.d.ts.map +1 -1
  91. package/dist/bigconsole/hooks/useGlobalFilters.js +39 -29
  92. package/dist/bigconsole/hooks/useGlobalFilters.js.map +1 -1
  93. package/dist/bigconsole/hooks/usePageOperations.d.ts.map +1 -1
  94. package/dist/bigconsole/hooks/usePageOperations.js +1 -0
  95. package/dist/bigconsole/hooks/usePageOperations.js.map +1 -1
  96. package/dist/bigconsole/hooks/usePipelineOperations.d.ts.map +1 -1
  97. package/dist/bigconsole/hooks/usePipelineOperations.js +114 -112
  98. package/dist/bigconsole/hooks/usePipelineOperations.js.map +1 -1
  99. package/dist/bigconsole/hooks/useShareRecipients.d.ts +22 -0
  100. package/dist/bigconsole/hooks/useShareRecipients.d.ts.map +1 -0
  101. package/dist/bigconsole/hooks/useShareRecipients.js +198 -0
  102. package/dist/bigconsole/hooks/useShareRecipients.js.map +1 -0
  103. package/dist/bigconsole/pages/DashboardBuilderPage.d.ts.map +1 -1
  104. package/dist/bigconsole/pages/DashboardBuilderPage.js +198 -193
  105. package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
  106. package/dist/bigconsole/pages/DashboardViewPage.d.ts.map +1 -1
  107. package/dist/bigconsole/pages/DashboardViewPage.js +171 -167
  108. package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
  109. package/dist/bigconsole/pages/DashboardsPage.d.ts.map +1 -1
  110. package/dist/bigconsole/pages/DashboardsPage.js +313 -266
  111. package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
  112. package/dist/bigconsole/pages/DataSinkBuilderPage.d.ts.map +1 -1
  113. package/dist/bigconsole/pages/DataSinkBuilderPage.js +84 -79
  114. package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
  115. package/dist/bigconsole/pages/DataSinkViewPage.d.ts.map +1 -1
  116. package/dist/bigconsole/pages/DataSinkViewPage.js +267 -257
  117. package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
  118. package/dist/bigconsole/pages/InsightsPage.d.ts.map +1 -1
  119. package/dist/bigconsole/pages/InsightsPage.js +465 -402
  120. package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
  121. package/dist/bigconsole/pages/PipelineBuilderPage.d.ts.map +1 -1
  122. package/dist/bigconsole/pages/PipelineBuilderPage.js +169 -157
  123. package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
  124. package/dist/bigconsole/pages/PipelineViewPage.d.ts.map +1 -1
  125. package/dist/bigconsole/pages/PipelineViewPage.js +370 -230
  126. package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
  127. package/dist/bigconsole/pages/PipelinesPage.d.ts.map +1 -1
  128. package/dist/bigconsole/pages/PipelinesPage.js +181 -151
  129. package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
  130. package/dist/bigconsole/pages/SettingsPage.d.ts.map +1 -1
  131. package/dist/bigconsole/pages/SettingsPage.js +188 -208
  132. package/dist/bigconsole/pages/SettingsPage.js.map +1 -1
  133. package/dist/bigconsole/pages/WidgetDemoPage.d.ts.map +1 -1
  134. package/dist/bigconsole/pages/WidgetDemoPage.js +134 -117
  135. package/dist/bigconsole/pages/WidgetDemoPage.js.map +1 -1
  136. package/dist/bigconsole/pages/WorkflowViewPage.d.ts.map +1 -1
  137. package/dist/bigconsole/pages/WorkflowViewPage.js +315 -267
  138. package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
  139. package/dist/bigconsole/pages/WorkflowsPage.d.ts.map +1 -1
  140. package/dist/bigconsole/pages/WorkflowsPage.js +4 -3
  141. package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
  142. package/dist/bigconsole/types/index.d.ts +4 -0
  143. package/dist/bigconsole/types/index.d.ts.map +1 -1
  144. package/dist/bigconsole/utils/drilldownParams.d.ts +3 -0
  145. package/dist/bigconsole/utils/drilldownParams.d.ts.map +1 -0
  146. package/dist/bigconsole/utils/drilldownParams.js +8 -0
  147. package/dist/bigconsole/utils/drilldownParams.js.map +1 -0
  148. package/dist/generated/wspace-operations.d.ts +148 -0
  149. package/dist/generated/wspace-operations.d.ts.map +1 -1
  150. package/dist/generated/wspace-operations.js +172 -133
  151. package/dist/generated/wspace-operations.js.map +1 -1
  152. package/dist/generated/wspace-types.d.ts +21 -0
  153. package/dist/generated/wspace-types.d.ts.map +1 -1
  154. package/dist/generated/wspace-types.js.map +1 -1
  155. package/dist/microfe-bigconsole.css +1 -1
  156. package/dist/translations/en.d.ts.map +1 -1
  157. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"useDashboardOperations.js","names":[],"sources":["../../../src/bigconsole/hooks/useDashboardOperations.ts"],"sourcesContent":["/**\n * useDashboardOperations Hook\n *\n * Provides CRUD operations for dashboards using GraphQL.\n */\n\nimport { useCallback, useState, useEffect } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport type { Dashboard, DashboardLayoutConfig, DashboardVisibility } from '../types';\nimport { useDashboardStore } from '../store';\nimport { createBigConsoleQuotaError, isBigConsoleQuotaError } from '../utils/quota';\nimport {\n ListDashboardsDocument,\n GetDashboardDocument,\n CreateDashboardDocument,\n UpdateDashboardDocument,\n DeleteDashboardDocument,\n CloneDashboardDocument,\n} from '../../generated/wspace-operations';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Lifecycle status of a dashboard (`Dashboard.status`, DashboardStatus enum).\n * DRAFT until it is published; ARCHIVED after a soft delete.\n */\nexport type DashboardListStatus = 'DRAFT' | 'PUBLISHED' | 'ARCHIVED';\n\nexport interface DashboardListItem {\n id: string;\n name: string;\n description?: string;\n category?: string;\n isPublic?: boolean;\n /** Undefined only if the field is missing from the response. */\n status?: DashboardListStatus;\n viewCount?: number;\n widgetCount?: number;\n createdAt: string;\n updatedAt: string;\n pageCount: number;\n}\n\ninterface CreateDashboardInput {\n name: string;\n description?: string;\n category?: string;\n layoutType?: string;\n tags?: string[];\n}\n\ninterface UpdateDashboardInput {\n id: string;\n name?: string;\n description?: string;\n category?: string;\n isPublic?: boolean;\n /** General access level (`BigConsoleUpdateDashboardInput.visibility`). */\n visibility?: DashboardVisibility;\n}\n\ninterface FetchDashboardOptions {\n /**\n * Silent refetch for live-canvas polling: do NOT toggle the global `loading`\n * flag (which would flash the page-level spinner) and do NOT surface a\n * transient fetch error (which would replace the board with the error screen).\n * The last good dashboard stays on screen if a poll fails.\n */\n silent?: boolean;\n}\n\ninterface DashboardOperationsResult {\n loading: boolean;\n error: Error | null;\n dashboards: DashboardListItem[];\n currentDashboard: Dashboard | null;\n\n // Operations\n fetchDashboards: (search?: string) => Promise<void>;\n fetchDashboard: (id: string, options?: FetchDashboardOptions) => Promise<Dashboard | null>;\n createDashboard: (input: CreateDashboardInput) => Promise<Dashboard | null>;\n updateDashboard: (input: UpdateDashboardInput) => Promise<Dashboard | null>;\n deleteDashboard: (id: string) => Promise<boolean>;\n cloneDashboard: (sourceDashboardId: string, name: string) => Promise<Dashboard | null>;\n refetch: () => Promise<void>;\n}\n\n// ============================================================================\n// Helper: Normalize dashboard from GraphQL response\n// ============================================================================\n\n// Raw GraphQL response shape for dashboard data\ninterface RawDashboardData {\n id: string;\n name?: string;\n description?: string;\n workspaceId?: string;\n isPublic?: boolean;\n isTemplate?: boolean;\n layout?: Record<string, unknown>;\n category?: string;\n status?: string;\n visibility?: string;\n viewCount?: number;\n widgetCount?: number;\n createdBy?: string;\n ownerId?: string;\n createdAt?: string;\n updatedAt?: string;\n pages?: {\n edges?: Array<{\n node: {\n id: string;\n dashboardId?: string;\n name?: string;\n order?: number;\n layout?: Record<string, unknown>;\n gridColumns?: number;\n createdAt?: string;\n updatedAt?: string;\n };\n }>;\n totalCount?: number;\n };\n}\n\n/**\n * Narrow the raw `visibility` string to the DashboardVisibility enum. An\n * unrecognised value yields `undefined`, which hides the general-access control\n * rather than showing a level the backend never returned.\n */\nfunction toDashboardVisibility(raw: string | undefined): DashboardVisibility | undefined {\n const upper = raw?.toUpperCase();\n return upper === 'PRIVATE' || upper === 'WORKSPACE' || upper === 'PUBLIC' ? upper : undefined;\n}\n\nfunction normalizeDashboard(data: RawDashboardData): Dashboard {\n const pages =\n data.pages?.edges?.map((edge) => ({\n id: edge.node.id,\n dashboardId: edge.node.dashboardId || data.id,\n name: edge.node.name,\n slug: edge.node.name?.toLowerCase().replace(/\\s+/g, '-') || '',\n order: edge.node.order || 0,\n layoutConfig: edge.node.layout || {},\n gridColumns: edge.node.gridColumns || 12,\n widgets: [], // Widgets loaded separately via useWidgetOperations\n createdAt: edge.node.createdAt || new Date().toISOString(),\n updatedAt: edge.node.updatedAt || new Date().toISOString(),\n })) || [];\n\n // GetDashboard caps pages at first:10 to stay under the gateway cost-limit\n // (see DashboardFullFields fragment / BOFF-2720). Surface a warning instead of\n // silently dropping pages if a dashboard ever exceeds the cap.\n const totalPages = data.pages?.totalCount ?? pages.length;\n if (totalPages > pages.length) {\n console.warn(\n `[bigconsole] Dashboard ${data.id} has ${totalPages} pages but only ${pages.length} were loaded ` +\n `(GetDashboard pages cap). Pages beyond the cap are not shown — see BOFF-2720.`\n );\n }\n\n return {\n id: data.id,\n workspaceId: data.workspaceId || '',\n name: data.name || 'Untitled Dashboard',\n description: data.description,\n slug: data.name?.toLowerCase().replace(/\\s+/g, '-') || '',\n isPublished: data.isPublic || false,\n isTemplate: data.isTemplate || false,\n // Selected by DashboardFullFields; it was parsed away here, so the share\n // dialog had no way to know the board's current general-access level.\n visibility: toDashboardVisibility(data.visibility),\n layoutConfig: (data.layout as unknown as DashboardLayoutConfig) || {\n columns: 12,\n rowHeight: 100,\n gap: 10,\n margin: [10, 10],\n containerPadding: [10, 10],\n },\n pages,\n translations: [],\n createdBy: data.createdBy || data.ownerId || '',\n createdAt: data.createdAt || new Date().toISOString(),\n updatedAt: data.updatedAt || new Date().toISOString(),\n };\n}\n\n/**\n * Narrow the raw `status` string to the DashboardStatus enum.\n *\n * The gateway returns the SCREAMING_SNAKE enum name, but normalising defensively\n * (rather than casting) means an unexpected value renders nothing instead of an\n * unstyled badge claiming a status the backend never defined.\n */\nfunction toDashboardListStatus(raw: string | undefined): DashboardListStatus | undefined {\n const upper = raw?.toUpperCase();\n return upper === 'DRAFT' || upper === 'PUBLISHED' || upper === 'ARCHIVED' ? upper : undefined;\n}\n\nfunction normalizeDashboardListItem(data: RawDashboardData): DashboardListItem {\n // The list query selects only pages.totalCount (no edges) to stay under the\n // gateway Armor cost-limit — see DashboardListItem fragment / BOFF-2719.\n return {\n id: data.id,\n name: data.name || 'Untitled Dashboard',\n description: data.description,\n category: data.category,\n isPublic: data.isPublic,\n status: toDashboardListStatus(data.status),\n viewCount: data.viewCount,\n widgetCount: data.widgetCount,\n createdAt: data.createdAt || new Date().toISOString(),\n updatedAt: data.updatedAt || new Date().toISOString(),\n pageCount: data.pages?.totalCount ?? 0,\n };\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport interface UseDashboardOperationsOptions {\n /**\n * Skip the mount-time `listDashboards` fetch. For callers that only need a\n * mutation (e.g. the share dialog changing visibility) and must not pull the\n * whole dashboard list to do it.\n */\n skipFetch?: boolean;\n}\n\nexport function useDashboardOperations({\n skipFetch = false,\n}: UseDashboardOperationsOptions = {}): DashboardOperationsResult {\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [dashboards, setDashboards] = useState<DashboardListItem[]>([]);\n\n // Get Apollo Client - will throw if not in ApolloProvider context\n const apolloClient = useApolloClient();\n\n // Store actions\n const currentDashboard = useDashboardStore((state) => state.currentDashboard);\n const setCurrentDashboard = useDashboardStore((state) => state.setCurrentDashboard);\n\n // Fetch dashboards list\n const fetchDashboards = useCallback(\n async (search?: string) => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.query<{ listDashboards?: { edges?: Array<{ node: RawDashboardData }> } }>({\n query: ListDashboardsDocument,\n variables: {\n first: 50,\n search,\n includeArchived: false,\n },\n fetchPolicy: 'network-only',\n });\n\n const edges = result.data?.listDashboards?.edges || [];\n const dashboardsList = edges.map((edge) => normalizeDashboardListItem(edge.node));\n setDashboards(dashboardsList);\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch dashboards');\n setError(error);\n } finally {\n setLoading(false);\n }\n },\n [apolloClient]\n );\n\n // Fetch single dashboard\n const fetchDashboard = useCallback(\n async (id: string, options?: FetchDashboardOptions): Promise<Dashboard | null> => {\n const silent = options?.silent ?? false;\n try {\n if (!silent) {\n setLoading(true);\n setError(null);\n }\n\n const result = await apolloClient.query<{ getDashboard?: RawDashboardData }>({\n query: GetDashboardDocument,\n variables: { id },\n fetchPolicy: 'network-only',\n });\n\n if (result.data?.getDashboard) {\n const dashboard = normalizeDashboard(result.data.getDashboard);\n setCurrentDashboard(dashboard);\n return dashboard;\n }\n\n return null;\n } catch (err) {\n // Silent (live-poll) failures must not wipe the board or flip the page to\n // the error screen — keep the last good dashboard on screen.\n if (!silent) {\n const error = err instanceof Error ? err : new Error('Failed to fetch dashboard');\n setError(error);\n }\n return null;\n } finally {\n if (!silent) {\n setLoading(false);\n }\n }\n },\n [apolloClient, setCurrentDashboard]\n );\n\n // Create dashboard\n const createDashboard = useCallback(\n async (input: CreateDashboardInput): Promise<Dashboard | null> => {\n try {\n setLoading(true);\n setError(null);\n\n // Build mutation input\n // Note: Tags require Tag IDs from the Tags service. For now, tags are stored in metadata.\n // Full tag integration would require querying/creating tags via the Tags service first.\n const mutationInput: Record<string, unknown> = {\n name: input.name,\n description: input.description,\n category: input.category || 'CUSTOM',\n layout: input.layoutType || 'GRID',\n createDefaultPage: true,\n };\n\n // Store tags in metadata until full Tags service integration\n if (input.tags && input.tags.length > 0) {\n mutationInput.metadata = {\n tags: input.tags,\n };\n }\n\n const result = await apolloClient.mutate<{ createBigConsoleDashboard?: RawDashboardData }>({\n mutation: CreateDashboardDocument,\n variables: {\n input: mutationInput,\n },\n });\n\n if (isBigConsoleQuotaError(result)) {\n throw createBigConsoleQuotaError(result);\n }\n\n if (result.data?.createBigConsoleDashboard) {\n const newDashboard = normalizeDashboard(result.data.createBigConsoleDashboard);\n await fetchDashboards();\n return newDashboard;\n }\n\n return null;\n } catch (err) {\n if (isBigConsoleQuotaError(err)) {\n throw err;\n }\n const error = err instanceof Error ? err : new Error('Failed to create dashboard');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, fetchDashboards]\n );\n\n // Update dashboard\n const updateDashboard = useCallback(\n async (input: UpdateDashboardInput): Promise<Dashboard | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.mutate<{ updateBigConsoleDashboard?: RawDashboardData }>({\n mutation: UpdateDashboardDocument,\n variables: { input },\n });\n\n if (result.data?.updateBigConsoleDashboard) {\n const updatedDashboard = normalizeDashboard(result.data.updateBigConsoleDashboard);\n if (currentDashboard?.id === input.id) {\n setCurrentDashboard(updatedDashboard);\n }\n await fetchDashboards();\n return updatedDashboard;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to update dashboard');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, currentDashboard?.id, setCurrentDashboard, fetchDashboards]\n );\n\n // Delete dashboard\n const deleteDashboard = useCallback(\n async (id: string): Promise<boolean> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.mutate<{ deleteBigConsoleDashboard?: boolean }>({\n mutation: DeleteDashboardDocument,\n variables: { id },\n });\n\n if (result.data?.deleteBigConsoleDashboard) {\n if (currentDashboard?.id === id) {\n setCurrentDashboard(null);\n }\n await fetchDashboards();\n return true;\n }\n\n return false;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to delete dashboard');\n setError(error);\n return false;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, currentDashboard?.id, setCurrentDashboard, fetchDashboards]\n );\n\n // Clone dashboard (duplicate including widgets)\n const cloneDashboard = useCallback(\n async (sourceDashboardId: string, name: string): Promise<Dashboard | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.mutate<{ cloneDashboard?: RawDashboardData }>({\n mutation: CloneDashboardDocument,\n variables: {\n input: { sourceDashboardId, name, includeWidgets: true },\n },\n });\n\n if (isBigConsoleQuotaError(result)) {\n throw createBigConsoleQuotaError(result);\n }\n\n if (result.data?.cloneDashboard) {\n const cloned = normalizeDashboard(result.data.cloneDashboard);\n await fetchDashboards();\n return cloned;\n }\n\n return null;\n } catch (err) {\n if (isBigConsoleQuotaError(err)) {\n throw err;\n }\n const error = err instanceof Error ? err : new Error('Failed to clone dashboard');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, fetchDashboards]\n );\n\n // Refetch\n const refetch = useCallback(async () => {\n await fetchDashboards();\n }, [fetchDashboards]);\n\n // Fetch on mount (unless the caller only wants the mutations)\n useEffect(() => {\n if (skipFetch) return;\n fetchDashboards();\n }, [fetchDashboards, skipFetch]);\n\n return {\n loading,\n error,\n dashboards,\n currentDashboard,\n fetchDashboards,\n fetchDashboard,\n createDashboard,\n updateDashboard,\n deleteDashboard,\n cloneDashboard,\n refetch,\n };\n}\n\nexport default useDashboardOperations;\n"],"mappings":"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"}
1
+ {"version":3,"file":"useDashboardOperations.js","names":[],"sources":["../../../src/bigconsole/hooks/useDashboardOperations.ts"],"sourcesContent":["/**\n * useDashboardOperations Hook\n *\n * Provides CRUD operations for dashboards using GraphQL.\n */\n\nimport { useCallback, useState, useEffect, useRef, useLayoutEffect } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport type { Dashboard, DashboardLayoutConfig, DashboardVisibility } from '../types';\nimport { useDashboardStore } from '../store';\nimport { useBigConsole } from '../context';\nimport { createBigConsoleQuotaError, isBigConsoleQuotaError } from '../utils/quota';\nimport {\n ListDashboardsDocument,\n GetDashboardDocument,\n CreateDashboardDocument,\n UpdateDashboardDocument,\n DeleteDashboardDocument,\n CloneDashboardDocument,\n} from '../../generated/wspace-operations';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Lifecycle status of a dashboard (`Dashboard.status`, DashboardStatus enum).\n * DRAFT until it is published; ARCHIVED after a soft delete.\n */\nexport type DashboardListStatus = 'DRAFT' | 'PUBLISHED' | 'ARCHIVED';\n\nexport interface DashboardListItem {\n id: string;\n name: string;\n description?: string;\n category?: string;\n isPublic?: boolean;\n /** Undefined only if the field is missing from the response. */\n status?: DashboardListStatus;\n viewCount?: number;\n widgetCount?: number;\n createdAt: string;\n updatedAt: string;\n pageCount: number;\n}\n\ninterface CreateDashboardInput {\n name: string;\n description?: string;\n category?: string;\n layoutType?: string;\n tags?: string[];\n}\n\ninterface UpdateDashboardInput {\n id: string;\n name?: string;\n description?: string;\n category?: string;\n isPublic?: boolean;\n /** General access level (`BigConsoleUpdateDashboardInput.visibility`). */\n visibility?: DashboardVisibility;\n}\n\ninterface FetchDashboardOptions {\n /**\n * Silent refetch for live-canvas polling: do NOT toggle the global `loading`\n * flag (which would flash the page-level spinner) and do NOT surface a\n * transient fetch error (which would replace the board with the error screen).\n * The last good dashboard stays on screen if a poll fails.\n */\n silent?: boolean;\n}\n\ninterface DashboardOperationsResult {\n loading: boolean;\n error: Error | null;\n dashboards: DashboardListItem[];\n currentDashboard: Dashboard | null;\n\n // Operations\n fetchDashboards: (search?: string) => Promise<void>;\n fetchDashboard: (id: string, options?: FetchDashboardOptions) => Promise<Dashboard | null>;\n createDashboard: (input: CreateDashboardInput) => Promise<Dashboard | null>;\n updateDashboard: (input: UpdateDashboardInput) => Promise<Dashboard | null>;\n deleteDashboard: (id: string) => Promise<boolean>;\n cloneDashboard: (sourceDashboardId: string, name: string) => Promise<Dashboard | null>;\n refetch: () => Promise<void>;\n}\n\n// ============================================================================\n// Helper: Normalize dashboard from GraphQL response\n// ============================================================================\n\n// Raw GraphQL response shape for dashboard data\ninterface RawDashboardData {\n id: string;\n name?: string;\n description?: string;\n workspaceId?: string;\n isPublic?: boolean;\n isTemplate?: boolean;\n layout?: Record<string, unknown>;\n category?: string;\n status?: string;\n visibility?: string;\n viewCount?: number;\n widgetCount?: number;\n createdBy?: string;\n ownerId?: string;\n createdAt?: string;\n updatedAt?: string;\n pages?: {\n edges?: Array<{\n node: {\n id: string;\n dashboardId?: string;\n name?: string;\n order?: number;\n layout?: Record<string, unknown>;\n gridColumns?: number;\n createdAt?: string;\n updatedAt?: string;\n };\n }>;\n totalCount?: number;\n };\n}\n\n/**\n * Narrow the raw `visibility` string to the DashboardVisibility enum. An\n * unrecognised value yields `undefined`, which hides the general-access control\n * rather than showing a level the backend never returned.\n */\nfunction toDashboardVisibility(raw: string | undefined): DashboardVisibility | undefined {\n const upper = raw?.toUpperCase();\n return upper === 'PRIVATE' || upper === 'WORKSPACE' || upper === 'PUBLIC' ? upper : undefined;\n}\n\nfunction normalizeDashboard(data: RawDashboardData): Dashboard {\n const pages =\n data.pages?.edges?.map((edge) => ({\n id: edge.node.id,\n dashboardId: edge.node.dashboardId || data.id,\n name: edge.node.name,\n slug: edge.node.name?.toLowerCase().replace(/\\s+/g, '-') || '',\n order: edge.node.order || 0,\n layoutConfig: edge.node.layout || {},\n gridColumns: edge.node.gridColumns || 12,\n widgets: [], // Widgets loaded separately via useWidgetOperations\n createdAt: edge.node.createdAt || new Date().toISOString(),\n updatedAt: edge.node.updatedAt || new Date().toISOString(),\n })) || [];\n\n // GetDashboard caps pages at first:10 to stay under the gateway cost-limit\n // (see DashboardFullFields fragment / BOFF-2720). Surface a warning instead of\n // silently dropping pages if a dashboard ever exceeds the cap.\n const totalPages = data.pages?.totalCount ?? pages.length;\n if (totalPages > pages.length) {\n console.warn(\n `[bigconsole] Dashboard ${data.id} has ${totalPages} pages but only ${pages.length} were loaded ` +\n `(GetDashboard pages cap). Pages beyond the cap are not shown — see BOFF-2720.`\n );\n }\n\n return {\n id: data.id,\n workspaceId: data.workspaceId || '',\n name: data.name || 'Untitled Dashboard',\n description: data.description,\n slug: data.name?.toLowerCase().replace(/\\s+/g, '-') || '',\n isPublished: data.isPublic || false,\n isTemplate: data.isTemplate || false,\n // Selected by DashboardFullFields; it was parsed away here, so the share\n // dialog had no way to know the board's current general-access level.\n visibility: toDashboardVisibility(data.visibility),\n layoutConfig: (data.layout as unknown as DashboardLayoutConfig) || {\n columns: 12,\n rowHeight: 100,\n gap: 10,\n margin: [10, 10],\n containerPadding: [10, 10],\n },\n pages,\n translations: [],\n createdBy: data.createdBy || data.ownerId || '',\n createdAt: data.createdAt || new Date().toISOString(),\n updatedAt: data.updatedAt || new Date().toISOString(),\n };\n}\n\n/**\n * Narrow the raw `status` string to the DashboardStatus enum.\n *\n * The gateway returns the SCREAMING_SNAKE enum name, but normalising defensively\n * (rather than casting) means an unexpected value renders nothing instead of an\n * unstyled badge claiming a status the backend never defined.\n */\nfunction toDashboardListStatus(raw: string | undefined): DashboardListStatus | undefined {\n const upper = raw?.toUpperCase();\n return upper === 'DRAFT' || upper === 'PUBLISHED' || upper === 'ARCHIVED' ? upper : undefined;\n}\n\nfunction normalizeDashboardListItem(data: RawDashboardData): DashboardListItem {\n // The list query selects only pages.totalCount (no edges) to stay under the\n // gateway Armor cost-limit — see DashboardListItem fragment / BOFF-2719.\n return {\n id: data.id,\n name: data.name || 'Untitled Dashboard',\n description: data.description,\n category: data.category,\n isPublic: data.isPublic,\n status: toDashboardListStatus(data.status),\n viewCount: data.viewCount,\n widgetCount: data.widgetCount,\n createdAt: data.createdAt || new Date().toISOString(),\n updatedAt: data.updatedAt || new Date().toISOString(),\n pageCount: data.pages?.totalCount ?? 0,\n };\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport interface UseDashboardOperationsOptions {\n /**\n * Skip the mount-time `listDashboards` fetch. For callers that only need a\n * mutation (e.g. the share dialog changing visibility) and must not pull the\n * whole dashboard list to do it.\n */\n skipFetch?: boolean;\n}\n\nexport function useDashboardOperations({\n skipFetch = false,\n}: UseDashboardOperationsOptions = {}): DashboardOperationsResult {\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [dashboards, setDashboards] = useState<DashboardListItem[]>([]);\n const { workspaceId, currentUser } = useBigConsole();\n const owner = `${workspaceId ?? ''}:${currentUser?.id ?? ''}`;\n const activeOwner = useRef(owner);\n const listRequest = useRef(0);\n const [loadedOwner, setLoadedOwner] = useState(owner);\n useLayoutEffect(() => {\n activeOwner.current = owner;\n return () => {\n activeOwner.current = '';\n listRequest.current += 1;\n };\n }, [owner]);\n\n // Get Apollo Client - will throw if not in ApolloProvider context\n const apolloClient = useApolloClient();\n\n // Store actions\n const currentDashboard = useDashboardStore((state) => state.currentDashboard);\n const setCurrentDashboard = useDashboardStore((state) => state.setCurrentDashboard);\n\n // Fetch dashboards list\n const fetchDashboards = useCallback(\n async (search?: string) => {\n const request = ++listRequest.current;\n const isCurrent = () => activeOwner.current === owner && listRequest.current === request;\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.query<{ listDashboards?: { edges?: Array<{ node: RawDashboardData }> } }>({\n query: ListDashboardsDocument,\n variables: {\n first: 50,\n search,\n includeArchived: false,\n },\n // Identical queries can otherwise join a request started before a\n // workspace switch. Bind this read to its owner and bypass deduplication.\n fetchPolicy: 'no-cache',\n errorPolicy: 'none',\n context: { requiredWorkspaceAuth: true, requiredWorkspaceId: workspaceId, queryDeduplication: false },\n });\n\n if (!isCurrent()) return;\n const edges = result.data?.listDashboards?.edges || [];\n if (workspaceId && edges.some((edge) => edge.node.workspaceId !== workspaceId)) {\n throw new Error('The active workspace changed. Please try again.');\n }\n const dashboardsList = edges.map((edge) => normalizeDashboardListItem(edge.node));\n setDashboards(dashboardsList);\n setLoadedOwner(owner);\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch dashboards');\n if (isCurrent()) setError(error);\n } finally {\n if (isCurrent()) setLoading(false);\n }\n },\n [apolloClient, owner, workspaceId]\n );\n\n // Fetch single dashboard\n const fetchDashboard = useCallback(\n async (id: string, options?: FetchDashboardOptions): Promise<Dashboard | null> => {\n const silent = options?.silent ?? false;\n try {\n if (!silent) {\n setLoading(true);\n setError(null);\n }\n\n const result = await apolloClient.query<{ getDashboard?: RawDashboardData }>({\n query: GetDashboardDocument,\n variables: { id },\n fetchPolicy: 'network-only',\n });\n\n if (result.data?.getDashboard) {\n const dashboard = normalizeDashboard(result.data.getDashboard);\n setCurrentDashboard(dashboard);\n return dashboard;\n }\n\n return null;\n } catch (err) {\n // Silent (live-poll) failures must not wipe the board or flip the page to\n // the error screen — keep the last good dashboard on screen.\n if (!silent) {\n const error = err instanceof Error ? err : new Error('Failed to fetch dashboard');\n setError(error);\n }\n return null;\n } finally {\n if (!silent) {\n setLoading(false);\n }\n }\n },\n [apolloClient, setCurrentDashboard]\n );\n\n // Create dashboard\n const createDashboard = useCallback(\n async (input: CreateDashboardInput): Promise<Dashboard | null> => {\n try {\n setLoading(true);\n setError(null);\n\n // Build mutation input\n // Note: Tags require Tag IDs from the Tags service. For now, tags are stored in metadata.\n // Full tag integration would require querying/creating tags via the Tags service first.\n const mutationInput: Record<string, unknown> = {\n name: input.name,\n description: input.description,\n category: input.category || 'CUSTOM',\n layout: input.layoutType || 'GRID',\n createDefaultPage: true,\n };\n\n // Store tags in metadata until full Tags service integration\n if (input.tags && input.tags.length > 0) {\n mutationInput.metadata = {\n tags: input.tags,\n };\n }\n\n const result = await apolloClient.mutate<{ createBigConsoleDashboard?: RawDashboardData }>({\n mutation: CreateDashboardDocument,\n variables: {\n input: mutationInput,\n },\n });\n\n if (isBigConsoleQuotaError(result)) {\n throw createBigConsoleQuotaError(result);\n }\n\n if (result.data?.createBigConsoleDashboard) {\n const newDashboard = normalizeDashboard(result.data.createBigConsoleDashboard);\n await fetchDashboards();\n return newDashboard;\n }\n\n return null;\n } catch (err) {\n if (isBigConsoleQuotaError(err)) {\n throw err;\n }\n const error = err instanceof Error ? err : new Error('Failed to create dashboard');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, fetchDashboards]\n );\n\n // Update dashboard\n const updateDashboard = useCallback(\n async (input: UpdateDashboardInput): Promise<Dashboard | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.mutate<{ updateBigConsoleDashboard?: RawDashboardData }>({\n mutation: UpdateDashboardDocument,\n variables: { input },\n });\n\n if (result.data?.updateBigConsoleDashboard) {\n const updatedDashboard = normalizeDashboard(result.data.updateBigConsoleDashboard);\n if (currentDashboard?.id === input.id) {\n setCurrentDashboard(updatedDashboard);\n }\n await fetchDashboards();\n return updatedDashboard;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to update dashboard');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, currentDashboard?.id, setCurrentDashboard, fetchDashboards]\n );\n\n // Delete dashboard\n const deleteDashboard = useCallback(\n async (id: string): Promise<boolean> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.mutate<{ deleteBigConsoleDashboard?: boolean }>({\n mutation: DeleteDashboardDocument,\n variables: { id },\n });\n\n if (result.data?.deleteBigConsoleDashboard) {\n if (currentDashboard?.id === id) {\n setCurrentDashboard(null);\n }\n await fetchDashboards();\n return true;\n }\n\n return false;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to delete dashboard');\n setError(error);\n return false;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, currentDashboard?.id, setCurrentDashboard, fetchDashboards]\n );\n\n // Clone dashboard (duplicate including widgets)\n const cloneDashboard = useCallback(\n async (sourceDashboardId: string, name: string): Promise<Dashboard | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.mutate<{ cloneDashboard?: RawDashboardData }>({\n mutation: CloneDashboardDocument,\n variables: {\n input: { sourceDashboardId, name, includeWidgets: true },\n },\n });\n\n if (isBigConsoleQuotaError(result)) {\n throw createBigConsoleQuotaError(result);\n }\n\n if (result.data?.cloneDashboard) {\n const cloned = normalizeDashboard(result.data.cloneDashboard);\n await fetchDashboards();\n return cloned;\n }\n\n return null;\n } catch (err) {\n if (isBigConsoleQuotaError(err)) {\n throw err;\n }\n const error = err instanceof Error ? err : new Error('Failed to clone dashboard');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, fetchDashboards]\n );\n\n // Refetch\n const refetch = useCallback(async () => {\n await fetchDashboards();\n }, [fetchDashboards]);\n\n // Fetch on mount (unless the caller only wants the mutations)\n useEffect(() => {\n if (skipFetch) return;\n fetchDashboards();\n }, [fetchDashboards, skipFetch]);\n\n return {\n loading,\n error,\n dashboards: loadedOwner === owner ? dashboards : [],\n currentDashboard,\n fetchDashboards,\n fetchDashboard,\n createDashboard,\n updateDashboard,\n deleteDashboard,\n cloneDashboard,\n refetch,\n };\n}\n\nexport default useDashboardOperations;\n"],"mappings":";;;;;;;;;AAsIA,SAAS,EAAsB,GAA0D;CACvF,IAAM,IAAQ,GAAK,aAAa;AAChC,QAAO,MAAU,aAAa,MAAU,eAAe,MAAU,WAAW,IAAQ,KAAA;;AAGtF,SAAS,EAAmB,GAAmC;CAC7D,IAAM,IACJ,EAAK,OAAO,OAAO,KAAK,OAAU;EAChC,IAAI,EAAK,KAAK;EACd,aAAa,EAAK,KAAK,eAAe,EAAK;EAC3C,MAAM,EAAK,KAAK;EAChB,MAAM,EAAK,KAAK,MAAM,aAAa,CAAC,QAAQ,QAAQ,IAAI,IAAI;EAC5D,OAAO,EAAK,KAAK,SAAS;EAC1B,cAAc,EAAK,KAAK,UAAU,EAAE;EACpC,aAAa,EAAK,KAAK,eAAe;EACtC,SAAS,EAAE;EACX,WAAW,EAAK,KAAK,8BAAa,IAAI,MAAM,EAAC,aAAa;EAC1D,WAAW,EAAK,KAAK,8BAAa,IAAI,MAAM,EAAC,aAAa;EAC3D,EAAE,IAAI,EAAE,EAKL,IAAa,EAAK,OAAO,cAAc,EAAM;AAQnD,QAPI,IAAa,EAAM,UACrB,QAAQ,KACN,0BAA0B,EAAK,GAAG,OAAO,EAAW,kBAAkB,EAAM,OAAO,4FAEpF,EAGI;EACL,IAAI,EAAK;EACT,aAAa,EAAK,eAAe;EACjC,MAAM,EAAK,QAAQ;EACnB,aAAa,EAAK;EAClB,MAAM,EAAK,MAAM,aAAa,CAAC,QAAQ,QAAQ,IAAI,IAAI;EACvD,aAAa,EAAK,YAAY;EAC9B,YAAY,EAAK,cAAc;EAG/B,YAAY,EAAsB,EAAK,WAAW;EAClD,cAAe,EAAK,UAA+C;GACjE,SAAS;GACT,WAAW;GACX,KAAK;GACL,QAAQ,CAAC,IAAI,GAAG;GAChB,kBAAkB,CAAC,IAAI,GAAG;GAC3B;EACD;EACA,cAAc,EAAE;EAChB,WAAW,EAAK,aAAa,EAAK,WAAW;EAC7C,WAAW,EAAK,8BAAa,IAAI,MAAM,EAAC,aAAa;EACrD,WAAW,EAAK,8BAAa,IAAI,MAAM,EAAC,aAAa;EACtD;;AAUH,SAAS,EAAsB,GAA0D;CACvF,IAAM,IAAQ,GAAK,aAAa;AAChC,QAAO,MAAU,WAAW,MAAU,eAAe,MAAU,aAAa,IAAQ,KAAA;;AAGtF,SAAS,EAA2B,GAA2C;AAG7E,QAAO;EACL,IAAI,EAAK;EACT,MAAM,EAAK,QAAQ;EACnB,aAAa,EAAK;EAClB,UAAU,EAAK;EACf,UAAU,EAAK;EACf,QAAQ,EAAsB,EAAK,OAAO;EAC1C,WAAW,EAAK;EAChB,aAAa,EAAK;EAClB,WAAW,EAAK,8BAAa,IAAI,MAAM,EAAC,aAAa;EACrD,WAAW,EAAK,8BAAa,IAAI,MAAM,EAAC,aAAa;EACrD,WAAW,EAAK,OAAO,cAAc;EACtC;;AAgBH,SAAgB,EAAuB,EACrC,eAAY,OACqB,EAAE,EAA6B;CAChE,IAAM,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAO,KAAY,EAAuB,KAAK,EAChD,CAAC,GAAY,KAAiB,EAA8B,EAAE,CAAC,EAC/D,EAAE,gBAAa,mBAAgB,GAAe,EAC9C,IAAQ,GAAG,KAAe,GAAG,GAAG,GAAa,MAAM,MACnD,IAAc,EAAO,EAAM,EAC3B,IAAc,EAAO,EAAE,EACvB,CAAC,GAAa,KAAkB,EAAS,EAAM;AACrD,UACE,EAAY,UAAU,SACT;AAEX,EADA,EAAY,UAAU,IACtB,EAAY,WAAW;KAExB,CAAC,EAAM,CAAC;CAGX,IAAM,IAAe,GAAiB,EAGhC,IAAmB,GAAmB,MAAU,EAAM,iBAAiB,EACvE,IAAsB,GAAmB,MAAU,EAAM,oBAAoB,EAG7E,IAAkB,EACtB,OAAO,MAAoB;EACzB,IAAM,IAAU,EAAE,EAAY,SACxB,UAAkB,EAAY,YAAY,KAAS,EAAY,YAAY;AACjF,MAAI;AAEF,GADA,EAAW,GAAK,EAChB,EAAS,KAAK;GAEd,IAAM,IAAS,MAAM,EAAa,MAA0E;IAC1G,OAAO;IACP,WAAW;KACT,OAAO;KACP;KACA,iBAAiB;KAClB;IAGD,aAAa;IACb,aAAa;IACb,SAAS;KAAE,uBAAuB;KAAM,qBAAqB;KAAa,oBAAoB;KAAO;IACtG,CAAC;AAEF,OAAI,CAAC,GAAW,CAAE;GAClB,IAAM,IAAQ,EAAO,MAAM,gBAAgB,SAAS,EAAE;AACtD,OAAI,KAAe,EAAM,MAAM,MAAS,EAAK,KAAK,gBAAgB,EAAY,CAC5E,OAAU,MAAM,kDAAkD;AAIpE,GADA,EADuB,EAAM,KAAK,MAAS,EAA2B,EAAK,KAAK,CAAC,CACpD,EAC7B,EAAe,EAAM;WACd,GAAK;GACZ,IAAM,IAAQ,aAAe,QAAQ,IAAM,gBAAI,MAAM,6BAA6B;AAClF,GAAI,GAAW,IAAE,EAAS,EAAM;YACxB;AACR,GAAI,GAAW,IAAE,EAAW,GAAM;;IAGtC;EAAC;EAAc;EAAO;EAAY,CACnC,EAGK,IAAiB,EACrB,OAAO,GAAY,MAA+D;EAChF,IAAM,IAAS,GAAS,UAAU;AAClC,MAAI;AACF,GAAK,MACH,EAAW,GAAK,EAChB,EAAS,KAAK;GAGhB,IAAM,IAAS,MAAM,EAAa,MAA2C;IAC3E,OAAO;IACP,WAAW,EAAE,OAAI;IACjB,aAAa;IACd,CAAC;AAEF,OAAI,EAAO,MAAM,cAAc;IAC7B,IAAM,IAAY,EAAmB,EAAO,KAAK,aAAa;AAE9D,WADA,EAAoB,EAAU,EACvB;;AAGT,UAAO;WACA,GAAK;AAOZ,UAJK,KAEH,EADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,4BAA4B,CAClE,EAEV;YACC;AACR,GAAK,KACH,EAAW,GAAM;;IAIvB,CAAC,GAAc,EAAoB,CACpC,EAGK,IAAkB,EACtB,OAAO,MAA2D;AAChE,MAAI;AAEF,GADA,EAAW,GAAK,EAChB,EAAS,KAAK;GAKd,IAAM,IAAyC;IAC7C,MAAM,EAAM;IACZ,aAAa,EAAM;IACnB,UAAU,EAAM,YAAY;IAC5B,QAAQ,EAAM,cAAc;IAC5B,mBAAmB;IACpB;AAGD,GAAI,EAAM,QAAQ,EAAM,KAAK,SAAS,MACpC,EAAc,WAAW,EACvB,MAAM,EAAM,MACb;GAGH,IAAM,IAAS,MAAM,EAAa,OAAyD;IACzF,UAAU;IACV,WAAW,EACT,OAAO,GACR;IACF,CAAC;AAEF,OAAI,EAAuB,EAAO,CAChC,OAAM,EAA2B,EAAO;AAG1C,OAAI,EAAO,MAAM,2BAA2B;IAC1C,IAAM,IAAe,EAAmB,EAAO,KAAK,0BAA0B;AAE9E,WADA,MAAM,GAAiB,EAChB;;AAGT,UAAO;WACA,GAAK;AACZ,OAAI,EAAuB,EAAI,CAC7B,OAAM;AAIR,UADA,EADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,6BAA6B,CACnE,EACR;YACC;AACR,KAAW,GAAM;;IAGrB,CAAC,GAAc,EAAgB,CAChC,EAGK,IAAkB,EACtB,OAAO,MAA2D;AAChE,MAAI;AAEF,GADA,EAAW,GAAK,EAChB,EAAS,KAAK;GAEd,IAAM,IAAS,MAAM,EAAa,OAAyD;IACzF,UAAU;IACV,WAAW,EAAE,UAAO;IACrB,CAAC;AAEF,OAAI,EAAO,MAAM,2BAA2B;IAC1C,IAAM,IAAmB,EAAmB,EAAO,KAAK,0BAA0B;AAKlF,WAJI,GAAkB,OAAO,EAAM,MACjC,EAAoB,EAAiB,EAEvC,MAAM,GAAiB,EAChB;;AAGT,UAAO;WACA,GAAK;AAGZ,UADA,EADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,6BAA6B,CACnE,EACR;YACC;AACR,KAAW,GAAM;;IAGrB;EAAC;EAAc,GAAkB;EAAI;EAAqB;EAAgB,CAC3E,EAGK,IAAkB,EACtB,OAAO,MAAiC;AACtC,MAAI;AAiBF,UAhBA,EAAW,GAAK,EAChB,EAAS,KAAK,GAEC,MAAM,EAAa,OAAgD;IAChF,UAAU;IACV,WAAW,EAAE,OAAI;IAClB,CAAC,EAES,MAAM,6BACX,GAAkB,OAAO,KAC3B,EAAoB,KAAK,EAE3B,MAAM,GAAiB,EAChB,MAGF;WACA,GAAK;AAGZ,UADA,EADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,6BAA6B,CACnE,EACR;YACC;AACR,KAAW,GAAM;;IAGrB;EAAC;EAAc,GAAkB;EAAI;EAAqB;EAAgB,CAC3E,EAGK,IAAiB,EACrB,OAAO,GAA2B,MAA4C;AAC5E,MAAI;AAEF,GADA,EAAW,GAAK,EAChB,EAAS,KAAK;GAEd,IAAM,IAAS,MAAM,EAAa,OAA8C;IAC9E,UAAU;IACV,WAAW,EACT,OAAO;KAAE;KAAmB;KAAM,gBAAgB;KAAM,EACzD;IACF,CAAC;AAEF,OAAI,EAAuB,EAAO,CAChC,OAAM,EAA2B,EAAO;AAG1C,OAAI,EAAO,MAAM,gBAAgB;IAC/B,IAAM,IAAS,EAAmB,EAAO,KAAK,eAAe;AAE7D,WADA,MAAM,GAAiB,EAChB;;AAGT,UAAO;WACA,GAAK;AACZ,OAAI,EAAuB,EAAI,CAC7B,OAAM;AAIR,UADA,EADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,4BAA4B,CAClE,EACR;YACC;AACR,KAAW,GAAM;;IAGrB,CAAC,GAAc,EAAgB,CAChC,EAGK,IAAU,EAAY,YAAY;AACtC,QAAM,GAAiB;IACtB,CAAC,EAAgB,CAAC;AAQrB,QALA,QAAgB;AACV,OACJ,GAAiB;IAChB,CAAC,GAAiB,EAAU,CAAC,EAEzB;EACL;EACA;EACA,YAAY,MAAgB,IAAQ,IAAa,EAAE;EACnD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD"}
@@ -4,12 +4,14 @@
4
4
  * Provides dashboard export functionality with file download.
5
5
  */
6
6
  export interface ExportDashboardOptions {
7
- /** Include data source reference information (default: true) */
7
+ /** Include data source reference information (default: false) */
8
8
  includeDataSourceRefs?: boolean;
9
9
  /** Include webhook mappings (default: false) */
10
10
  includeWebhookMappings?: boolean;
11
11
  /** Pretty print JSON output (default: true) */
12
12
  prettyPrint?: boolean;
13
+ /** Exact reviewed document to download, without another server export. */
14
+ preparedContent?: string;
13
15
  }
14
16
  export interface UseExportDashboardResult {
15
17
  /** Loading state */
@@ -1 +1 @@
1
- {"version":3,"file":"useExportDashboard.d.ts","sourceRoot":"","sources":["../../../src/bigconsole/hooks/useExportDashboard.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,WAAW,sBAAsB;IACrC,gEAAgE;IAChE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gDAAgD;IAChD,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,+CAA+C;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,oBAAoB;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,sCAAsC;IACtC,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACnG,iDAAiD;IACjD,iBAAiB,EAAE,CACjB,WAAW,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,sBAAsB,KAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,kBAAkB;IAClB,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AA8BD,wBAAgB,kBAAkB,IAAI,wBAAwB,CA0G7D;AAED,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"useExportDashboard.d.ts","sourceRoot":"","sources":["../../../src/bigconsole/hooks/useExportDashboard.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,WAAW,sBAAsB;IACrC,iEAAiE;IACjE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gDAAgD;IAChD,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,+CAA+C;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,oBAAoB;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,sCAAsC;IACtC,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACnG,iDAAiD;IACjD,iBAAiB,EAAE,CACjB,WAAW,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,sBAAsB,KAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,kBAAkB;IAClB,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AA8BD,wBAAgB,kBAAkB,IAAI,wBAAwB,CA8G7D;AAED,eAAe,kBAAkB,CAAC"}
@@ -13,19 +13,32 @@ function o() {
13
13
  let [o, s] = n(!1), [c, l] = n(null), u = r(), d = t(async (t, n = {}) => {
14
14
  try {
15
15
  s(!0), l(null);
16
- let r = (await u.query({
16
+ let r = await u.query({
17
17
  query: e,
18
18
  variables: {
19
19
  id: t,
20
20
  options: {
21
- includeDataSourceRefs: n.includeDataSourceRefs ?? !0,
21
+ includeDataSourceRefs: n.includeDataSourceRefs ?? !1,
22
22
  includeWebhookMappings: n.includeWebhookMappings ?? !1,
23
23
  prettyPrint: n.prettyPrint ?? !0
24
24
  }
25
25
  },
26
26
  fetchPolicy: "network-only"
27
- })).data?.exportDashboard;
28
- if (r) return r;
27
+ });
28
+ if (r.error) throw r.error;
29
+ let i = r.data?.exportDashboard;
30
+ if (i) {
31
+ let e = JSON.parse(i);
32
+ if (!e || typeof e != "object" || Array.isArray(e)) throw Error("Export returned invalid JSON");
33
+ let r = { ...e }, a = "exportedFrom" in r && r.exportedFrom && typeof r.exportedFrom == "object" ? r.exportedFrom : {};
34
+ return Object.assign(r, { exportedFrom: {
35
+ ...a,
36
+ dashboardId: t
37
+ } }), "dashboard" in r && r.dashboard && typeof r.dashboard == "object" && (r.dashboard = {
38
+ ...r.dashboard,
39
+ id: t
40
+ }), JSON.stringify(r, null, n.prettyPrint === !1 ? void 0 : 2);
41
+ }
29
42
  throw Error("Export returned empty content");
30
43
  } catch (e) {
31
44
  return l(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to export dashboard")), null;
@@ -39,17 +52,14 @@ function o() {
39
52
  exportDashboard: d,
40
53
  downloadDashboard: t(async (e, t, n = {}) => {
41
54
  try {
42
- let r = await d(e, n);
55
+ let r = n.preparedContent ?? await d(e, n);
43
56
  if (!r) return !1;
44
- let o = r, s = t;
45
- try {
46
- let t = JSON.parse(r);
47
- t.exportedFrom ? t.exportedFrom.dashboardId = e : t.exportedFrom = { dashboardId: e }, t.dashboard && (t.dashboard.id = e), s = s || t.dashboard?.name || "dashboard", o = JSON.stringify(t, null, 2);
48
- } catch {
49
- s ||= "dashboard";
57
+ let o = t;
58
+ if (!o) {
59
+ let e = JSON.parse(r);
60
+ e && typeof e == "object" && "dashboard" in e && e.dashboard && typeof e.dashboard == "object" && "name" in e.dashboard && typeof e.dashboard.name == "string" && (o = e.dashboard.name);
50
61
  }
51
- let c = `${a(s || "dashboard")}-export-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`;
52
- return i(o, c), !0;
62
+ return i(r, `${a(o || "dashboard")}-export-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`), !0;
53
63
  } catch (e) {
54
64
  return l(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to download dashboard")), !1;
55
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useExportDashboard.js","names":[],"sources":["../../../src/bigconsole/hooks/useExportDashboard.ts"],"sourcesContent":["/**\n * useExportDashboard Hook\n *\n * Provides dashboard export functionality with file download.\n */\n\nimport { useCallback, useState } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport { ExportDashboardDocument } from '../../generated/wspace-operations';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ExportDashboardOptions {\n /** Include data source reference information (default: true) */\n includeDataSourceRefs?: boolean;\n /** Include webhook mappings (default: false) */\n includeWebhookMappings?: boolean;\n /** Pretty print JSON output (default: true) */\n prettyPrint?: boolean;\n}\n\nexport interface UseExportDashboardResult {\n /** Loading state */\n loading: boolean;\n /** Error state */\n error: Error | null;\n /** Export dashboard to JSON string */\n exportDashboard: (dashboardId: string, options?: ExportDashboardOptions) => Promise<string | null>;\n /** Export and download dashboard as JSON file */\n downloadDashboard: (\n dashboardId: string,\n dashboardName?: string,\n options?: ExportDashboardOptions\n ) => Promise<boolean>;\n /** Clear error */\n clearError: () => void;\n}\n\n// ============================================================================\n// Helper: Download JSON file\n// ============================================================================\n\nfunction downloadJsonFile(content: string, filename: string): void {\n const blob = new Blob([content], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n}\n\nfunction sanitizeFilename(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .substring(0, 50);\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useExportDashboard(): UseExportDashboardResult {\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const apolloClient = useApolloClient();\n\n // Export dashboard to JSON string\n const exportDashboard = useCallback(\n async (dashboardId: string, options: ExportDashboardOptions = {}): Promise<string | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.query<{ exportDashboard?: string }>({\n query: ExportDashboardDocument,\n variables: {\n id: dashboardId,\n options: {\n includeDataSourceRefs: options.includeDataSourceRefs ?? true,\n includeWebhookMappings: options.includeWebhookMappings ?? false,\n prettyPrint: options.prettyPrint ?? true,\n },\n },\n fetchPolicy: 'network-only',\n });\n\n const jsonContent = result.data?.exportDashboard;\n\n if (jsonContent) {\n return jsonContent;\n }\n\n throw new Error('Export returned empty content');\n } catch (err) {\n const exportError = err instanceof Error ? err : new Error('Failed to export dashboard');\n setError(exportError);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient]\n );\n\n // Export and download dashboard as JSON file\n const downloadDashboard = useCallback(\n async (dashboardId: string, dashboardName?: string, options: ExportDashboardOptions = {}): Promise<boolean> => {\n try {\n const content = await exportDashboard(dashboardId, options);\n\n if (!content) {\n return false;\n }\n\n // Inject dashboard ID into the export for re-import update support\n let finalContent = content;\n let filename = dashboardName;\n try {\n const parsed = JSON.parse(content);\n\n // Add the dashboard ID to exportedFrom for re-import matching\n if (parsed.exportedFrom) {\n parsed.exportedFrom.dashboardId = dashboardId;\n } else {\n parsed.exportedFrom = { dashboardId };\n }\n\n // Also add ID directly to dashboard object for clarity\n if (parsed.dashboard) {\n parsed.dashboard.id = dashboardId;\n }\n\n filename = filename || parsed.dashboard?.name || 'dashboard';\n finalContent = JSON.stringify(parsed, null, 2);\n } catch {\n filename = filename || 'dashboard';\n }\n\n const sanitizedName = sanitizeFilename(filename || 'dashboard');\n const timestamp = new Date().toISOString().split('T')[0];\n const fullFilename = `${sanitizedName}-export-${timestamp}.json`;\n\n downloadJsonFile(finalContent, fullFilename);\n\n return true;\n } catch (err) {\n const downloadError = err instanceof Error ? err : new Error('Failed to download dashboard');\n setError(downloadError);\n return false;\n }\n },\n [exportDashboard]\n );\n\n // Clear error\n const clearError = useCallback(() => {\n setError(null);\n }, []);\n\n return {\n loading,\n error,\n exportDashboard,\n downloadDashboard,\n clearError,\n };\n}\n\nexport default useExportDashboard;\n"],"mappings":";;;;AA4CA,SAAS,EAAiB,GAAiB,GAAwB;CACjE,IAAM,IAAO,IAAI,KAAK,CAAC,EAAQ,EAAE,EAAE,MAAM,oBAAoB,CAAC,EACxD,IAAM,IAAI,gBAAgB,EAAK,EAC/B,IAAO,SAAS,cAAc,IAAI;AAMxC,CALA,EAAK,OAAO,GACZ,EAAK,WAAW,GAChB,SAAS,KAAK,YAAY,EAAK,EAC/B,EAAK,OAAO,EACZ,SAAS,KAAK,YAAY,EAAK,EAC/B,IAAI,gBAAgB,EAAI;;AAG1B,SAAS,EAAiB,GAAsB;AAC9C,QAAO,EACJ,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG,CACvB,UAAU,GAAG,GAAG;;AAOrB,SAAgB,IAA+C;CAC7D,IAAM,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAO,KAAY,EAAuB,KAAK,EAEhD,IAAe,GAAiB,EAGhC,IAAkB,EACtB,OAAO,GAAqB,IAAkC,EAAE,KAA6B;AAC3F,MAAI;AAEF,GADA,EAAW,GAAK,EAChB,EAAS,KAAK;GAed,IAAM,KAbS,MAAM,EAAa,MAAoC;IACpE,OAAO;IACP,WAAW;KACT,IAAI;KACJ,SAAS;MACP,uBAAuB,EAAQ,yBAAyB;MACxD,wBAAwB,EAAQ,0BAA0B;MAC1D,aAAa,EAAQ,eAAe;MACrC;KACF;IACD,aAAa;IACd,CAAC,EAEyB,MAAM;AAEjC,OAAI,EACF,QAAO;AAGT,SAAU,MAAM,gCAAgC;WACzC,GAAK;AAGZ,UADA,EADoB,aAAe,QAAQ,IAAM,gBAAI,MAAM,6BAA6B,CACnE,EACd;YACC;AACR,KAAW,GAAM;;IAGrB,CAAC,EAAa,CACf;AAyDD,QAAO;EACL;EACA;EACA;EACA,mBA1DwB,EACxB,OAAO,GAAqB,GAAwB,IAAkC,EAAE,KAAuB;AAC7G,OAAI;IACF,IAAM,IAAU,MAAM,EAAgB,GAAa,EAAQ;AAE3D,QAAI,CAAC,EACH,QAAO;IAIT,IAAI,IAAe,GACf,IAAW;AACf,QAAI;KACF,IAAM,IAAS,KAAK,MAAM,EAAQ;AAelC,KAZI,EAAO,eACT,EAAO,aAAa,cAAc,IAElC,EAAO,eAAe,EAAE,gBAAa,EAInC,EAAO,cACT,EAAO,UAAU,KAAK,IAGxB,IAAW,KAAY,EAAO,WAAW,QAAQ,aACjD,IAAe,KAAK,UAAU,GAAQ,MAAM,EAAE;YACxC;AACN,WAAuB;;IAKzB,IAAM,IAAe,GAFC,EAAiB,KAAY,YAAY,CAEzB,2BADpB,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,IAAI,CAAC,GACI;AAI1D,WAFA,EAAiB,GAAc,EAAa,EAErC;YACA,GAAK;AAGZ,WADA,EADsB,aAAe,QAAQ,IAAM,gBAAI,MAAM,+BAA+B,CACrE,EAChB;;KAGX,CAAC,EAAgB,CAClB;EAYC,YATiB,QAAkB;AACnC,KAAS,KAAK;KACb,EAAE,CAAC;EAQL"}
1
+ {"version":3,"file":"useExportDashboard.js","names":[],"sources":["../../../src/bigconsole/hooks/useExportDashboard.ts"],"sourcesContent":["/**\n * useExportDashboard Hook\n *\n * Provides dashboard export functionality with file download.\n */\n\nimport { useCallback, useState } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport { ExportDashboardDocument } from '../../generated/wspace-operations';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ExportDashboardOptions {\n /** Include data source reference information (default: false) */\n includeDataSourceRefs?: boolean;\n /** Include webhook mappings (default: false) */\n includeWebhookMappings?: boolean;\n /** Pretty print JSON output (default: true) */\n prettyPrint?: boolean;\n /** Exact reviewed document to download, without another server export. */\n preparedContent?: string;\n}\n\nexport interface UseExportDashboardResult {\n /** Loading state */\n loading: boolean;\n /** Error state */\n error: Error | null;\n /** Export dashboard to JSON string */\n exportDashboard: (dashboardId: string, options?: ExportDashboardOptions) => Promise<string | null>;\n /** Export and download dashboard as JSON file */\n downloadDashboard: (\n dashboardId: string,\n dashboardName?: string,\n options?: ExportDashboardOptions\n ) => Promise<boolean>;\n /** Clear error */\n clearError: () => void;\n}\n\n// ============================================================================\n// Helper: Download JSON file\n// ============================================================================\n\nfunction downloadJsonFile(content: string, filename: string): void {\n const blob = new Blob([content], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n}\n\nfunction sanitizeFilename(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .substring(0, 50);\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useExportDashboard(): UseExportDashboardResult {\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const apolloClient = useApolloClient();\n\n // Export dashboard to JSON string\n const exportDashboard = useCallback(\n async (dashboardId: string, options: ExportDashboardOptions = {}): Promise<string | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.query<{ exportDashboard?: string }>({\n query: ExportDashboardDocument,\n variables: {\n id: dashboardId,\n options: {\n includeDataSourceRefs: options.includeDataSourceRefs ?? false,\n includeWebhookMappings: options.includeWebhookMappings ?? false,\n prettyPrint: options.prettyPrint ?? true,\n },\n },\n fetchPolicy: 'network-only',\n });\n\n if (result.error) throw result.error;\n const jsonContent = result.data?.exportDashboard;\n\n if (jsonContent) {\n const parsed: unknown = JSON.parse(jsonContent);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n throw new Error('Export returned invalid JSON');\n const document = { ...parsed };\n const exportedFrom =\n 'exportedFrom' in document && document.exportedFrom && typeof document.exportedFrom === 'object'\n ? document.exportedFrom\n : {};\n Object.assign(document, { exportedFrom: { ...exportedFrom, dashboardId } });\n if ('dashboard' in document && document.dashboard && typeof document.dashboard === 'object') {\n document.dashboard = { ...document.dashboard, id: dashboardId };\n }\n return JSON.stringify(document, null, options.prettyPrint === false ? undefined : 2);\n }\n\n throw new Error('Export returned empty content');\n } catch (err) {\n const exportError = err instanceof Error ? err : new Error('Failed to export dashboard');\n setError(exportError);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient]\n );\n\n // Export and download dashboard as JSON file\n const downloadDashboard = useCallback(\n async (dashboardId: string, dashboardName?: string, options: ExportDashboardOptions = {}): Promise<boolean> => {\n try {\n const content = options.preparedContent ?? (await exportDashboard(dashboardId, options));\n\n if (!content) {\n return false;\n }\n\n let filename = dashboardName;\n if (!filename) {\n const parsed: unknown = JSON.parse(content);\n if (\n parsed &&\n typeof parsed === 'object' &&\n 'dashboard' in parsed &&\n parsed.dashboard &&\n typeof parsed.dashboard === 'object' &&\n 'name' in parsed.dashboard &&\n typeof parsed.dashboard.name === 'string'\n )\n filename = parsed.dashboard.name;\n }\n\n const sanitizedName = sanitizeFilename(filename || 'dashboard');\n const timestamp = new Date().toISOString().split('T')[0];\n const fullFilename = `${sanitizedName}-export-${timestamp}.json`;\n\n downloadJsonFile(content, fullFilename);\n\n return true;\n } catch (err) {\n const downloadError = err instanceof Error ? err : new Error('Failed to download dashboard');\n setError(downloadError);\n return false;\n }\n },\n [exportDashboard]\n );\n\n // Clear error\n const clearError = useCallback(() => {\n setError(null);\n }, []);\n\n return {\n loading,\n error,\n exportDashboard,\n downloadDashboard,\n clearError,\n };\n}\n\nexport default useExportDashboard;\n"],"mappings":"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"}
@@ -41,6 +41,24 @@ interface FilterUrlSyncResult {
41
41
  /** Current drilldown depth */
42
42
  drilldownDepth: number;
43
43
  }
44
+ /**
45
+ * Encode filter value for URL
46
+ */
47
+ export declare function encodeFilterValue(value: unknown): string;
48
+ /**
49
+ * Decode filter value from URL
50
+ */
51
+ export declare function decodeFilterValue(encoded: string, filterType?: string): unknown;
52
+ /**
53
+ * Parse URL search params to filter values
54
+ */
55
+ export declare function parseUrlToFilters(search: string, paramPrefix: string): Record<string, unknown>;
56
+ /** Current-route values include drilldown context; nothing from the previous board is retained. */
57
+ export declare function parseUrlToGlobalFilterValues(search: string, paramPrefix: string): Record<string, unknown>;
58
+ /**
59
+ * Build URL search params from filter values
60
+ */
61
+ export declare function filtersToUrlParams(filters: Record<string, unknown>, paramPrefix: string): URLSearchParams;
44
62
  export declare function useFilterUrlSync(options?: UseFilterUrlSyncOptions): FilterUrlSyncResult;
45
63
  export default useFilterUrlSync;
46
64
  //# sourceMappingURL=useFilterUrlSync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useFilterUrlSync.d.ts","sourceRoot":"","sources":["../../../src/bigconsole/hooks/useFilterUrlSync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,UAAU,uBAAuB;IAC/B,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,iBAAiB;IACzB,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,mDAAmD;IACnD,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,UAAU,mBAAmB;IAC3B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,uCAAuC;IACvC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,0CAA0C;IAC1C,iBAAiB,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjD,sCAAsC;IACtC,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;IAC9C,2CAA2C;IAC3C,aAAa,EAAE,OAAO,CAAC;IACvB,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;CACxB;AA8LD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,mBAAmB,CAmM3F;AAED,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"useFilterUrlSync.d.ts","sourceRoot":"","sources":["../../../src/bigconsole/hooks/useFilterUrlSync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,UAAU,uBAAuB;IAC/B,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,iBAAiB;IACzB,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,mDAAmD;IACnD,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,UAAU,mBAAmB;IAC3B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,6CAA6C;IAC7C,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,uCAAuC;IACvC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,0CAA0C;IAC1C,iBAAiB,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjD,sCAAsC;IACtC,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;IAC9C,2CAA2C;IAC3C,aAAa,EAAE,OAAO,CAAC;IACvB,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;CACxB;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAmCxD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAsE/E;AAkBD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAa9F;AA4CD,mGAAmG;AACnG,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYzG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,eAAe,CAgBzG;AAMD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,mBAAmB,CAiL3F;AAED,eAAe,gBAAgB,CAAC"}
@@ -6,38 +6,55 @@ import { useLocation as l } from "react-router";
6
6
  //#region src/bigconsole/hooks/useFilterUrlSync.ts
7
7
  function u(e) {
8
8
  if (e == null) return "";
9
- if (Array.isArray(e)) return e.map((e) => encodeURIComponent(String(e))).join(",");
9
+ if (Array.isArray(e)) return JSON.stringify(e);
10
10
  if (typeof e == "object") {
11
11
  let t = e;
12
- return t.start || t.end ? `${t.start || ""},${t.end || ""}` : t.min !== void 0 || t.max !== void 0 ? `${t.min ?? ""},${t.max ?? ""}` : t.value === void 0 ? encodeURIComponent(JSON.stringify(t)) : encodeURIComponent(String(t.value));
12
+ return t.start || t.end || t.min !== void 0 || t.max !== void 0 || t.value === void 0 ? JSON.stringify(t) : JSON.stringify(t.value);
13
13
  }
14
- return encodeURIComponent(String(e));
14
+ return JSON.stringify(e);
15
15
  }
16
16
  function d(e, t) {
17
- if (e) {
18
- if (t === "DATE_RANGE" || e.includes("T") && e.includes(",")) {
19
- let t = e.split(",");
20
- if (t.length === 2) return {
21
- start: t[0] || void 0,
22
- end: t[1] || void 0
17
+ if (!e) return;
18
+ try {
19
+ return JSON.parse(e);
20
+ } catch {}
21
+ let n = e;
22
+ try {
23
+ n = decodeURIComponent(e);
24
+ } catch {}
25
+ if (n.startsWith("[") || n.startsWith("{")) try {
26
+ return JSON.parse(n);
27
+ } catch {
28
+ return n;
29
+ }
30
+ if (t === "DATE_RANGE" || e.includes("T") && e.includes(",")) {
31
+ let t = e.split(",");
32
+ if (t.length === 2) return {
33
+ start: t[0] || void 0,
34
+ end: t[1] || void 0
35
+ };
36
+ }
37
+ if (t === "NUMERIC_RANGE" || /^-?\d*\.?\d*,-?\d*\.?\d*$/.test(e)) {
38
+ let t = e.split(",");
39
+ if (t.length === 2) {
40
+ let e = t[0] ? parseFloat(t[0]) : void 0, n = t[1] ? parseFloat(t[1]) : void 0;
41
+ if (!isNaN(e) || !isNaN(n)) return {
42
+ min: e,
43
+ max: n
23
44
  };
24
45
  }
25
- if (t === "NUMERIC_RANGE" || /^-?\d*\.?\d*,-?\d*\.?\d*$/.test(e)) {
26
- let t = e.split(",");
27
- if (t.length === 2) {
28
- let e = t[0] ? parseFloat(t[0]) : void 0, n = t[1] ? parseFloat(t[1]) : void 0;
29
- if (!isNaN(e) || !isNaN(n)) return {
30
- min: e,
31
- max: n
32
- };
33
- }
34
- }
35
- if (e.includes(",") && t === "CATEGORY") return e.split(",").map((e) => decodeURIComponent(e));
46
+ }
47
+ if (e.includes(",") && t === "CATEGORY") return e.split(",").map((e) => {
36
48
  try {
37
- return JSON.parse(decodeURIComponent(e));
38
- } catch {
39
49
  return decodeURIComponent(e);
50
+ } catch {
51
+ return e;
40
52
  }
53
+ });
54
+ try {
55
+ return JSON.parse(n);
56
+ } catch {
57
+ return n;
41
58
  }
42
59
  }
43
60
  function f(e) {
@@ -81,61 +98,74 @@ function h(e) {
81
98
  };
82
99
  }
83
100
  function g(e, t) {
101
+ let n = p(e, t);
102
+ Object.entries(m(e)).forEach(([e, t]) => {
103
+ n[`ctx_${e}`] = t;
104
+ });
105
+ let r = h(e);
106
+ return r.depth > 0 && (n.__drilldown_depth = r.depth, n.__drilldown_parent = r.parent, n.__drilldown_path = r.path), n;
107
+ }
108
+ function _(e, t) {
84
109
  let n = new URLSearchParams();
85
110
  return Object.entries(e).forEach(([e, r]) => {
86
- if (r != null && r !== "") {
111
+ if (!(e.startsWith("ctx_") || e.startsWith("__drilldown_")) && r != null && r !== "") {
87
112
  let i = u(r);
88
113
  i && n.set(`${t}${e}`, i);
89
114
  }
90
115
  }), n;
91
116
  }
92
- function _(n = {}) {
93
- let { enabled: r = !0, paramPrefix: u = i, debounceMs: d = 300 } = n, f = l(), _ = t(), v = e((e) => e.setGlobalFilterValue), y = c(!1), b = c(""), x = c(null), S = c(f.pathname);
94
- o(() => {
95
- f.pathname !== S.current && (y.current = !1, b.current = "", S.current = f.pathname);
96
- }, [f.pathname]);
97
- let C = s(() => typeof window > "u" ? {
117
+ function v(n = {}) {
118
+ let { enabled: r = !0, paramPrefix: u = i, debounceMs: d = 300 } = n, f = l(), p = t(), v = e((e) => e.setGlobalFilterValues), y = c(""), b = c(""), x = c(null), S = s(() => typeof window > "u" ? {
98
119
  depth: 0,
99
120
  parent: null,
100
121
  path: []
101
- } : h(f.search), [f.search]), w = C.depth > 0, T = C.depth;
122
+ } : h(f.search), [f.search]), C = S.depth > 0, w = S.depth;
102
123
  return o(() => {
103
- if (!r || y.current) return;
104
- let e = p(window.location.search, u);
105
- Object.keys(e).length > 0 && Object.entries(e).forEach(([e, t]) => {
106
- v(e, t);
107
- }), b.current = JSON.stringify(e), y.current = !0;
124
+ if (!r) return;
125
+ x.current && clearTimeout(x.current);
126
+ let t = g(window.location.search, u), n = JSON.stringify(t);
127
+ JSON.stringify(e.getState().globalFilterValues) !== n && v(t), b.current = n, y.current = `${f.key}:${f.pathname}:${f.search}`;
108
128
  }, [
109
129
  r,
130
+ f.key,
131
+ f.pathname,
132
+ f.search,
110
133
  u,
111
134
  v
112
135
  ]), o(() => {
113
- if (!r || !y.current) return;
114
- let e = JSON.stringify(_);
115
- if (e !== b.current) return x.current && clearTimeout(x.current), x.current = setTimeout(() => {
116
- let t = g(_, u), n = new URLSearchParams(window.location.search);
117
- Array.from(n.keys()).forEach((e) => {
118
- e.startsWith(u) && n.delete(e);
119
- }), t.forEach((e, t) => {
120
- n.set(t, e);
136
+ if (!r || !y.current || JSON.stringify(p) !== JSON.stringify(e.getState().globalFilterValues)) return;
137
+ let t = JSON.stringify(p);
138
+ if (t === b.current) return;
139
+ x.current && clearTimeout(x.current);
140
+ let n = f.pathname;
141
+ return x.current = setTimeout(() => {
142
+ if (window.location.pathname !== n || JSON.stringify(e.getState().globalFilterValues) !== t) return;
143
+ let r = _(p, u), i = new URLSearchParams(window.location.search);
144
+ Array.from(i.keys()).forEach((e) => {
145
+ e.startsWith(u) && i.delete(e);
146
+ }), r.forEach((e, t) => {
147
+ i.set(t, e);
121
148
  });
122
- let r = n.toString(), i = r ? `${window.location.pathname}?${r}` : window.location.pathname;
149
+ let a = i.toString(), o = a ? `${window.location.pathname}?${a}` : window.location.pathname;
123
150
  window.history.replaceState({
124
151
  ...window.history.state,
125
- filters: _
126
- }, "", i), b.current = e;
152
+ filters: p
153
+ }, "", o), b.current = t;
127
154
  }, d), () => {
128
155
  x.current && clearTimeout(x.current);
129
156
  };
130
157
  }, [
131
158
  r,
132
- _,
159
+ p,
133
160
  u,
134
- d
161
+ d,
162
+ f.pathname,
163
+ f.key,
164
+ f.search
135
165
  ]), {
136
166
  syncToUrl: a(() => {
137
167
  if (!r) return;
138
- let e = g(_, u), t = new URLSearchParams(window.location.search);
168
+ let e = _(p, u), t = new URLSearchParams(window.location.search);
139
169
  Array.from(t.keys()).forEach((e) => {
140
170
  e.startsWith(u) && t.delete(e);
141
171
  }), e.forEach((e, n) => {
@@ -144,21 +174,21 @@ function _(n = {}) {
144
174
  let n = t.toString(), i = n ? `${window.location.pathname}?${n}` : window.location.pathname;
145
175
  window.history.replaceState({
146
176
  ...window.history.state,
147
- filters: _
148
- }, "", i), b.current = JSON.stringify(_);
177
+ filters: p
178
+ }, "", i), b.current = JSON.stringify(p);
149
179
  }, [
150
180
  r,
151
- _,
181
+ p,
152
182
  u
153
183
  ]),
154
184
  getShareableUrl: a(() => {
155
- let e = g(_, u), t = `${window.location.origin}${window.location.pathname}`, n = new URLSearchParams(window.location.search);
185
+ let e = _(p, u), t = `${window.location.origin}${window.location.pathname}`, n = new URLSearchParams(window.location.search);
156
186
  Array.from(n.keys()).forEach((t) => {
157
187
  t.startsWith(u) || e.set(t, n.get(t) || "");
158
188
  });
159
189
  let r = e.toString();
160
190
  return r ? `${t}?${r}` : t;
161
- }, [_, u]),
191
+ }, [p, u]),
162
192
  clearUrlParams: a(() => {
163
193
  let e = new URLSearchParams(window.location.search);
164
194
  Array.from(e.keys()).forEach((t) => {
@@ -169,11 +199,11 @@ function _(n = {}) {
169
199
  }, [u]),
170
200
  getContextFromUrl: a(() => m(window.location.search), []),
171
201
  getDrilldownMetadata: a(() => h(window.location.search), []),
172
- isInDrilldown: w,
173
- drilldownDepth: T
202
+ isInDrilldown: C,
203
+ drilldownDepth: w
174
204
  };
175
205
  }
176
206
  //#endregion
177
- export { _ as default };
207
+ export { v as default, g as parseUrlToGlobalFilterValues };
178
208
 
179
209
  //# sourceMappingURL=useFilterUrlSync.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useFilterUrlSync.js","names":[],"sources":["../../../src/bigconsole/hooks/useFilterUrlSync.ts"],"sourcesContent":["/**\n * useFilterUrlSync Hook\n *\n * Syncs global filter values to URL query parameters for sharing filtered views.\n * Supports all filter types: DATE_RANGE, CATEGORY, TEXT_SEARCH, NUMERIC_RANGE, CUSTOM\n *\n * Also handles drilldown context parameters:\n * - filter_* : Global filter values\n * - ctx_* : Drilldown context values (clicked item data)\n * - drilldown_* : Drilldown metadata (depth, parent, path)\n */\n\nimport { useEffect, useCallback, useRef, useMemo } from 'react';\nimport { useLocation } from 'react-router';\nimport { useGlobalFilterValues, useDashboardStore } from '../store';\nimport { CONTEXT_PARAM_PREFIX, FILTER_PARAM_PREFIX, DRILLDOWN_PARAM_PREFIX } from './useDrilldown';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface UseFilterUrlSyncOptions {\n /** Whether URL sync is enabled */\n enabled?: boolean;\n /** Prefix for URL params (default: 'filter_') */\n paramPrefix?: string;\n /** Debounce delay in ms for URL updates (default: 300) */\n debounceMs?: number;\n}\n\ninterface DrilldownMetadata {\n /** Current drilldown depth */\n depth: number;\n /** Parent dashboard ID */\n parent: string | null;\n /** Array of dashboard IDs in the drilldown path */\n path: string[];\n}\n\ninterface FilterUrlSyncResult {\n /** Manually sync current filters to URL */\n syncToUrl: () => void;\n /** Get shareable URL with current filters */\n getShareableUrl: () => string;\n /** Clear all filter params from URL */\n clearUrlParams: () => void;\n /** Get context params from URL (ctx_*) */\n getContextFromUrl: () => Record<string, unknown>;\n /** Get drilldown metadata from URL */\n getDrilldownMetadata: () => DrilldownMetadata;\n /** Check if currently in drilldown mode */\n isInDrilldown: boolean;\n /** Current drilldown depth */\n drilldownDepth: number;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Encode filter value for URL\n */\nfunction encodeFilterValue(value: unknown): string {\n if (value === null || value === undefined) {\n return '';\n }\n\n // Array (multi-select): [\"a\", \"b\"] -> \"a,b\"\n if (Array.isArray(value)) {\n return value.map((v) => encodeURIComponent(String(v))).join(',');\n }\n\n // Object types\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n\n // Date range: {start, end} -> \"start,end\"\n if (obj.start || obj.end) {\n return `${obj.start || ''},${obj.end || ''}`;\n }\n\n // Numeric range: {min, max} -> \"min,max\"\n if (obj.min !== undefined || obj.max !== undefined) {\n return `${obj.min ?? ''},${obj.max ?? ''}`;\n }\n\n // Object with value property (category)\n if (obj.value !== undefined) {\n return encodeURIComponent(String(obj.value));\n }\n\n // Generic object - JSON stringify\n return encodeURIComponent(JSON.stringify(obj));\n }\n\n // Simple value\n return encodeURIComponent(String(value));\n}\n\n/**\n * Decode filter value from URL\n */\nfunction decodeFilterValue(encoded: string, filterType?: string): unknown {\n if (!encoded) {\n return undefined;\n }\n\n // Date range\n if (filterType === 'DATE_RANGE' || (encoded.includes('T') && encoded.includes(','))) {\n const parts = encoded.split(',');\n if (parts.length === 2) {\n return {\n start: parts[0] || undefined,\n end: parts[1] || undefined,\n };\n }\n }\n\n // Numeric range (check if both parts are numbers)\n if (filterType === 'NUMERIC_RANGE' || /^-?\\d*\\.?\\d*,-?\\d*\\.?\\d*$/.test(encoded)) {\n const parts = encoded.split(',');\n if (parts.length === 2) {\n const min = parts[0] ? parseFloat(parts[0]) : undefined;\n const max = parts[1] ? parseFloat(parts[1]) : undefined;\n if (!isNaN(min as number) || !isNaN(max as number)) {\n return { min, max };\n }\n }\n }\n\n // Multi-select array\n if (encoded.includes(',') && filterType === 'CATEGORY') {\n return encoded.split(',').map((v) => decodeURIComponent(v));\n }\n\n // Try to parse as JSON\n try {\n const decoded = decodeURIComponent(encoded);\n return JSON.parse(decoded);\n } catch {\n // Simple value\n return decodeURIComponent(encoded);\n }\n}\n\n/**\n * Decode a context param value from URL\n */\nfunction decodeContextValue(encoded: string): unknown {\n if (!encoded) {\n return undefined;\n }\n const decoded = decodeURIComponent(encoded);\n // Try to parse as JSON\n try {\n return JSON.parse(decoded);\n } catch {\n return decoded;\n }\n}\n\n/**\n * Parse URL search params to filter values\n */\nfunction parseUrlToFilters(search: string, paramPrefix: string): Record<string, unknown> {\n const params = new URLSearchParams(search);\n const filters: Record<string, unknown> = {};\n\n params.forEach((value, key) => {\n if (key.startsWith(paramPrefix)) {\n const filterId = key.slice(paramPrefix.length);\n // Try to detect type from value format\n filters[filterId] = decodeFilterValue(value);\n }\n });\n\n return filters;\n}\n\n/**\n * Parse URL search params to context values\n */\nfunction parseUrlToContext(search: string): Record<string, unknown> {\n const params = new URLSearchParams(search);\n const context: Record<string, unknown> = {};\n\n params.forEach((value, key) => {\n if (key.startsWith(CONTEXT_PARAM_PREFIX)) {\n const contextKey = key.slice(CONTEXT_PARAM_PREFIX.length);\n context[contextKey] = decodeContextValue(value);\n }\n });\n\n return context;\n}\n\n/**\n * Parse drilldown metadata from URL\n */\nfunction parseUrlToDrilldownMetadata(search: string): DrilldownMetadata {\n const params = new URLSearchParams(search);\n const depthStr = params.get(`${DRILLDOWN_PARAM_PREFIX}depth`);\n const parent = params.get(`${DRILLDOWN_PARAM_PREFIX}parent`);\n const pathStr = params.get(`${DRILLDOWN_PARAM_PREFIX}path`);\n\n let path: string[] = [];\n if (pathStr) {\n try {\n path = JSON.parse(decodeURIComponent(pathStr));\n } catch {\n path = [];\n }\n }\n\n return {\n depth: depthStr ? parseInt(depthStr, 10) : 0,\n parent: parent || null,\n path,\n };\n}\n\n/**\n * Build URL search params from filter values\n */\nfunction filtersToUrlParams(filters: Record<string, unknown>, paramPrefix: string): URLSearchParams {\n const params = new URLSearchParams();\n\n Object.entries(filters).forEach(([filterId, value]) => {\n if (value !== undefined && value !== null && value !== '') {\n const encoded = encodeFilterValue(value);\n if (encoded) {\n params.set(`${paramPrefix}${filterId}`, encoded);\n }\n }\n });\n\n return params;\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useFilterUrlSync(options: UseFilterUrlSyncOptions = {}): FilterUrlSyncResult {\n const { enabled = true, paramPrefix = FILTER_PARAM_PREFIX, debounceMs = 300 } = options;\n\n // Use react-router's useLocation to react to URL changes\n const location = useLocation();\n\n const filterValues = useGlobalFilterValues();\n const setGlobalFilterValue = useDashboardStore((state) => state.setGlobalFilterValue);\n\n // Track if initial load from URL is done\n const initialLoadDone = useRef(false);\n // Track last synced values to avoid loops\n const lastSyncedValues = useRef<string>('');\n // Debounce timer\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n // Track the last pathname to detect same-route navigations (e.g. drilldown)\n const lastPathname = useRef(location.pathname);\n\n // Reset initialLoadDone when pathname changes (e.g. drilldown to different dashboard).\n // Without this, same-route navigation reuses the component and the filter_* URL params\n // from the drilldown URL are never loaded into the Zustand store.\n useEffect(() => {\n if (location.pathname !== lastPathname.current) {\n initialLoadDone.current = false;\n lastSyncedValues.current = '';\n lastPathname.current = location.pathname;\n }\n }, [location.pathname]);\n\n // Compute drilldown state from URL - now reactive to location.search changes\n const drilldownMetadata = useMemo(() => {\n if (typeof window === 'undefined') {\n return { depth: 0, parent: null, path: [] };\n }\n return parseUrlToDrilldownMetadata(location.search);\n }, [location.search]);\n\n const isInDrilldown = drilldownMetadata.depth > 0;\n const drilldownDepth = drilldownMetadata.depth;\n\n // Load filters from URL on initial mount\n useEffect(() => {\n if (!enabled || initialLoadDone.current) return;\n\n // Parse filter params\n const urlFilters = parseUrlToFilters(window.location.search, paramPrefix);\n\n if (Object.keys(urlFilters).length > 0) {\n // Set each filter value\n Object.entries(urlFilters).forEach(([filterId, value]) => {\n setGlobalFilterValue(filterId, value);\n });\n }\n\n // IMPORTANT: Set lastSyncedValues to current filterValues AFTER the sync effect runs\n // This prevents the race condition where sync effect runs with stale (empty) filterValues\n // and strips the URL params that we just loaded.\n // We use the URL filters as the initial synced values since that's what we loaded from.\n lastSyncedValues.current = JSON.stringify(urlFilters);\n\n initialLoadDone.current = true;\n }, [enabled, paramPrefix, setGlobalFilterValue]);\n\n // Sync filter values to URL when they change\n useEffect(() => {\n if (!enabled || !initialLoadDone.current) return;\n\n const currentValues = JSON.stringify(filterValues);\n\n // Skip if values haven't changed\n if (currentValues === lastSyncedValues.current) {\n return;\n }\n\n // Debounce URL updates\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n\n debounceTimer.current = setTimeout(() => {\n const params = filtersToUrlParams(filterValues, paramPrefix);\n\n // Get current URL without filter params (but keep other params like ctx_*, drilldown_*)\n const currentParams = new URLSearchParams(window.location.search);\n // Remove existing filter params only\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n // Merge with new filter params\n params.forEach((value, key) => {\n currentParams.set(key, value);\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n // Update URL without triggering navigation\n window.history.replaceState({ ...window.history.state, filters: filterValues }, '', newUrl);\n\n lastSyncedValues.current = currentValues;\n }, debounceMs);\n\n return () => {\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n };\n }, [enabled, filterValues, paramPrefix, debounceMs]);\n\n // Manual sync to URL\n const syncToUrl = useCallback(() => {\n if (!enabled) return;\n\n const params = filtersToUrlParams(filterValues, paramPrefix);\n const currentParams = new URLSearchParams(window.location.search);\n\n // Remove existing filter params only\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n // Merge with new filter params\n params.forEach((value, key) => {\n currentParams.set(key, value);\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n window.history.replaceState({ ...window.history.state, filters: filterValues }, '', newUrl);\n\n lastSyncedValues.current = JSON.stringify(filterValues);\n }, [enabled, filterValues, paramPrefix]);\n\n // Get shareable URL\n const getShareableUrl = useCallback((): string => {\n const params = filtersToUrlParams(filterValues, paramPrefix);\n const baseUrl = `${window.location.origin}${window.location.pathname}`;\n\n // Preserve non-filter params (ctx_*, drilldown_*)\n const currentParams = new URLSearchParams(window.location.search);\n Array.from(currentParams.keys()).forEach((key) => {\n if (!key.startsWith(paramPrefix)) {\n params.set(key, currentParams.get(key) || '');\n }\n });\n\n const search = params.toString();\n return search ? `${baseUrl}?${search}` : baseUrl;\n }, [filterValues, paramPrefix]);\n\n // Clear all filter params from URL\n const clearUrlParams = useCallback(() => {\n const currentParams = new URLSearchParams(window.location.search);\n\n // Remove filter params only (keep ctx_*, drilldown_*)\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n // Keep the router's history state (its `key`/`idx`): wiping it breaks Back\n // navigation across drilldowns and page switches (BOFF-7625).\n window.history.replaceState(window.history.state, '', newUrl);\n lastSyncedValues.current = '{}';\n }, [paramPrefix]);\n\n // Get context params from URL\n const getContextFromUrl = useCallback((): Record<string, unknown> => {\n return parseUrlToContext(window.location.search);\n }, []);\n\n // Get drilldown metadata from URL\n const getDrilldownMetadata = useCallback((): DrilldownMetadata => {\n return parseUrlToDrilldownMetadata(window.location.search);\n }, []);\n\n return {\n syncToUrl,\n getShareableUrl,\n clearUrlParams,\n getContextFromUrl,\n getDrilldownMetadata,\n isInDrilldown,\n drilldownDepth,\n };\n}\n\nexport default useFilterUrlSync;\n"],"mappings":"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"}
1
+ {"version":3,"file":"useFilterUrlSync.js","names":[],"sources":["../../../src/bigconsole/hooks/useFilterUrlSync.ts"],"sourcesContent":["/**\n * useFilterUrlSync Hook\n *\n * Syncs global filter values to URL query parameters for sharing filtered views.\n * Supports all filter types: DATE_RANGE, CATEGORY, TEXT_SEARCH, NUMERIC_RANGE, CUSTOM\n *\n * Also handles drilldown context parameters:\n * - filter_* : Global filter values\n * - ctx_* : Drilldown context values (clicked item data)\n * - drilldown_* : Drilldown metadata (depth, parent, path)\n */\n\nimport { useEffect, useCallback, useRef, useMemo } from 'react';\nimport { useLocation } from 'react-router';\nimport { useGlobalFilterValues, useDashboardStore } from '../store';\nimport { CONTEXT_PARAM_PREFIX, FILTER_PARAM_PREFIX, DRILLDOWN_PARAM_PREFIX } from './useDrilldown';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface UseFilterUrlSyncOptions {\n /** Whether URL sync is enabled */\n enabled?: boolean;\n /** Prefix for URL params (default: 'filter_') */\n paramPrefix?: string;\n /** Debounce delay in ms for URL updates (default: 300) */\n debounceMs?: number;\n}\n\ninterface DrilldownMetadata {\n /** Current drilldown depth */\n depth: number;\n /** Parent dashboard ID */\n parent: string | null;\n /** Array of dashboard IDs in the drilldown path */\n path: string[];\n}\n\ninterface FilterUrlSyncResult {\n /** Manually sync current filters to URL */\n syncToUrl: () => void;\n /** Get shareable URL with current filters */\n getShareableUrl: () => string;\n /** Clear all filter params from URL */\n clearUrlParams: () => void;\n /** Get context params from URL (ctx_*) */\n getContextFromUrl: () => Record<string, unknown>;\n /** Get drilldown metadata from URL */\n getDrilldownMetadata: () => DrilldownMetadata;\n /** Check if currently in drilldown mode */\n isInDrilldown: boolean;\n /** Current drilldown depth */\n drilldownDepth: number;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Encode filter value for URL\n */\nexport function encodeFilterValue(value: unknown): string {\n if (value === null || value === undefined) {\n return '';\n }\n\n // Arrays use JSON so category labels containing commas remain distinct.\n if (Array.isArray(value)) {\n return JSON.stringify(value);\n }\n\n // Object types\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n\n // Range objects retain their shape and optional bounds.\n if (obj.start || obj.end) {\n return JSON.stringify(obj);\n }\n\n // Numeric range.\n if (obj.min !== undefined || obj.max !== undefined) {\n return JSON.stringify(obj);\n }\n\n // Object with value property (category)\n if (obj.value !== undefined) {\n return JSON.stringify(obj.value);\n }\n\n // Generic object - JSON stringify\n return JSON.stringify(obj);\n }\n\n // Simple value\n return JSON.stringify(value);\n}\n\n/**\n * Decode filter value from URL\n */\nexport function decodeFilterValue(encoded: string, filterType?: string): unknown {\n if (!encoded) {\n return undefined;\n }\n\n // Current links use JSON for scalars/arrays to preserve commas, percent\n // sequences and primitive types. Parse that before decoding legacy values.\n try {\n return JSON.parse(encoded);\n } catch {\n // Legacy links use a second encoded layer or range notation.\n }\n\n // URLSearchParams already decodes the outer URL layer. Legacy links encoded\n // each value a second time; accept those without throwing on a literal percent.\n let decoded = encoded;\n try {\n decoded = decodeURIComponent(encoded);\n } catch {\n // A literal percent is valid filter text, not necessarily an escape.\n }\n if (decoded.startsWith('[') || decoded.startsWith('{')) {\n try {\n return JSON.parse(decoded);\n } catch {\n return decoded;\n }\n }\n\n // Date range\n if (filterType === 'DATE_RANGE' || (encoded.includes('T') && encoded.includes(','))) {\n const parts = encoded.split(',');\n if (parts.length === 2) {\n return {\n start: parts[0] || undefined,\n end: parts[1] || undefined,\n };\n }\n }\n\n // Numeric range (check if both parts are numbers)\n if (filterType === 'NUMERIC_RANGE' || /^-?\\d*\\.?\\d*,-?\\d*\\.?\\d*$/.test(encoded)) {\n const parts = encoded.split(',');\n if (parts.length === 2) {\n const min = parts[0] ? parseFloat(parts[0]) : undefined;\n const max = parts[1] ? parseFloat(parts[1]) : undefined;\n if (!isNaN(min as number) || !isNaN(max as number)) {\n return { min, max };\n }\n }\n }\n\n // Multi-select array\n if (encoded.includes(',') && filterType === 'CATEGORY') {\n return encoded.split(',').map((value) => {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n });\n }\n\n // Try to parse as JSON\n try {\n return JSON.parse(decoded);\n } catch {\n // Simple value\n return decoded;\n }\n}\n\n/**\n * Decode a context param value from URL\n */\nfunction decodeContextValue(encoded: string): unknown {\n if (!encoded) {\n return undefined;\n }\n const decoded = decodeURIComponent(encoded);\n // Try to parse as JSON\n try {\n return JSON.parse(decoded);\n } catch {\n return decoded;\n }\n}\n\n/**\n * Parse URL search params to filter values\n */\nexport function parseUrlToFilters(search: string, paramPrefix: string): Record<string, unknown> {\n const params = new URLSearchParams(search);\n const filters: Record<string, unknown> = {};\n\n params.forEach((value, key) => {\n if (key.startsWith(paramPrefix)) {\n const filterId = key.slice(paramPrefix.length);\n // Try to detect type from value format\n filters[filterId] = decodeFilterValue(value);\n }\n });\n\n return filters;\n}\n\n/**\n * Parse URL search params to context values\n */\nfunction parseUrlToContext(search: string): Record<string, unknown> {\n const params = new URLSearchParams(search);\n const context: Record<string, unknown> = {};\n\n params.forEach((value, key) => {\n if (key.startsWith(CONTEXT_PARAM_PREFIX)) {\n const contextKey = key.slice(CONTEXT_PARAM_PREFIX.length);\n context[contextKey] = decodeContextValue(value);\n }\n });\n\n return context;\n}\n\n/**\n * Parse drilldown metadata from URL\n */\nfunction parseUrlToDrilldownMetadata(search: string): DrilldownMetadata {\n const params = new URLSearchParams(search);\n const depthStr = params.get(`${DRILLDOWN_PARAM_PREFIX}depth`);\n const parent = params.get(`${DRILLDOWN_PARAM_PREFIX}parent`);\n const pathStr = params.get(`${DRILLDOWN_PARAM_PREFIX}path`);\n\n let path: string[] = [];\n if (pathStr) {\n try {\n path = JSON.parse(decodeURIComponent(pathStr));\n } catch {\n path = [];\n }\n }\n\n return {\n depth: depthStr ? parseInt(depthStr, 10) : 0,\n parent: parent || null,\n path,\n };\n}\n\n/** Current-route values include drilldown context; nothing from the previous board is retained. */\nexport function parseUrlToGlobalFilterValues(search: string, paramPrefix: string): Record<string, unknown> {\n const values = parseUrlToFilters(search, paramPrefix);\n Object.entries(parseUrlToContext(search)).forEach(([key, value]) => {\n values[`ctx_${key}`] = value;\n });\n const metadata = parseUrlToDrilldownMetadata(search);\n if (metadata.depth > 0) {\n values.__drilldown_depth = metadata.depth;\n values.__drilldown_parent = metadata.parent;\n values.__drilldown_path = metadata.path;\n }\n return values;\n}\n\n/**\n * Build URL search params from filter values\n */\nexport function filtersToUrlParams(filters: Record<string, unknown>, paramPrefix: string): URLSearchParams {\n const params = new URLSearchParams();\n\n Object.entries(filters).forEach(([filterId, value]) => {\n // Drilldown context already has ctx_/drilldown_ URL parameters. It is\n // available to widgets through the store, but is not a global filter.\n if (filterId.startsWith('ctx_') || filterId.startsWith('__drilldown_')) return;\n if (value !== undefined && value !== null && value !== '') {\n const encoded = encodeFilterValue(value);\n if (encoded) {\n params.set(`${paramPrefix}${filterId}`, encoded);\n }\n }\n });\n\n return params;\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useFilterUrlSync(options: UseFilterUrlSyncOptions = {}): FilterUrlSyncResult {\n const { enabled = true, paramPrefix = FILTER_PARAM_PREFIX, debounceMs = 300 } = options;\n\n // Use react-router's useLocation to react to URL changes\n const location = useLocation();\n\n const filterValues = useGlobalFilterValues();\n const setGlobalFilterValues = useDashboardStore((state) => state.setGlobalFilterValues);\n\n const hydratedLocation = useRef('');\n const lastSyncedValues = useRef<string>('');\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Compute drilldown state from URL - now reactive to location.search changes\n const drilldownMetadata = useMemo(() => {\n if (typeof window === 'undefined') {\n return { depth: 0, parent: null, path: [] };\n }\n return parseUrlToDrilldownMetadata(location.search);\n }, [location.search]);\n\n const isInDrilldown = drilldownMetadata.depth > 0;\n const drilldownDepth = drilldownMetadata.depth;\n\n // Router navigation (including Back on the same dashboard) makes the URL\n // authoritative. Replace rather than merge so removed parameters clear values.\n useEffect(() => {\n if (!enabled) return;\n if (debounceTimer.current) clearTimeout(debounceTimer.current);\n const urlFilters = parseUrlToGlobalFilterValues(window.location.search, paramPrefix);\n const serialized = JSON.stringify(urlFilters);\n if (JSON.stringify(useDashboardStore.getState().globalFilterValues) !== serialized) {\n setGlobalFilterValues(urlFilters);\n }\n lastSyncedValues.current = serialized;\n hydratedLocation.current = `${location.key}:${location.pathname}:${location.search}`;\n }, [enabled, location.key, location.pathname, location.search, paramPrefix, setGlobalFilterValues]);\n\n // Sync filter values to URL when they change\n useEffect(() => {\n if (!enabled || !hydratedLocation.current) return;\n\n // Another URL-sync instance or the route initialization may have updated the\n // store after this render. Never schedule an old empty snapshot over it.\n if (JSON.stringify(filterValues) !== JSON.stringify(useDashboardStore.getState().globalFilterValues)) return;\n\n const currentValues = JSON.stringify(filterValues);\n\n // Skip if values haven't changed\n if (currentValues === lastSyncedValues.current) {\n return;\n }\n\n // Debounce URL updates\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n\n const pathname = location.pathname;\n debounceTimer.current = setTimeout(() => {\n if (window.location.pathname !== pathname) return;\n if (JSON.stringify(useDashboardStore.getState().globalFilterValues) !== currentValues) return;\n const params = filtersToUrlParams(filterValues, paramPrefix);\n\n // Get current URL without filter params (but keep other params like ctx_*, drilldown_*)\n const currentParams = new URLSearchParams(window.location.search);\n // Remove existing filter params only\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n // Merge with new filter params\n params.forEach((value, key) => {\n currentParams.set(key, value);\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n // Update URL without triggering navigation\n window.history.replaceState({ ...window.history.state, filters: filterValues }, '', newUrl);\n\n lastSyncedValues.current = currentValues;\n }, debounceMs);\n\n return () => {\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n };\n }, [enabled, filterValues, paramPrefix, debounceMs, location.pathname, location.key, location.search]);\n\n // Manual sync to URL\n const syncToUrl = useCallback(() => {\n if (!enabled) return;\n\n const params = filtersToUrlParams(filterValues, paramPrefix);\n const currentParams = new URLSearchParams(window.location.search);\n\n // Remove existing filter params only\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n // Merge with new filter params\n params.forEach((value, key) => {\n currentParams.set(key, value);\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n window.history.replaceState({ ...window.history.state, filters: filterValues }, '', newUrl);\n\n lastSyncedValues.current = JSON.stringify(filterValues);\n }, [enabled, filterValues, paramPrefix]);\n\n // Get shareable URL\n const getShareableUrl = useCallback((): string => {\n const params = filtersToUrlParams(filterValues, paramPrefix);\n const baseUrl = `${window.location.origin}${window.location.pathname}`;\n\n // Preserve non-filter params (ctx_*, drilldown_*)\n const currentParams = new URLSearchParams(window.location.search);\n Array.from(currentParams.keys()).forEach((key) => {\n if (!key.startsWith(paramPrefix)) {\n params.set(key, currentParams.get(key) || '');\n }\n });\n\n const search = params.toString();\n return search ? `${baseUrl}?${search}` : baseUrl;\n }, [filterValues, paramPrefix]);\n\n // Clear all filter params from URL\n const clearUrlParams = useCallback(() => {\n const currentParams = new URLSearchParams(window.location.search);\n\n // Remove filter params only (keep ctx_*, drilldown_*)\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n // Keep the router's history state (its `key`/`idx`): wiping it breaks Back\n // navigation across drilldowns and page switches (BOFF-7625).\n window.history.replaceState(window.history.state, '', newUrl);\n lastSyncedValues.current = '{}';\n }, [paramPrefix]);\n\n // Get context params from URL\n const getContextFromUrl = useCallback((): Record<string, unknown> => {\n return parseUrlToContext(window.location.search);\n }, []);\n\n // Get drilldown metadata from URL\n const getDrilldownMetadata = useCallback((): DrilldownMetadata => {\n return parseUrlToDrilldownMetadata(window.location.search);\n }, []);\n\n return {\n syncToUrl,\n getShareableUrl,\n clearUrlParams,\n getContextFromUrl,\n getDrilldownMetadata,\n isInDrilldown,\n drilldownDepth,\n };\n}\n\nexport default useFilterUrlSync;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useGlobalFilters.d.ts","sourceRoot":"","sources":["../../../src/bigconsole/hooks/useGlobalFilters.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useGlobalFilters.d.ts","sourceRoot":"","sources":["../../../src/bigconsole/hooks/useGlobalFilters.ts"],"names":[],"mappings":"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"}