drizzle-cube 0.2.25 → 0.2.27

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 (95) hide show
  1. package/dist/client/charts.js +2 -2
  2. package/dist/client/chunks/{charts-BsXrHSCm.js → charts-BfXEKIrq.js} +44 -44
  3. package/dist/client/chunks/{charts-BsXrHSCm.js.map → charts-BfXEKIrq.js.map} +1 -1
  4. package/dist/client/chunks/charts-DQuOI5HA.js +9465 -0
  5. package/dist/client/chunks/charts-DQuOI5HA.js.map +1 -0
  6. package/dist/client/chunks/components-CWMeA5e-.js +14872 -0
  7. package/dist/client/chunks/components-CWMeA5e-.js.map +1 -0
  8. package/dist/client/chunks/core-DrhYtHHa.js +6 -0
  9. package/dist/client/chunks/core-DrhYtHHa.js.map +1 -0
  10. package/dist/client/chunks/hooks-CAKGR-w0.js +828 -0
  11. package/dist/client/chunks/hooks-CAKGR-w0.js.map +1 -0
  12. package/dist/client/components/AIAssistant/index.d.ts +5 -2
  13. package/dist/client/components/AnalysisBuilder/index.d.ts +6 -0
  14. package/dist/client/components/AnalysisBuilder/types.d.ts +9 -7
  15. package/dist/client/components/AnalysisBuilder/utils/fieldUtils.d.ts +46 -0
  16. package/dist/client/components/AnalysisBuilder/utils/filterUtils.d.ts +19 -0
  17. package/dist/client/components/AnalysisBuilder/utils/idUtils.d.ts +11 -0
  18. package/dist/client/components/AnalysisBuilder/utils/index.d.ts +11 -0
  19. package/dist/client/components/AnalysisBuilder/utils/queryUtils.d.ts +11 -0
  20. package/dist/client/components/AnalysisBuilder/utils/recentFieldsUtils.d.ts +14 -0
  21. package/dist/client/components/AnalysisBuilder/utils/storageUtils.d.ts +42 -0
  22. package/dist/client/components/ConfirmModal.d.ts +30 -0
  23. package/dist/client/components/DashboardFilters/CompactFilterBar.d.ts +13 -0
  24. package/dist/client/components/DashboardFilters/CustomDateDropdown.d.ts +10 -0
  25. package/dist/client/components/DashboardFilters/DashboardFilterConfigModal.d.ts +20 -0
  26. package/dist/client/components/DashboardFilters/DashboardFilterItem.d.ts +14 -0
  27. package/dist/client/components/DashboardFilters/DatePresetChips.d.ts +8 -0
  28. package/dist/client/components/DashboardFilters/FilterChip.d.ts +12 -0
  29. package/dist/client/components/DashboardFilters/FilterValuePopover.d.ts +11 -0
  30. package/dist/client/components/DashboardFilters/XTDDropdown.d.ts +10 -0
  31. package/dist/client/components/DashboardPortletCard.d.ts +13 -34
  32. package/dist/client/components/PortletAnalysisModal.d.ts +4 -2
  33. package/dist/client/components/QueryBuilder/types.d.ts +0 -17
  34. package/dist/client/components/shared/utils.d.ts +33 -109
  35. package/dist/client/components.d.ts +0 -1
  36. package/dist/client/components.js +12 -13
  37. package/dist/client/hooks/queries/index.d.ts +15 -0
  38. package/dist/client/hooks/queries/useCubeLoadQuery.d.ts +67 -0
  39. package/dist/client/hooks/queries/useCubeMetaQuery.d.ts +51 -0
  40. package/dist/client/hooks/queries/useDryRunQuery.d.ts +103 -0
  41. package/dist/client/hooks/queries/useMultiCubeLoadQuery.d.ts +68 -0
  42. package/dist/client/hooks/useAnalysisAI.d.ts +44 -0
  43. package/dist/client/hooks/useAnalysisBuilderHook.d.ts +113 -0
  44. package/dist/client/hooks/useAnalysisChartDefaults.d.ts +35 -0
  45. package/dist/client/hooks/useAnalysisCombinedFields.d.ts +18 -0
  46. package/dist/client/hooks/useAnalysisInitialization.d.ts +25 -0
  47. package/dist/client/hooks/useAnalysisQueryBuilder.d.ts +32 -0
  48. package/dist/client/hooks/useAnalysisQueryExecution.d.ts +38 -0
  49. package/dist/client/hooks/useAnalysisShare.d.ts +23 -0
  50. package/dist/client/hooks/useAnalysisUIState.d.ts +24 -0
  51. package/dist/client/hooks/useDashboardHook.d.ts +108 -0
  52. package/dist/client/hooks/useDebounceQuery.d.ts +48 -0
  53. package/dist/client/hooks/useDirtyStateTracking.d.ts +37 -0
  54. package/dist/client/hooks/useFilterValues.d.ts +4 -1
  55. package/dist/client/hooks.d.ts +4 -2
  56. package/dist/client/hooks.js +12 -170
  57. package/dist/client/hooks.js.map +1 -1
  58. package/dist/client/icons.js +1 -1
  59. package/dist/client/index.d.ts +11 -4
  60. package/dist/client/index.js +99 -70
  61. package/dist/client/providers/CubeMetaProvider.d.ts +1 -1
  62. package/dist/client/providers/CubeProvider.d.ts +7 -3
  63. package/dist/client/providers.js +1 -1
  64. package/dist/client/shared/queryKey.d.ts +1 -0
  65. package/dist/client/shared/utils.d.ts +5 -0
  66. package/dist/client/stores/analysisBuilderStore.d.ts +271 -0
  67. package/dist/client/stores/dashboardStore.d.ts +267 -0
  68. package/dist/client/stores/index.d.ts +13 -0
  69. package/dist/client/styles.css +1 -1
  70. package/dist/client/types.d.ts +27 -1
  71. package/dist/client/utils/chartUtils.d.ts +1 -2
  72. package/dist/client/utils/filterUtils.d.ts +15 -10
  73. package/dist/client/utils/pivotUtils.d.ts +1 -2
  74. package/dist/client-bundle-stats.html +1 -1
  75. package/package.json +13 -19
  76. package/dist/client/chunks/charts-BvLb1eub.js +0 -7427
  77. package/dist/client/chunks/charts-BvLb1eub.js.map +0 -1
  78. package/dist/client/chunks/components-DhA8GIUY.js +0 -14851
  79. package/dist/client/chunks/components-DhA8GIUY.js.map +0 -1
  80. package/dist/client/chunks/core-DcwgBEzv.js +0 -6
  81. package/dist/client/chunks/core-DcwgBEzv.js.map +0 -1
  82. package/dist/client/chunks/index-9x0R-Fme.js +0 -551
  83. package/dist/client/chunks/index-9x0R-Fme.js.map +0 -1
  84. package/dist/client/components/AIAssistant/AIAssistantModal.d.ts +0 -10
  85. package/dist/client/components/AnalysisBuilder/utils.d.ts +0 -103
  86. package/dist/client/components/CubeRelationshipDiagram/CubeNode.d.ts +0 -13
  87. package/dist/client/components/CubeRelationshipDiagram/ERDControls.d.ts +0 -7
  88. package/dist/client/components/CubeRelationshipDiagram/RelationshipEdge.d.ts +0 -11
  89. package/dist/client/components/CubeRelationshipDiagram/index.d.ts +0 -10
  90. package/dist/client/components/CubeRelationshipDiagram/useERDLayout.d.ts +0 -21
  91. package/dist/client/components/PortletEditModal.d.ts +0 -12
  92. package/dist/client/components/shared/CubeMetaExplorer.d.ts +0 -4
  93. package/dist/client/hooks/useCubeMeta.d.ts +0 -39
  94. package/dist/client/hooks/useCubeQuery.d.ts +0 -9
  95. package/dist/client/hooks/useMultiCubeQuery.d.ts +0 -36
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-CAKGR-w0.js","sources":["../../../src/client/shared/types.ts","../../../src/client/shared/utils.ts","../../../src/client/shared/queryKey.ts","../../../src/client/hooks/useDebounceQuery.ts","../../../src/client/hooks/queries/useCubeLoadQuery.ts","../../../src/client/utils/multiQueryUtils.ts","../../../src/client/hooks/queries/useMultiCubeLoadQuery.ts","../../../src/client/hooks/queries/useDryRunQuery.ts","../../../src/client/hooks/useFilterValues.ts","../../../src/client/hooks/useDebounce.ts","../../../src/client/hooks/useResponsiveDashboard.ts","../../../src/client/hooks/useDirtyStateTracking.ts"],"sourcesContent":["/**\n * Shared type definitions used across QueryBuilder and AnalysisBuilder\n */\n\nimport type { CubeQuery, FilterOperator } from '../types'\n\n// ============================================================================\n// Meta endpoint response types\n// ============================================================================\n\nexport interface MetaField {\n name: string // e.g., \"Employees.count\"\n title: string // e.g., \"Total Employees\"\n shortTitle: string // e.g., \"Total Employees\"\n type: string // e.g., \"count\", \"string\", \"time\", \"number\"\n description?: string // Optional description\n}\n\nexport interface MetaCube {\n name: string // e.g., \"Employees\"\n title: string // e.g., \"Employee Analytics\"\n description: string // e.g., \"Employee data and metrics\"\n measures: MetaField[] // e.g., \"Employees.count\"\n dimensions: MetaField[] // e.g., \"Employees.name\"\n segments: MetaField[] // Currently empty in examples\n}\n\nexport interface MetaResponse {\n cubes: MetaCube[]\n}\n\n// ============================================================================\n// Query analysis types for debugging transparency\n// ============================================================================\n\nexport type PrimaryCubeSelectionReason =\n | 'most_dimensions'\n | 'most_connected'\n | 'alphabetical_fallback'\n | 'single_cube'\n\nexport interface PrimaryCubeCandidate {\n cubeName: string\n dimensionCount: number\n joinCount: number\n canReachAll: boolean\n}\n\nexport interface PrimaryCubeAnalysis {\n selectedCube: string\n reason: PrimaryCubeSelectionReason\n explanation: string\n candidates?: PrimaryCubeCandidate[]\n}\n\nexport interface JoinPathStep {\n fromCube: string\n toCube: string\n relationship: 'belongsTo' | 'hasOne' | 'hasMany' | 'belongsToMany'\n joinType: 'inner' | 'left' | 'right' | 'full'\n joinColumns: Array<{\n sourceColumn: string\n targetColumn: string\n }>\n junctionTable?: {\n tableName: string\n sourceColumns: string[]\n targetColumns: string[]\n }\n}\n\nexport interface JoinPathAnalysis {\n targetCube: string\n pathFound: boolean\n path?: JoinPathStep[]\n pathLength?: number\n error?: string\n visitedCubes?: string[]\n}\n\nexport interface PreAggregationAnalysis {\n cubeName: string\n cteAlias: string\n reason: string\n measures: string[]\n joinKeys: Array<{\n sourceColumn: string\n targetColumn: string\n }>\n}\n\nexport interface QuerySummary {\n queryType: 'single_cube' | 'multi_cube_join' | 'multi_cube_cte'\n joinCount: number\n cteCount: number\n hasPreAggregation: boolean\n}\n\nexport interface QueryAnalysis {\n timestamp: string\n cubeCount: number\n cubesInvolved: string[]\n primaryCube: PrimaryCubeAnalysis\n joinPaths: JoinPathAnalysis[]\n preAggregations: PreAggregationAnalysis[]\n querySummary: QuerySummary\n warnings?: string[]\n}\n\n// ============================================================================\n// Validation response from /dry-run endpoint\n// ============================================================================\n\nexport interface ValidationResult {\n valid?: boolean // Our custom property (may not be present in official Cube.js)\n error?: string\n query?: CubeQuery\n sql?: {\n sql: string[]\n params: any[]\n }\n queryType?: string // Always present in successful Cube.js responses\n normalizedQueries?: any[]\n queryOrder?: string[]\n transformedQueries?: any[]\n pivotQuery?: any\n complexity?: string\n cubesUsed?: string[]\n joinType?: string\n // Query analysis for debugging transparency\n analysis?: QueryAnalysis\n}\n\n// ============================================================================\n// Filter operator metadata\n// ============================================================================\n\nexport interface FilterOperatorMeta {\n label: string\n description: string\n requiresValues: boolean\n supportsMultipleValues: boolean\n valueType: 'string' | 'number' | 'date' | 'boolean' | 'any'\n fieldTypes: string[] // Which field types support this operator\n}\n\nexport const FILTER_OPERATORS: Record<FilterOperator, FilterOperatorMeta> = {\n // String operators\n equals: {\n label: 'equals',\n description: 'Exact match',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'boolean', 'time']\n },\n notEquals: {\n label: 'not equals',\n description: 'Does not match',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'boolean', 'time']\n },\n contains: {\n label: 'contains',\n description: 'Contains text (case insensitive)',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n notContains: {\n label: 'not contains',\n description: 'Does not contain text',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n startsWith: {\n label: 'starts with',\n description: 'Starts with text',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n notStartsWith: {\n label: 'not starts with',\n description: 'Does not start with text',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n endsWith: {\n label: 'ends with',\n description: 'Ends with text',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n notEndsWith: {\n label: 'not ends with',\n description: 'Does not end with text',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n like: {\n label: 'like',\n description: 'SQL LIKE pattern matching (case sensitive)',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n notLike: {\n label: 'not like',\n description: 'SQL NOT LIKE pattern matching (case sensitive)',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n ilike: {\n label: 'ilike',\n description: 'SQL ILIKE pattern matching (case insensitive)',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n // Numeric operators\n gt: {\n label: 'greater than',\n description: 'Greater than value',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n gte: {\n label: 'greater than or equal',\n description: 'Greater than or equal to value',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n lt: {\n label: 'less than',\n description: 'Less than value',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n lte: {\n label: 'less than or equal',\n description: 'Less than or equal to value',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n between: {\n label: 'between',\n description: 'Between two values (inclusive)',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n notBetween: {\n label: 'not between',\n description: 'Not between two values',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n // Array operators\n in: {\n label: 'in',\n description: 'Matches any of the provided values',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'boolean']\n },\n notIn: {\n label: 'not in',\n description: 'Does not match any of the provided values',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'boolean']\n },\n // Null/Empty operators\n set: {\n label: 'is set',\n description: 'Is not null/empty',\n requiresValues: false,\n supportsMultipleValues: false,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'time', 'boolean']\n },\n notSet: {\n label: 'is not set',\n description: 'Is null/empty',\n requiresValues: false,\n supportsMultipleValues: false,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'time', 'boolean']\n },\n isEmpty: {\n label: 'is empty',\n description: 'Is empty string or null',\n requiresValues: false,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n isNotEmpty: {\n label: 'is not empty',\n description: 'Is not empty string and not null',\n requiresValues: false,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n // Date operators\n inDateRange: {\n label: 'in date range',\n description: 'Between two dates',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'date',\n fieldTypes: ['time']\n },\n beforeDate: {\n label: 'before date',\n description: 'Before specified date',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'date',\n fieldTypes: ['time']\n },\n afterDate: {\n label: 'after date',\n description: 'After specified date',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'date',\n fieldTypes: ['time']\n },\n // Regex operators\n regex: {\n label: 'matches regex',\n description: 'Matches regular expression pattern',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n notRegex: {\n label: 'not matches regex',\n description: 'Does not match regular expression pattern',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n // PostgreSQL array operators\n arrayContains: {\n label: 'array contains all',\n description: 'Array field contains all specified values (PostgreSQL only)',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'string',\n fieldTypes: ['string']\n },\n arrayOverlaps: {\n label: 'array contains any',\n description: 'Array field contains any of the specified values (PostgreSQL only)',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'string',\n fieldTypes: ['string']\n },\n arrayContained: {\n label: 'array values in',\n description: 'All array field values are within specified values (PostgreSQL only)',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'string',\n fieldTypes: ['string']\n }\n}\n\n// ============================================================================\n// Date range types\n// ============================================================================\n\nexport type DateRangeType =\n | 'custom'\n | 'today'\n | 'yesterday'\n | 'this_week'\n | 'this_month'\n | 'this_quarter'\n | 'this_year'\n | 'last_7_days'\n | 'last_30_days'\n | 'last_week'\n | 'last_month'\n | 'last_quarter'\n | 'last_year'\n | 'last_12_months'\n | 'last_n_days'\n | 'last_n_weeks'\n | 'last_n_months'\n | 'last_n_quarters'\n | 'last_n_years'\n\nexport interface DateRangeOption {\n value: DateRangeType\n label: string\n}\n\nexport const DATE_RANGE_OPTIONS: DateRangeOption[] = [\n { value: 'custom', label: 'Custom' },\n { value: 'today', label: 'Today' },\n { value: 'yesterday', label: 'Yesterday' },\n { value: 'this_week', label: 'This week' },\n { value: 'this_month', label: 'This month' },\n { value: 'this_quarter', label: 'This quarter' },\n { value: 'this_year', label: 'This year' },\n { value: 'last_7_days', label: 'Last 7 days' },\n { value: 'last_30_days', label: 'Last 30 days' },\n { value: 'last_n_days', label: 'Last N days' },\n { value: 'last_week', label: 'Last week' },\n { value: 'last_n_weeks', label: 'Last N weeks' },\n { value: 'last_month', label: 'Last month' },\n { value: 'last_12_months', label: 'Last 12 months' },\n { value: 'last_n_months', label: 'Last N months' },\n { value: 'last_quarter', label: 'Last quarter' },\n { value: 'last_n_quarters', label: 'Last N quarters' },\n { value: 'last_year', label: 'Last year' },\n { value: 'last_n_years', label: 'Last N years' }\n] as const\n\n// ============================================================================\n// Time dimension granularity options\n// ============================================================================\n\nexport const TIME_GRANULARITIES = [\n { value: 'hour', label: 'Hour' },\n { value: 'day', label: 'Day' },\n { value: 'week', label: 'Week' },\n { value: 'month', label: 'Month' },\n { value: 'quarter', label: 'Quarter' },\n { value: 'year', label: 'Year' }\n] as const\n\nexport type TimeGranularity = typeof TIME_GRANULARITIES[number]['value']\n","/**\n * Shared utility functions used across QueryBuilder and AnalysisBuilder\n */\n\nimport type { CubeQuery, Filter, SimpleFilter, GroupFilter } from '../types'\nimport type { MetaField, MetaResponse } from './types'\nimport { FILTER_OPERATORS } from './types'\n\n// ============================================================================\n// Filter type guards\n// ============================================================================\n\n/**\n * Check if a filter is a simple filter\n */\nexport function isSimpleFilter(filter: Filter): filter is SimpleFilter {\n return 'member' in filter && 'operator' in filter && 'values' in filter\n}\n\n/**\n * Check if a filter is a group filter\n */\nexport function isGroupFilter(filter: Filter): filter is GroupFilter {\n return 'type' in filter && 'filters' in filter\n}\n\n/**\n * Check if a filter is an AND filter\n */\nexport function isAndFilter(filter: Filter): filter is GroupFilter {\n return isGroupFilter(filter) && filter.type === 'and'\n}\n\n/**\n * Check if a filter is an OR filter\n */\nexport function isOrFilter(filter: Filter): filter is GroupFilter {\n return isGroupFilter(filter) && filter.type === 'or'\n}\n\n// ============================================================================\n// Filter manipulation functions\n// ============================================================================\n\n/**\n * Flatten all simple filters from a hierarchical filter structure\n */\nexport function flattenFilters(filters: Filter[]): SimpleFilter[] {\n const simple: SimpleFilter[] = []\n\n const flatten = (filter: Filter) => {\n if (isSimpleFilter(filter)) {\n simple.push(filter)\n } else if (isGroupFilter(filter)) {\n filter.filters.forEach(flatten)\n }\n }\n\n filters.forEach(flatten)\n return simple\n}\n\n/**\n * Count total filters in hierarchical structure\n */\nexport function countFilters(filters: Filter[]): number {\n let count = 0\n\n const countFilter = (filter: Filter) => {\n if (isSimpleFilter(filter)) {\n count++\n } else if (isGroupFilter(filter)) {\n filter.filters.forEach(countFilter)\n }\n }\n\n filters.forEach(countFilter)\n return count\n}\n\n/**\n * Create a new simple filter\n */\nexport function createSimpleFilter(member: string, operator: string = 'equals', values: any[] = []): SimpleFilter {\n return {\n member,\n operator: operator as any,\n values\n }\n}\n\n/**\n * Create a new AND filter group\n */\nexport function createAndFilter(filters: Filter[] = []): GroupFilter {\n return {\n type: 'and',\n filters\n }\n}\n\n/**\n * Create a new OR filter group\n */\nexport function createOrFilter(filters: Filter[] = []): GroupFilter {\n return {\n type: 'or',\n filters\n }\n}\n\n/**\n * Clean up filters - backward compatible (returns filters unchanged)\n * @deprecated This function is no longer used as we now support filtering on any schema field\n */\nexport function cleanupFilters(filters: Filter[], _query?: CubeQuery): Filter[] {\n return filters || []\n}\n\n// ============================================================================\n// Filter transformation functions\n// ============================================================================\n\n/**\n * Transform filters from new GroupFilter format to legacy server format\n * Server expects { and: [...] } and { or: [...] } instead of { type: 'and', filters: [...] }\n */\nexport function transformFiltersForServer(filters: Filter[]): any[] {\n const transformFilter = (filter: Filter): any => {\n if (isSimpleFilter(filter)) {\n return filter\n } else if (isGroupFilter(filter)) {\n const transformedSubFilters = filter.filters.map(transformFilter)\n\n if (filter.type === 'and') {\n return { and: transformedSubFilters }\n } else {\n return { or: transformedSubFilters }\n }\n }\n return filter\n }\n\n return filters.map(transformFilter)\n}\n\n/**\n * Transform filters from server/API format to UI format\n * Converts {and: [...]} and {or: [...]} to {type: 'and', filters: [...]} format\n */\nexport function transformFiltersFromServer(filters: any[]): Filter[] {\n return filters.map(filter => {\n if (!filter || typeof filter !== 'object') {\n return filter\n }\n\n // Handle legacy {and: [...]} format\n if ('and' in filter && Array.isArray(filter.and)) {\n return {\n type: 'and',\n filters: transformFiltersFromServer(filter.and)\n } as GroupFilter\n }\n\n // Handle legacy {or: [...]} format\n if ('or' in filter && Array.isArray(filter.or)) {\n return {\n type: 'or',\n filters: transformFiltersFromServer(filter.or)\n } as GroupFilter\n }\n\n // Handle new format {type: 'and', filters: [...]} - process recursively\n if ('type' in filter && 'filters' in filter && Array.isArray(filter.filters)) {\n return {\n type: filter.type,\n filters: transformFiltersFromServer(filter.filters)\n } as GroupFilter\n }\n\n // Simple filter - pass through\n return filter as SimpleFilter\n }).filter(Boolean) // Remove any null/undefined values\n}\n\n// ============================================================================\n// Query utility functions\n// ============================================================================\n\n/**\n * Check if query has any content (measures, dimensions, or timeDimensions)\n */\nexport function hasQueryContent(query: CubeQuery): boolean {\n return Boolean(\n (query.measures && query.measures.length > 0) ||\n (query.dimensions && query.dimensions.length > 0) ||\n (query.timeDimensions && query.timeDimensions.length > 0)\n )\n}\n\n/**\n * Clean query object by removing empty arrays\n */\nexport function cleanQuery(query: CubeQuery): CubeQuery {\n const cleanedQuery: CubeQuery = {}\n\n if (query.measures && query.measures.length > 0) {\n cleanedQuery.measures = query.measures\n }\n\n if (query.dimensions && query.dimensions.length > 0) {\n cleanedQuery.dimensions = query.dimensions\n }\n\n if (query.timeDimensions && query.timeDimensions.length > 0) {\n cleanedQuery.timeDimensions = query.timeDimensions\n }\n\n if (query.filters && query.filters.length > 0) {\n cleanedQuery.filters = query.filters\n }\n\n if (query.order) {\n cleanedQuery.order = query.order\n }\n\n if (query.limit) {\n cleanedQuery.limit = query.limit\n }\n\n if (query.offset) {\n cleanedQuery.offset = query.offset\n }\n\n if (query.segments && query.segments.length > 0) {\n cleanedQuery.segments = query.segments\n }\n\n return cleanedQuery\n}\n\n/**\n * Clean a query and transform filters for server compatibility\n * This version transforms GroupFilter to legacy and/or format\n */\nexport function cleanQueryForServer(query: CubeQuery): CubeQuery {\n const cleanedQuery = cleanQuery(query)\n\n // Apply server transformation to filters\n if (cleanedQuery.filters && cleanedQuery.filters.length > 0) {\n cleanedQuery.filters = transformFiltersForServer(cleanedQuery.filters) as any\n }\n\n return cleanedQuery\n}\n\n/**\n * Transform a Cube.js query from external format to UI internal format\n * This handles format differences between server/API queries and QueryBuilder state\n */\nexport function transformQueryForUI(query: any): CubeQuery {\n if (!query || typeof query !== 'object') {\n return {}\n }\n\n const transformed: CubeQuery = {}\n\n // Copy simple fields if they exist\n if (query.measures) transformed.measures = Array.isArray(query.measures) ? query.measures : []\n if (query.dimensions) transformed.dimensions = Array.isArray(query.dimensions) ? query.dimensions : []\n if (query.timeDimensions) transformed.timeDimensions = Array.isArray(query.timeDimensions) ? query.timeDimensions : []\n if (query.order) transformed.order = query.order\n if (query.limit) transformed.limit = query.limit\n if (query.offset) transformed.offset = query.offset\n if (query.segments) transformed.segments = Array.isArray(query.segments) ? query.segments : []\n\n // Transform filters from server format to UI format\n if (query.filters && Array.isArray(query.filters)) {\n transformed.filters = transformFiltersFromServer(query.filters)\n }\n\n return cleanQuery(transformed)\n}\n\n// ============================================================================\n// Schema utility functions\n// ============================================================================\n\n/**\n * Get cube name from field name (e.g., \"Employees.count\" -> \"Employees\")\n */\nexport function getCubeNameFromField(fieldName: string): string {\n return fieldName.split('.')[0]\n}\n\n/**\n * Get field type from schema\n */\nexport function getFieldType(fieldName: string, schema: MetaResponse): string {\n for (const cube of schema.cubes) {\n // Check measures\n const measure = cube.measures.find(m => m.name === fieldName)\n if (measure) return measure.type\n\n // Check dimensions\n const dimension = cube.dimensions.find(d => d.name === fieldName)\n if (dimension) return dimension.type\n }\n\n return 'string' // Default fallback\n}\n\n/**\n * Get field title from schema metadata, falling back to field name\n */\nexport function getFieldTitle(fieldName: string, schema: MetaResponse | null): string {\n if (!schema) return fieldName\n\n for (const cube of schema.cubes) {\n // Check measures\n const measure = cube.measures.find(m => m.name === fieldName)\n if (measure) return measure.title || measure.shortTitle || fieldName\n\n // Check dimensions\n const dimension = cube.dimensions.find(d => d.name === fieldName)\n if (dimension) return dimension.title || dimension.shortTitle || fieldName\n }\n\n return fieldName // Fallback to field name if not found\n}\n\n/**\n * Get available operators for a field type\n */\nexport function getAvailableOperators(fieldType: string): Array<{operator: string, label: string}> {\n const operators: Array<{operator: string, label: string}> = []\n\n for (const [operator, meta] of Object.entries(FILTER_OPERATORS)) {\n if (meta.fieldTypes.includes(fieldType)) {\n operators.push({\n operator,\n label: meta.label\n })\n }\n }\n\n return operators\n}\n\n/**\n * Get ALL filterable fields from schema\n */\nexport function getAllFilterableFields(schema: MetaResponse): MetaField[] {\n const allFields: MetaField[] = []\n\n schema.cubes.forEach(cube => {\n allFields.push(...cube.measures)\n allFields.push(...cube.dimensions)\n })\n\n return allFields.sort((a, b) => a.name.localeCompare(b.name))\n}\n\n// ============================================================================\n// Date range utility functions\n// ============================================================================\n\n/**\n * Convert DateRangeType to Cube.js compatible date range format\n */\nexport function convertDateRangeTypeToValue(rangeType: string, number?: number): string {\n const typeMap: Record<string, string> = {\n 'today': 'today',\n 'yesterday': 'yesterday',\n 'this_week': 'this week',\n 'this_month': 'this month',\n 'this_quarter': 'this quarter',\n 'this_year': 'this year',\n 'last_7_days': 'last 7 days',\n 'last_30_days': 'last 30 days',\n 'last_week': 'last week',\n 'last_month': 'last month',\n 'last_quarter': 'last quarter',\n 'last_year': 'last year',\n 'last_12_months': 'last 12 months'\n }\n\n // Handle dynamic ranges with number input\n if (rangeType.startsWith('last_n_') && number !== undefined && number > 0) {\n const unit = rangeType.replace('last_n_', '')\n const unitSingular = unit.slice(0, -1) // Remove 's' for singular form\n return number === 1 ? `last ${unitSingular}` : `last ${number} ${unit}`\n }\n\n return typeMap[rangeType] || rangeType\n}\n\n/**\n * Check if a date range type requires a number input\n */\nexport function requiresNumberInput(rangeType: string): boolean {\n return rangeType.startsWith('last_n_')\n}\n\n/**\n * Format date for Cube.js (YYYY-MM-DD)\n */\nexport function formatDateForCube(date: Date): string {\n return date.toISOString().split('T')[0]\n}\n","export function stableStringify(value: unknown): string {\n const seen = new WeakSet<object>()\n\n const stringify = (input: unknown): string => {\n if (input === null || typeof input !== 'object') {\n return JSON.stringify(input)\n }\n\n if (seen.has(input as object)) {\n return '\"[Circular]\"'\n }\n seen.add(input as object)\n\n if (Array.isArray(input)) {\n return `[${input.map((item) => stringify(item)).join(',')}]`\n }\n\n const record = input as Record<string, unknown>\n const keys = Object.keys(record).sort()\n const props = keys.map((key) => `${JSON.stringify(key)}:${stringify(record[key])}`)\n return `{${props.join(',')}}`\n }\n\n return stringify(value)\n}\n","/**\n * useDebounceQuery - Shared debounce logic for query hooks\n *\n * This hook encapsulates the common debouncing pattern used by\n * useCubeLoadQuery and useMultiCubeLoadQuery to prevent excessive API calls\n * when users are actively editing queries.\n *\n * Features:\n * - Debounces value changes with configurable delay\n * - Handles skip-to-unskip transitions (e.g., portlet becoming visible)\n * - Clears debounced value when invalid or skipped\n * - Provides isDebouncing state for UI feedback\n */\n\nimport { useState, useEffect, useRef, useMemo } from 'react'\nimport { stableStringify } from '../shared/queryKey'\n\nexport interface UseDebounceQueryOptions {\n /**\n * Whether the value is valid (has required fields)\n */\n isValid: boolean\n /**\n * Whether to skip the debounced value\n * @default false\n */\n skip?: boolean\n /**\n * Debounce delay in milliseconds\n * @default 300\n */\n debounceMs?: number\n}\n\nexport interface UseDebounceQueryResult<T> {\n /** The debounced value (null if skipped or invalid) */\n debouncedValue: T | null\n /** Whether the hook is currently debouncing (waiting for timer) */\n isDebouncing: boolean\n}\n\n/**\n * Hook for debouncing query values with skip and validity support\n *\n * Usage:\n * ```tsx\n * const { debouncedValue, isDebouncing } = useDebounceQuery(query, {\n * isValid: isValidCubeQuery(query),\n * skip: !isReady,\n * debounceMs: 300\n * })\n * ```\n */\nexport function useDebounceQuery<T>(\n value: T | null,\n options: UseDebounceQueryOptions\n): UseDebounceQueryResult<T> {\n const { isValid, skip = false, debounceMs = 300 } = options\n\n // Debounced state\n const [debouncedValue, setDebouncedValue] = useState<T | null>(null)\n const [isDebouncing, setIsDebouncing] = useState(false)\n const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const lastValueStringRef = useRef<string>('')\n const wasSkippedRef = useRef<boolean>(skip)\n\n // Serialize value for comparison\n const valueString = useMemo(() => {\n if (!value) return ''\n return stableStringify(value)\n }, [value])\n\n // Debounce the value changes\n useEffect(() => {\n // Detect skip-to-unskip transition (e.g., portlet becoming visible)\n const wasSkipped = wasSkippedRef.current\n const justBecameUnskipped = wasSkipped && !skip\n wasSkippedRef.current = skip\n\n // Skip if value hasn't actually changed AND we haven't just become unskipped\n // The justBecameUnskipped check ensures we re-trigger when visibility changes\n if (valueString === lastValueStringRef.current && !justBecameUnskipped) {\n return\n }\n\n // Clear existing timer\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current)\n }\n\n // If value is valid, set debouncing state and schedule update\n if (isValid && !skip) {\n setIsDebouncing(true)\n debounceTimerRef.current = setTimeout(() => {\n lastValueStringRef.current = valueString\n setDebouncedValue(value)\n setIsDebouncing(false)\n }, debounceMs)\n } else {\n // Clear debounced value if invalid or skipped\n lastValueStringRef.current = valueString\n setDebouncedValue(null)\n setIsDebouncing(false)\n }\n\n return () => {\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current)\n }\n }\n }, [valueString, isValid, skip, debounceMs, value])\n\n return {\n debouncedValue,\n isDebouncing,\n }\n}\n","/**\n * useCubeLoadQuery - TanStack Query hook for cube data loading\n *\n * Features:\n * - Built-in debouncing to prevent excessive API calls\n * - Automatic query deduplication\n * - Background refetch support\n * - Proper loading/error states\n * - Query key based on query content for caching\n *\n * This hook replaces the manual debouncing and query execution\n * in useQueryExecution.\n */\n\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useMemo } from 'react'\nimport { useCubeApi } from '../../providers/CubeApiProvider'\nimport type { CubeQuery, CubeResultSet } from '../../types'\nimport { cleanQueryForServer } from '../../shared/utils'\nimport { stableStringify } from '../../shared/queryKey'\nimport { useDebounceQuery } from '../useDebounceQuery'\n\n// Default debounce delay in milliseconds\nconst DEFAULT_DEBOUNCE_MS = 300\n\n/**\n * Create a stable query key from a CubeQuery\n * The key includes all query parameters to ensure proper caching\n */\nexport function createQueryKey(query: CubeQuery | null): readonly unknown[] {\n if (!query) return ['cube', 'load', null] as const\n // Use JSON.stringify for deep equality comparison\n return ['cube', 'load', stableStringify(query)] as const\n}\n\nexport interface UseCubeLoadQueryOptions {\n /**\n * Whether to skip the query\n * @default false\n */\n skip?: boolean\n /**\n * Debounce delay in milliseconds\n * @default 300\n */\n debounceMs?: number\n /**\n * Whether to reset result set when query changes\n * @default true\n */\n resetResultSetOnChange?: boolean\n /**\n * Stale time in milliseconds\n * @default 60 * 1000 (1 minute)\n */\n staleTime?: number\n /**\n * Whether to keep previous data while loading new data\n * @default true\n */\n keepPreviousData?: boolean\n}\n\nexport interface UseCubeLoadQueryResult {\n /** The result set from the query */\n resultSet: CubeResultSet | null\n /** Raw data from the result set */\n rawData: unknown[] | null\n /** Whether the query is loading (initial load) */\n isLoading: boolean\n /** Whether the query is fetching (includes refetch) */\n isFetching: boolean\n /** Whether query is debouncing (waiting for user to stop typing) */\n isDebouncing: boolean\n /** Error if the query failed */\n error: Error | null\n /** The debounced query that was executed */\n debouncedQuery: CubeQuery | null\n /** Whether the current query is valid */\n isValidQuery: boolean\n /** Manually refetch the data */\n refetch: () => void\n /** Clear the query cache */\n clearCache: () => void\n}\n\n/**\n * Check if a query is valid (has at least one measure or dimension)\n */\nfunction isValidCubeQuery(query: CubeQuery | null): boolean {\n if (!query) return false\n const hasMeasures = Boolean(query.measures && query.measures.length > 0)\n const hasDimensions = Boolean(query.dimensions && query.dimensions.length > 0)\n const hasTimeDimensions = Boolean(query.timeDimensions && query.timeDimensions.length > 0)\n return hasMeasures || hasDimensions || hasTimeDimensions\n}\n\n/**\n * TanStack Query hook for loading cube data with debouncing\n *\n * Usage:\n * ```tsx\n * const { resultSet, rawData, isLoading, error } = useCubeLoadQuery(query, {\n * debounceMs: 300,\n * skip: !isReady\n * })\n * ```\n */\nexport function useCubeLoadQuery(\n query: CubeQuery | null,\n options: UseCubeLoadQueryOptions = {}\n): UseCubeLoadQueryResult {\n const {\n skip = false,\n debounceMs = DEFAULT_DEBOUNCE_MS,\n resetResultSetOnChange = true,\n staleTime = 60 * 1000,\n keepPreviousData = true,\n } = options\n\n const { cubeApi } = useCubeApi()\n const queryClient = useQueryClient()\n\n // Validate query\n const isValidQuery = isValidCubeQuery(query)\n\n // Silence unused variable warning - used for future functionality\n void resetResultSetOnChange\n\n // Use shared debounce hook\n const { debouncedValue: debouncedQuery, isDebouncing } = useDebounceQuery(query, {\n isValid: isValidQuery,\n skip,\n debounceMs,\n })\n\n // Transform query for server (converts filter groups)\n const serverQuery = useMemo(() => {\n if (!debouncedQuery) return null\n return cleanQueryForServer(debouncedQuery)\n }, [debouncedQuery])\n\n // Execute query with TanStack Query\n const queryResult = useQuery({\n queryKey: createQueryKey(serverQuery),\n queryFn: async () => {\n if (!serverQuery) throw new Error('No query provided')\n return cubeApi.load(serverQuery)\n },\n enabled: !!serverQuery && !skip,\n staleTime,\n placeholderData: keepPreviousData ? (prevData) => prevData : undefined,\n })\n\n // Extract raw data from result set\n const rawData = useMemo(() => {\n if (!queryResult.data) return null\n try {\n return queryResult.data.rawData()\n } catch {\n return null\n }\n }, [queryResult.data])\n\n // Refetch function - forces immediate refetch\n const refetch = () => {\n if (serverQuery) {\n queryClient.refetchQueries({ queryKey: createQueryKey(serverQuery) })\n }\n }\n\n // Clear cache function\n const clearCache = () => {\n queryClient.removeQueries({ queryKey: ['cube', 'load'] })\n }\n\n // Handle resetResultSetOnChange\n const resultSet = useMemo(() => {\n if (resetResultSetOnChange && isDebouncing) {\n // Keep showing old data while debouncing\n return queryResult.data ?? null\n }\n return queryResult.data ?? null\n }, [queryResult.data, isDebouncing, resetResultSetOnChange])\n\n return {\n resultSet,\n rawData,\n isLoading: queryResult.isLoading || isDebouncing,\n isFetching: queryResult.isFetching,\n isDebouncing,\n error: queryResult.error,\n debouncedQuery,\n isValidQuery,\n refetch,\n clearCache,\n }\n}\n","/**\n * Multi-Query Data Utilities\n * Handles merging results from multiple CubeQuery executions\n *\n * Pattern follows comparisonUtils.ts for metadata injection:\n * - __queryIndex: numeric index of the source query (0-based)\n * - __queryLabel: user-defined or auto-generated label for the query\n */\n\nimport type { CubeResultSet, CubeQuery, QueryMergeStrategy } from '../types'\n\n/**\n * Metadata fields injected into multi-query data\n */\nexport interface MultiQueryMetadata {\n __queryIndex: number\n __queryLabel: string\n}\n\n/**\n * Check if data contains multi-query metadata\n */\nexport function isMultiQueryData(data: unknown[]): boolean {\n return data.length > 0 && typeof data[0] === 'object' && data[0] !== null && '__queryIndex' in data[0]\n}\n\n/**\n * Get unique query labels from multi-query data\n */\nexport function getQueryLabels(data: unknown[]): string[] {\n if (!isMultiQueryData(data)) return []\n\n const labels = new Set<string>()\n for (const row of data) {\n const label = (row as Record<string, unknown>).__queryLabel\n if (typeof label === 'string') {\n labels.add(label)\n }\n }\n return Array.from(labels)\n}\n\n/**\n * Get query indices from multi-query data\n */\nexport function getQueryIndices(data: unknown[]): number[] {\n if (!isMultiQueryData(data)) return []\n\n const indices = new Set<number>()\n for (const row of data) {\n const index = (row as Record<string, unknown>).__queryIndex\n if (typeof index === 'number') {\n indices.add(index)\n }\n }\n return Array.from(indices).sort((a, b) => a - b)\n}\n\n/**\n * Merge results using 'concat' strategy\n * Appends all rows with __queryIndex and __queryLabel metadata\n *\n * @param resultSets - Array of CubeResultSet from each query\n * @param queries - Original CubeQuery objects\n * @param labels - Optional user-defined labels per query\n * @returns Merged data array with query metadata\n */\nexport function mergeResultsConcat(\n resultSets: CubeResultSet[],\n _queries: CubeQuery[],\n labels?: string[]\n): unknown[] {\n const merged: unknown[] = []\n\n resultSets.forEach((resultSet, queryIndex) => {\n const data = resultSet.rawData()\n const label = labels?.[queryIndex] || `Query ${queryIndex + 1}`\n\n data.forEach(row => {\n merged.push({\n ...row,\n __queryIndex: queryIndex,\n __queryLabel: label\n })\n })\n })\n\n return merged\n}\n\n/**\n * Merge results using 'merge' strategy\n * Aligns data by common dimensions (composite key), combining measures from all queries\n *\n * Example:\n * Query 1: [{ date: '2024-01', revenue: 100 }]\n * Query 2: [{ date: '2024-01', cost: 50 }]\n * Result: [{ date: '2024-01', revenue: 100, cost: 50 }]\n *\n * If multiple queries have the same measure, the first query's value is used.\n *\n * @param resultSets - Array of CubeResultSet from each query\n * @param queries - Original CubeQuery objects\n * @param mergeKeys - Dimension fields to align data on (composite key)\n * @param _labels - Optional user-defined labels per query (unused, kept for API compatibility)\n * @returns Merged data array with combined measures\n */\nexport function mergeResultsByKey(\n resultSets: CubeResultSet[],\n queries: CubeQuery[],\n mergeKeys: string[],\n _labels?: string[]\n): unknown[] {\n const mergedMap = new Map<string, Record<string, unknown>>()\n\n resultSets.forEach((resultSet, queryIndex) => {\n const data = resultSet.rawData()\n const measures = queries[queryIndex].measures || []\n\n data.forEach(row => {\n // Create composite key from all merge dimensions\n const keyValue = mergeKeys.map(k => String(row[k] ?? '')).join('|')\n\n if (!mergedMap.has(keyValue)) {\n // Initialize with all dimension values\n const baseRow: Record<string, unknown> = {}\n mergeKeys.forEach(k => { baseRow[k] = row[k] })\n mergedMap.set(keyValue, baseRow)\n }\n\n const mergedRow = mergedMap.get(keyValue)!\n\n // Add measures using raw field names (no prefix)\n // If same measure exists in multiple queries, first one wins\n measures.forEach(measure => {\n if (!(measure in mergedRow)) {\n mergedRow[measure] = row[measure]\n }\n })\n\n // Copy other dimensions (non-measure, non-merge-key fields) from first query\n if (queryIndex === 0) {\n Object.keys(row).forEach(field => {\n if (!mergeKeys.includes(field) && !measures.includes(field)) {\n if (!(field in mergedRow)) {\n mergedRow[field] = row[field]\n }\n }\n })\n }\n })\n })\n\n // Sort by first merge key for consistent ordering\n return Array.from(mergedMap.values()).sort((a, b) => {\n const aKey = String(a[mergeKeys[0]] ?? '')\n const bKey = String(b[mergeKeys[0]] ?? '')\n return aKey.localeCompare(bKey)\n })\n}\n\n/**\n * Main entry point for merging query results\n * Delegates to appropriate strategy implementation\n *\n * @param resultSets - Array of CubeResultSet from each query\n * @param queries - Original CubeQuery objects\n * @param strategy - Merge strategy ('concat' or 'merge')\n * @param mergeKeys - Dimension fields to align on (required for 'merge' strategy)\n * @param labels - Optional user-defined labels per query\n * @returns Merged data array\n */\nexport function mergeQueryResults(\n resultSets: CubeResultSet[],\n queries: CubeQuery[],\n strategy: QueryMergeStrategy,\n mergeKeys?: string[],\n labels?: string[]\n): unknown[] {\n // Handle edge cases\n if (resultSets.length === 0) return []\n if (resultSets.length === 1) return resultSets[0].rawData()\n\n // Use merge strategy if we have merge keys\n if (strategy === 'merge' && mergeKeys && mergeKeys.length > 0) {\n return mergeResultsByKey(resultSets, queries, mergeKeys, labels)\n }\n\n // Fall back to concat strategy\n return mergeResultsConcat(resultSets, queries, labels)\n}\n\n/**\n * Get combined fields from all queries\n * Used for chart configuration to show all available measures/dimensions\n *\n * @param queries - Array of CubeQuery objects\n * @param _labels - Optional user-defined labels per query (unused, kept for API compatibility)\n * @returns Object containing combined measures, dimensions, and time dimensions\n */\nexport function getCombinedFields(\n queries: CubeQuery[],\n _labels?: string[]\n): {\n measures: string[]\n dimensions: string[]\n timeDimensions: string[]\n} {\n const measures = new Set<string>()\n const dimensions = new Set<string>()\n const timeDimensions = new Set<string>()\n\n queries.forEach((query) => {\n // Measures use raw field names (no prefix), de-duplicated\n query.measures?.forEach(m => measures.add(m))\n\n // Dimensions are shared across queries (de-duplicated)\n query.dimensions?.forEach(d => dimensions.add(d))\n\n // Time dimensions are also shared\n query.timeDimensions?.forEach(td => timeDimensions.add(td.dimension))\n })\n\n return {\n measures: Array.from(measures),\n dimensions: Array.from(dimensions),\n timeDimensions: Array.from(timeDimensions)\n }\n}\n\n/**\n * Generate a default label for a query based on its measures\n * Used when user doesn't provide custom labels\n */\nexport function generateQueryLabel(query: CubeQuery, index: number): string {\n // Try to use first measure name without cube prefix\n if (query.measures && query.measures.length > 0) {\n const firstMeasure = query.measures[0]\n const parts = firstMeasure.split('.')\n if (parts.length > 1) {\n return parts[parts.length - 1] // Use measure name without cube prefix\n }\n return firstMeasure\n }\n\n // Fall back to indexed label\n return `Query ${index + 1}`\n}\n\n/**\n * Validate merge key exists in all queries\n * Returns validation result with details\n */\nexport function validateMergeKey(\n queries: CubeQuery[],\n mergeKey: string\n): {\n isValid: boolean\n missingInQueries: number[]\n} {\n const missingInQueries: number[] = []\n\n queries.forEach((query, index) => {\n const allDimensions = [\n ...(query.dimensions || []),\n ...(query.timeDimensions?.map(td => td.dimension) || [])\n ]\n\n if (!allDimensions.includes(mergeKey)) {\n missingInQueries.push(index)\n }\n })\n\n return {\n isValid: missingInQueries.length === 0,\n missingInQueries\n }\n}\n","/**\n * useMultiCubeLoadQuery - TanStack Query hook for multi-cube data loading\n *\n * Features:\n * - Execute multiple cube queries in parallel\n * - Merge results using configurable strategies\n * - Built-in debouncing to prevent excessive API calls\n * - Per-query error tracking\n * - BatchCoordinator integration for dashboard-level batching\n */\n\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useMemo } from 'react'\nimport { useCubeApi } from '../../providers/CubeApiProvider'\nimport type { MultiQueryConfig, CubeResultSet } from '../../types'\nimport { cleanQueryForServer } from '../../shared/utils'\nimport { mergeQueryResults } from '../../utils/multiQueryUtils'\nimport { stableStringify } from '../../shared/queryKey'\nimport { useDebounceQuery } from '../useDebounceQuery'\n\n// Default debounce delay in milliseconds\nconst DEFAULT_DEBOUNCE_MS = 300\n\n/**\n * Create a stable query key for multi-query\n */\nexport function createMultiQueryKey(\n config: MultiQueryConfig | null\n): readonly unknown[] {\n if (!config) return ['cube', 'multiLoad', null] as const\n return ['cube', 'multiLoad', stableStringify(config)] as const\n}\n\nexport interface UseMultiCubeLoadQueryOptions {\n /**\n * Whether to skip the query\n * @default false\n */\n skip?: boolean\n /**\n * Debounce delay in milliseconds\n * @default 300\n */\n debounceMs?: number\n /**\n * Whether to reset result set when query changes\n * @default true\n */\n resetResultSetOnChange?: boolean\n /**\n * Stale time in milliseconds\n * @default 60 * 1000 (1 minute)\n */\n staleTime?: number\n /**\n * Whether to keep previous data while loading new data\n * @default true\n */\n keepPreviousData?: boolean\n}\n\nexport interface UseMultiCubeLoadQueryResult {\n /** Merged data from all queries */\n data: unknown[] | null\n /** Individual result sets from each query */\n resultSets: (CubeResultSet | null)[] | null\n /** Per-query raw data */\n perQueryData: (unknown[] | null)[] | null\n /** Whether any query is still loading (initial load) */\n isLoading: boolean\n /** Whether any query is fetching (includes refetch) */\n isFetching: boolean\n /** Whether query is debouncing (waiting for user to stop typing) */\n isDebouncing: boolean\n /** First error encountered */\n error: Error | null\n /** Per-query errors */\n errors: (Error | null)[]\n /** The debounced config that was executed */\n debouncedConfig: MultiQueryConfig | null\n /** Whether the current config is valid */\n isValidConfig: boolean\n /** Manually refetch the data */\n refetch: () => void\n}\n\n/**\n * Check if a MultiQueryConfig is valid (has at least 2 valid queries)\n */\nfunction isValidMultiQueryConfig(config: MultiQueryConfig | null): boolean {\n if (!config || !config.queries || config.queries.length < 2) return false\n\n const validQueries = config.queries.filter(\n (q) =>\n (q.measures && q.measures.length > 0) ||\n (q.dimensions && q.dimensions.length > 0) ||\n (q.timeDimensions && q.timeDimensions.length > 0)\n )\n\n return validQueries.length >= 2\n}\n\n/**\n * TanStack Query hook for loading multi-cube data with debouncing\n *\n * Usage:\n * ```tsx\n * const { data, isLoading, error } = useMultiCubeLoadQuery(config, {\n * debounceMs: 300,\n * skip: !isMultiQueryMode\n * })\n * ```\n */\nexport function useMultiCubeLoadQuery(\n config: MultiQueryConfig | null,\n options: UseMultiCubeLoadQueryOptions = {}\n): UseMultiCubeLoadQueryResult {\n const {\n skip = false,\n debounceMs = DEFAULT_DEBOUNCE_MS,\n resetResultSetOnChange: _resetResultSetOnChange = true,\n staleTime = 60 * 1000,\n keepPreviousData = true,\n } = options\n\n // Silence unused variable warning - used for future functionality\n void _resetResultSetOnChange\n\n const { cubeApi, batchCoordinator, enableBatching } = useCubeApi()\n const queryClient = useQueryClient()\n\n // Validate config\n const isValidConfig = isValidMultiQueryConfig(config)\n\n // Use shared debounce hook\n const { debouncedValue: debouncedConfig, isDebouncing } = useDebounceQuery(config, {\n isValid: isValidConfig,\n skip,\n debounceMs,\n })\n\n // Transform queries for server\n const serverConfig = useMemo(() => {\n if (!debouncedConfig) return null\n return {\n ...debouncedConfig,\n queries: debouncedConfig.queries.map((q) => cleanQueryForServer(q)),\n }\n }, [debouncedConfig])\n\n // Execute multi-query with TanStack Query\n const queryResult = useQuery({\n queryKey: createMultiQueryKey(serverConfig),\n queryFn: async () => {\n if (!serverConfig) throw new Error('No config provided')\n\n let resultSets: CubeResultSet[]\n\n // Use BatchCoordinator if enabled\n if (enableBatching && batchCoordinator) {\n resultSets = await Promise.all(\n serverConfig.queries.map((query) => batchCoordinator.register(query))\n )\n } else {\n // Direct batch call\n resultSets = await cubeApi.batchLoad(serverConfig.queries)\n }\n\n // Track per-query errors\n const errors: (Error | null)[] = resultSets.map((rs) => {\n if (rs && 'error' in rs && (rs as { error?: string }).error) {\n return new Error((rs as { error: string }).error)\n }\n return null\n })\n\n // Get per-query raw data\n const perQueryData: (unknown[] | null)[] = resultSets.map((rs, i) => {\n if (errors[i]) return null\n try {\n return rs.rawData()\n } catch {\n return null\n }\n })\n\n // Filter successful results for merging\n const successfulResults = resultSets.filter((_, i) => !errors[i])\n const successfulQueries = serverConfig.queries.filter((_, i) => !errors[i])\n\n // Merge results using configured strategy\n const data =\n successfulResults.length > 0\n ? mergeQueryResults(\n successfulResults,\n successfulQueries,\n serverConfig.mergeStrategy,\n serverConfig.mergeKeys,\n serverConfig.queryLabels\n )\n : []\n\n return {\n data,\n resultSets,\n perQueryData,\n errors,\n firstError: errors.find((e) => e !== null) || null,\n }\n },\n enabled: !!serverConfig && !skip,\n staleTime,\n placeholderData: keepPreviousData ? (prevData) => prevData : undefined,\n })\n\n // Refetch function - forces immediate refetch\n const refetch = () => {\n if (serverConfig) {\n queryClient.refetchQueries({\n queryKey: createMultiQueryKey(serverConfig),\n })\n }\n }\n\n // Extract data from query result\n const data = queryResult.data?.data ?? null\n const resultSets = queryResult.data?.resultSets ?? null\n const perQueryData = queryResult.data?.perQueryData ?? null\n const errors = queryResult.data?.errors ?? []\n const error = queryResult.data?.firstError ?? queryResult.error\n\n return {\n data,\n resultSets,\n perQueryData,\n isLoading: queryResult.isLoading || isDebouncing,\n isFetching: queryResult.isFetching,\n isDebouncing,\n error,\n errors,\n debouncedConfig,\n isValidConfig,\n refetch,\n }\n}\n","/**\n * useDryRunQuery - TanStack Query hook for dry-run (debug) data\n *\n * Features:\n * - Fetch SQL and analysis data for query debugging\n * - Support for single and multi-query modes\n * - Built-in caching with TanStack Query\n * - Parallel fetching for multiple queries\n *\n * This hook replaces the debug data useEffect in AnalysisBuilder.\n */\n\nimport { useQuery, useQueries } from '@tanstack/react-query'\nimport { useMemo } from 'react'\nimport { useCubeApi } from '../../providers/CubeApiProvider'\nimport type { CubeQuery } from '../../types'\nimport type { QueryAnalysis } from '../../components/AnalysisBuilder/types'\nimport { cleanQueryForServer } from '../../shared/utils'\nimport { stableStringify } from '../../shared/queryKey'\n\n/**\n * Debug data entry for a single query\n */\nexport interface DebugDataEntry {\n /** Generated SQL and parameters */\n sql: { sql: string; params: unknown[] } | null\n /** Query analysis (dimensions, measures, complexity, etc.) */\n analysis: QueryAnalysis | null\n /** Whether this entry is loading */\n loading: boolean\n /** Error if fetch failed */\n error: Error | null\n}\n\n/**\n * Create a stable query key for dry-run\n */\nexport function createDryRunQueryKey(\n query: CubeQuery | null\n): readonly unknown[] {\n if (!query) return ['cube', 'dryRun', null] as const\n return ['cube', 'dryRun', stableStringify(query)] as const\n}\n\nexport interface UseDryRunQueryOptions {\n /**\n * Whether to skip the query\n * @default false\n */\n skip?: boolean\n /**\n * Stale time in milliseconds\n * @default 5 * 60 * 1000 (5 minutes)\n */\n staleTime?: number\n}\n\nexport interface UseDryRunQueryResult {\n /** Debug data for the query */\n debugData: DebugDataEntry\n /** Manually refetch */\n refetch: () => void\n}\n\n/**\n * TanStack Query hook for single query dry-run (debug) data\n *\n * Usage:\n * ```tsx\n * const { debugData } = useDryRunQuery(query, { skip: !isValidQuery })\n * ```\n */\nexport function useDryRunQuery(\n query: CubeQuery | null,\n options: UseDryRunQueryOptions = {}\n): UseDryRunQueryResult {\n const { skip = false, staleTime = 5 * 60 * 1000 } = options\n const { cubeApi } = useCubeApi()\n\n // Transform query for server\n const serverQuery = useMemo(() => {\n if (!query) return null\n return cleanQueryForServer(query)\n }, [query])\n\n const queryResult = useQuery({\n queryKey: createDryRunQueryKey(serverQuery),\n queryFn: async () => {\n if (!serverQuery) throw new Error('No query provided')\n const result = await cubeApi.dryRun(serverQuery)\n return {\n sql: result.sql,\n analysis: result.analysis,\n }\n },\n enabled: !!serverQuery && !skip,\n staleTime,\n })\n\n const debugData: DebugDataEntry = {\n sql: queryResult.data?.sql ?? null,\n analysis: queryResult.data?.analysis ?? null,\n loading: queryResult.isLoading,\n error: queryResult.error ?? null,\n }\n\n return {\n debugData,\n refetch: () => queryResult.refetch(),\n }\n}\n\nexport interface UseMultiDryRunQueriesOptions {\n /**\n * Whether to skip all queries\n * @default false\n */\n skip?: boolean\n /**\n * Stale time in milliseconds\n * @default 5 * 60 * 1000 (5 minutes)\n */\n staleTime?: number\n}\n\nexport interface UseMultiDryRunQueriesResult {\n /** Debug data for each query */\n debugDataPerQuery: DebugDataEntry[]\n /** Whether any query is loading */\n isLoading: boolean\n /** Manually refetch all */\n refetchAll: () => void\n}\n\n/**\n * TanStack Query hook for multiple query dry-runs (debug) data\n *\n * Fetches debug data for multiple queries in parallel.\n *\n * Usage:\n * ```tsx\n * const { debugDataPerQuery, isLoading } = useMultiDryRunQueries(queries, {\n * skip: !isMultiQueryMode\n * })\n * ```\n */\nexport function useMultiDryRunQueries(\n queries: CubeQuery[],\n options: UseMultiDryRunQueriesOptions = {}\n): UseMultiDryRunQueriesResult {\n const { skip = false, staleTime = 5 * 60 * 1000 } = options\n const { cubeApi } = useCubeApi()\n\n // Transform queries for server\n const serverQueries = useMemo(() => {\n return queries.map((q) => cleanQueryForServer(q))\n }, [queries])\n\n // Use useQueries for parallel fetching\n const queryResults = useQueries({\n queries: serverQueries.map((query) => ({\n queryKey: createDryRunQueryKey(query),\n queryFn: async () => {\n const result = await cubeApi.dryRun(query)\n return {\n sql: result.sql,\n analysis: result.analysis,\n }\n },\n enabled: !skip,\n staleTime,\n })),\n })\n\n // Transform results to DebugDataEntry array\n const debugDataPerQuery: DebugDataEntry[] = queryResults.map((result) => ({\n sql: result.data?.sql ?? null,\n analysis: result.data?.analysis ?? null,\n loading: result.isLoading,\n error: result.error ?? null,\n }))\n\n // Check if any query is loading\n const isLoading = queryResults.some((r) => r.isLoading)\n\n // Refetch all queries\n const refetchAll = () => {\n queryResults.forEach((r) => r.refetch())\n }\n\n return {\n debugDataPerQuery,\n isLoading,\n refetchAll,\n }\n}\n\n/**\n * Combined hook for single or multi-query dry-run based on mode\n *\n * This is a convenience wrapper that automatically chooses between\n * single and multi-query dry-run based on the number of queries.\n *\n * Usage:\n * ```tsx\n * const { debugDataPerQuery } = useDryRunQueries({\n * queries: isMultiQueryMode ? allQueries : [currentQuery],\n * isMultiQueryMode,\n * skip: !isValidQuery\n * })\n * ```\n */\nexport function useDryRunQueries(options: {\n queries: CubeQuery[]\n isMultiQueryMode: boolean\n skip?: boolean\n staleTime?: number\n}): UseMultiDryRunQueriesResult {\n const { queries, isMultiQueryMode, skip = false, staleTime } = options\n\n // For single query mode, wrap in array for consistent interface\n const queriesToFetch = isMultiQueryMode ? queries : queries.slice(0, 1)\n\n return useMultiDryRunQueries(queriesToFetch, { skip, staleTime })\n}\n","/**\n * Hook for fetching distinct field values for filter dropdowns\n * Uses TanStack Query via useCubeLoadQuery for data fetching\n */\n\nimport { useState, useCallback, useRef, useMemo, useEffect } from 'react'\nimport { useCubeLoadQuery } from './queries/useCubeLoadQuery'\nimport type { CubeQuery } from '../types'\n\ninterface UseFilterValuesResult {\n values: any[]\n loading: boolean\n error: string | null\n refetch: () => void\n searchValues: (searchTerm: string, force?: boolean) => void\n}\n\n/**\n * Custom hook to fetch distinct values for a field\n *\n * Uses TanStack Query for server state (data fetching, caching, loading).\n * Values are derived via useMemo from query results - NOT stored in useState.\n */\nexport function useFilterValues(\n fieldName: string | null,\n enabled: boolean = true\n): UseFilterValuesResult {\n const [currentQuery, setCurrentQuery] = useState<CubeQuery | null>(null)\n const lastSearchTerm = useRef<string>('')\n\n // Use TanStack Query hook for data fetching\n const {\n resultSet,\n isLoading,\n error: queryError,\n } = useCubeLoadQuery(currentQuery, {\n skip: !currentQuery || !enabled || !fieldName,\n debounceMs: 150, // Quick debounce for filter searches\n keepPreviousData: true,\n })\n\n // Derive values from resultSet using useMemo (NOT useState)\n // This is the correct pattern - server state stays in TanStack Query\n const values = useMemo(() => {\n // Return empty if no result set, loading, or error\n if (!resultSet || isLoading || queryError || !fieldName) {\n return []\n }\n\n try {\n const data = resultSet.tablePivot()\n const uniqueValues = new Set<any>()\n\n data.forEach((row: any) => {\n const value = row[fieldName]\n if (value !== null && value !== undefined && value !== '') {\n uniqueValues.add(value)\n }\n })\n\n // Convert to array - already sorted by query\n return Array.from(uniqueValues)\n } catch (err) {\n console.error('Error extracting values from result set:', err)\n return []\n }\n }, [resultSet, isLoading, queryError, fieldName])\n\n // Reset query when fieldName becomes null or enabled changes\n useEffect(() => {\n if (!fieldName || !enabled) {\n setCurrentQuery(null)\n lastSearchTerm.current = ''\n }\n }, [fieldName, enabled])\n\n // Refetch function\n const refetch = useCallback(() => {\n if (!fieldName) return\n\n lastSearchTerm.current = ''\n\n try {\n const query: CubeQuery = {\n dimensions: [fieldName],\n limit: 25,\n order: { [fieldName]: 'asc' }\n }\n setCurrentQuery(query)\n } catch (err) {\n console.error('Error creating query:', err)\n }\n }, [fieldName])\n\n // Search function for server-side filtering\n const searchValues = useCallback((searchTerm: string, force: boolean = false) => {\n if (!fieldName) {\n return\n }\n\n // Don't create a new query if the search term hasn't changed (unless forced)\n if (!force && searchTerm === lastSearchTerm.current) {\n return\n }\n\n lastSearchTerm.current = searchTerm\n\n try {\n // Create query inline to avoid dependency issues\n const query: CubeQuery = {\n dimensions: [fieldName],\n limit: 25,\n order: { [fieldName]: 'asc' }\n }\n\n if (searchTerm && searchTerm.trim()) {\n query.filters = [{\n member: fieldName,\n operator: 'contains',\n values: [searchTerm.trim()]\n }]\n }\n\n setCurrentQuery(query)\n } catch (err) {\n console.error('Error creating search query:', err)\n }\n }, [fieldName])\n\n return {\n values,\n loading: isLoading,\n error: queryError ? (queryError instanceof Error ? queryError.message : String(queryError)) : null,\n refetch,\n searchValues\n }\n}\n","/**\n * Custom hook for debouncing values\n * Delays updating the value until after the specified delay has passed\n * since the last change\n */\n\nimport { useState, useEffect } from 'react'\n\n/**\n * Debounces a value by the specified delay\n * @param value The value to debounce\n * @param delay The delay in milliseconds\n * @returns The debounced value\n */\nexport function useDebounce<T>(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState(value)\n\n useEffect(() => {\n // Set up a timer to update the debounced value after the delay\n const handler = setTimeout(() => {\n setDebouncedValue(value)\n }, delay)\n\n // Clean up the timer if the value changes before the delay\n return () => {\n clearTimeout(handler)\n }\n }, [value, delay])\n\n return debouncedValue\n}","/**\n * Custom hook for responsive dashboard layout management\n * Implements a three-tier responsive strategy:\n * - Desktop (1200px+): Normal grid layout with full editing\n * - Scaled (768-1199px): CSS transform scaling, read-only\n * - Mobile (<768px): Single-column stacked layout, read-only\n */\n\nimport { useState, useEffect, useRef, useMemo, useCallback, RefCallback } from 'react'\n\nexport type DashboardDisplayMode = 'desktop' | 'scaled' | 'mobile'\n\nconst DESIGN_WIDTH = 1200\nconst MOBILE_THRESHOLD = 768\n\nexport interface UseResponsiveDashboardResult {\n containerRef: RefCallback<HTMLDivElement>\n containerWidth: number\n displayMode: DashboardDisplayMode\n scaleFactor: number\n isEditable: boolean\n designWidth: number\n}\n\n/**\n * Hook for managing responsive dashboard layouts\n * Uses ResizeObserver for accurate width detection and calculates\n * the appropriate display mode and scale factor\n */\nexport function useResponsiveDashboard(): UseResponsiveDashboardResult {\n // Start with window width as initial estimate\n const [containerWidth, setContainerWidth] = useState(() =>\n typeof window !== 'undefined' ? window.innerWidth : DESIGN_WIDTH\n )\n const observerRef = useRef<ResizeObserver | null>(null)\n const elementRef = useRef<HTMLDivElement | null>(null)\n\n // Ref callback - called when element is attached/detached\n // This is key: unlike useEffect, this fires immediately when the DOM element exists\n const containerRef = useCallback((node: HTMLDivElement | null) => {\n // Cleanup previous observer\n if (observerRef.current) {\n observerRef.current.disconnect()\n observerRef.current = null\n }\n\n elementRef.current = node\n\n if (node) {\n // Get initial width immediately (synchronously when element attaches)\n const initialWidth = node.offsetWidth\n if (initialWidth > 0) {\n setContainerWidth(initialWidth)\n }\n\n // Set up ResizeObserver for ongoing changes\n observerRef.current = new ResizeObserver((entries) => {\n const width = entries[0]?.contentRect.width\n if (width && width > 0) {\n setContainerWidth(width)\n }\n })\n observerRef.current.observe(node)\n }\n }, [])\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (observerRef.current) {\n observerRef.current.disconnect()\n }\n }\n }, [])\n\n // Fallback: window resize listener to catch resize events that ResizeObserver might miss\n // This is particularly important for containers in flex/grid layouts or deeply nested elements\n useEffect(() => {\n const handleWindowResize = () => {\n if (elementRef.current) {\n const width = elementRef.current.offsetWidth\n if (width > 0) {\n setContainerWidth(width)\n }\n }\n }\n\n window.addEventListener('resize', handleWindowResize)\n\n // Also measure after a short delay to catch late layout calculations\n const timeoutId = setTimeout(handleWindowResize, 100)\n\n return () => {\n window.removeEventListener('resize', handleWindowResize)\n clearTimeout(timeoutId)\n }\n }, [])\n\n const displayMode = useMemo<DashboardDisplayMode>(() => {\n if (containerWidth >= DESIGN_WIDTH) return 'desktop'\n if (containerWidth >= MOBILE_THRESHOLD) return 'scaled'\n return 'mobile'\n }, [containerWidth])\n\n const scaleFactor = useMemo(() => {\n if (displayMode !== 'scaled') return 1\n return containerWidth / DESIGN_WIDTH\n }, [containerWidth, displayMode])\n\n const isEditable = displayMode === 'desktop'\n\n return {\n containerRef,\n containerWidth,\n displayMode,\n scaleFactor,\n isEditable,\n designWidth: DESIGN_WIDTH\n }\n}\n","/**\n * useDirtyStateTracking - Track configuration changes and dirty state\n *\n * Extracts dirty state tracking logic from AnalyticsDashboard:\n * - Tracks initial config to prevent saves during initial load\n * - Detects meaningful changes from initial state\n * - Manages dirty state through onDirtyStateChange callback\n *\n * @example\n * const { handleConfigChange, handleSave } = useDirtyStateTracking({\n * initialConfig: config,\n * onConfigChange,\n * onSave,\n * onDirtyStateChange,\n * })\n */\n\nimport { useCallback, useRef } from 'react'\n\nexport interface UseDirtyStateTrackingOptions<T> {\n /** Initial configuration to compare against */\n initialConfig: T\n /** Original config change handler */\n onConfigChange?: (config: T) => void\n /** Original save handler */\n onSave?: (config: T) => Promise<void> | void\n /** Dirty state change callback */\n onDirtyStateChange?: (isDirty: boolean) => void\n}\n\nexport interface UseDirtyStateTrackingResult<T> {\n /** Wrapped config change handler that tracks dirty state */\n handleConfigChange: (config: T) => void\n /** Wrapped save handler that tracks dirty state */\n handleSave: (config: T) => Promise<void>\n /** Whether config has changed from initial */\n hasChanged: () => boolean\n /** Reset the initial config reference (e.g., after external config update) */\n resetInitialConfig: (config: T) => void\n}\n\nexport function useDirtyStateTracking<T>({\n initialConfig,\n onConfigChange,\n onSave,\n onDirtyStateChange,\n}: UseDirtyStateTrackingOptions<T>): UseDirtyStateTrackingResult<T> {\n // Track initial config to prevent saves during initial load\n const initialConfigRef = useRef(initialConfig)\n const hasConfigChangedFromInitial = useRef(false)\n\n // Enhanced save handler that tracks dirty state and prevents saves during initial load\n const handleSave = useCallback(\n async (config: T) => {\n // Don't save if this config hasn't actually changed from the initial load\n if (!hasConfigChangedFromInitial.current) {\n return // Prevent saves during initial load/responsive changes\n }\n\n if (onDirtyStateChange) {\n onDirtyStateChange(true) // Mark as dirty when save starts\n }\n\n try {\n if (onSave) {\n await onSave(config)\n }\n\n // Update our reference point after successful save\n initialConfigRef.current = config\n\n // Mark as clean after successful save\n if (onDirtyStateChange) {\n onDirtyStateChange(false)\n }\n } catch (error) {\n // Keep dirty state if save failed\n console.error('Save failed:', error)\n throw error\n }\n },\n [onSave, onDirtyStateChange]\n )\n\n // Enhanced config change handler that marks as dirty (only after initial load)\n const handleConfigChange = useCallback(\n (config: T) => {\n if (onConfigChange) {\n onConfigChange(config)\n }\n\n // Check if this is a meaningful change from the initial config\n const configString = JSON.stringify(config)\n const initialConfigString = JSON.stringify(initialConfigRef.current)\n\n if (configString !== initialConfigString) {\n hasConfigChangedFromInitial.current = true\n\n if (onDirtyStateChange) {\n onDirtyStateChange(true)\n }\n }\n },\n [onConfigChange, onDirtyStateChange]\n )\n\n // Check if config has changed from initial\n const hasChanged = useCallback(() => {\n return hasConfigChangedFromInitial.current\n }, [])\n\n // Reset initial config reference (useful when config is updated externally)\n const resetInitialConfig = useCallback((config: T) => {\n initialConfigRef.current = config\n hasConfigChangedFromInitial.current = false\n }, [])\n\n return {\n handleConfigChange,\n handleSave,\n hasChanged,\n resetInitialConfig,\n }\n}\n"],"names":["FILTER_OPERATORS","DATE_RANGE_OPTIONS","isSimpleFilter","filter","isGroupFilter","transformFiltersForServer","filters","transformFilter","transformedSubFilters","cleanQuery","query","cleanedQuery","cleanQueryForServer","getAvailableOperators","fieldType","operators","operator","meta","convertDateRangeTypeToValue","rangeType","number","typeMap","unit","unitSingular","requiresNumberInput","formatDateForCube","date","stableStringify","value","seen","stringify","input","item","record","key","useDebounceQuery","options","isValid","skip","debounceMs","debouncedValue","setDebouncedValue","useState","isDebouncing","setIsDebouncing","debounceTimerRef","useRef","lastValueStringRef","wasSkippedRef","valueString","useMemo","useEffect","justBecameUnskipped","DEFAULT_DEBOUNCE_MS","createQueryKey","isValidCubeQuery","hasMeasures","hasDimensions","hasTimeDimensions","useCubeLoadQuery","resetResultSetOnChange","staleTime","keepPreviousData","cubeApi","useCubeApi","queryClient","useQueryClient","isValidQuery","debouncedQuery","serverQuery","queryResult","useQuery","prevData","rawData","refetch","clearCache","isMultiQueryData","data","getQueryLabels","labels","row","label","getQueryIndices","indices","index","a","b","mergeResultsConcat","resultSets","_queries","merged","resultSet","queryIndex","mergeResultsByKey","queries","mergeKeys","_labels","mergedMap","measures","keyValue","k","baseRow","mergedRow","measure","field","aKey","bKey","mergeQueryResults","strategy","getCombinedFields","dimensions","timeDimensions","m","d","td","generateQueryLabel","firstMeasure","parts","validateMergeKey","mergeKey","missingInQueries","createMultiQueryKey","config","isValidMultiQueryConfig","q","useMultiCubeLoadQuery","batchCoordinator","enableBatching","isValidConfig","debouncedConfig","serverConfig","errors","rs","perQueryData","i","successfulResults","_","successfulQueries","e","error","createDryRunQueryKey","useDryRunQuery","result","useMultiDryRunQueries","serverQueries","queryResults","useQueries","debugDataPerQuery","isLoading","r","useDryRunQueries","isMultiQueryMode","queriesToFetch","useFilterValues","fieldName","enabled","currentQuery","setCurrentQuery","lastSearchTerm","queryError","values","uniqueValues","err","useCallback","searchValues","searchTerm","force","useDebounce","delay","handler","DESIGN_WIDTH","MOBILE_THRESHOLD","useResponsiveDashboard","containerWidth","setContainerWidth","observerRef","elementRef","containerRef","node","initialWidth","entries","width","handleWindowResize","timeoutId","displayMode","scaleFactor","useDirtyStateTracking","initialConfig","onConfigChange","onSave","onDirtyStateChange","initialConfigRef","hasConfigChangedFromInitial","handleSave","handleConfigChange","configString","initialConfigString","hasChanged","resetInitialConfig"],"mappings":"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"}
@@ -1,7 +1,10 @@
1
1
  /**
2
- * AI Assistant components export
2
+ * AI Assistant utilities export
3
+ *
4
+ * Note: AIAssistantModal has been deprecated and removed.
5
+ * The inline AI panel in AnalysisBuilder uses useAnalysisAI hook which
6
+ * depends on these utilities.
3
7
  */
4
- export { default as AIAssistantModal } from './AIAssistantModal';
5
8
  export * from './types';
6
9
  export * from './constants';
7
10
  export * from './utils';
@@ -1,3 +1,9 @@
1
1
  import { AnalysisBuilderProps, AnalysisBuilderRef } from './types';
2
+ /**
3
+ * AnalysisBuilder - Main exported component
4
+ *
5
+ * Wraps the inner component with the store provider to ensure
6
+ * each AnalysisBuilder instance has its own isolated state.
7
+ */
2
8
  declare const AnalysisBuilder: import('react').ForwardRefExoticComponent<AnalysisBuilderProps & import('react').RefAttributes<AnalysisBuilderRef>>;
3
9
  export default AnalysisBuilder;
@@ -37,6 +37,10 @@ export type ValidationStatus = 'idle' | 'validating' | 'valid' | 'invalid';
37
37
  export type ExecutionStatus = 'idle' | 'loading' | 'refreshing' | 'success' | 'error';
38
38
  /**
39
39
  * Main state for the AnalysisBuilder component
40
+ *
41
+ * Note: Execution state (results, loading, error) is NOT stored here.
42
+ * All server state is managed by TanStack Query. This state only contains
43
+ * client-side configuration (metrics, breakdowns, filters, validation).
40
44
  */
41
45
  export interface AnalysisBuilderState {
42
46
  /** Selected metrics (measures) */
@@ -49,11 +53,6 @@ export interface AnalysisBuilderState {
49
53
  order?: Record<string, 'asc' | 'desc'>;
50
54
  validationStatus: ValidationStatus;
51
55
  validationError: string | null;
52
- executionStatus: ExecutionStatus;
53
- executionResults: any[] | null;
54
- executionError: string | null;
55
- totalRowCount: number | null;
56
- resultsStale: boolean;
57
56
  }
58
57
  /**
59
58
  * State for the AI query generation panel
@@ -260,15 +259,18 @@ export interface AnalysisResultsPanelProps {
260
259
  debugDataPerQuery?: Array<{
261
260
  sql: {
262
261
  sql: string;
263
- params: any[];
262
+ params: unknown[];
264
263
  } | null;
265
264
  analysis: QueryAnalysis | null;
266
265
  loading: boolean;
267
- error: string | null;
266
+ error: Error | null;
268
267
  }>;
269
268
  onShareClick?: () => void;
270
269
  canShare?: boolean;
271
270
  shareButtonState?: 'idle' | 'copied' | 'copied-no-chart';
271
+ onRefreshClick?: () => void;
272
+ canRefresh?: boolean;
273
+ isRefreshing?: boolean;
272
274
  onClearClick?: () => void;
273
275
  canClear?: boolean;
274
276
  enableAI?: boolean;
@@ -0,0 +1,46 @@
1
+ import { FieldOption, FieldType } from '../types';
2
+ import { MetaResponse, MetaField } from '../../../shared/types';
3
+ /**
4
+ * Get cube name from a field name (e.g., "Employees.count" -> "Employees")
5
+ */
6
+ export declare function getCubeNameFromField(fieldName: string): string;
7
+ /**
8
+ * Get field short name from full name (e.g., "Employees.count" -> "count")
9
+ */
10
+ export declare function getFieldShortName(fieldName: string): string;
11
+ /**
12
+ * Find field metadata from schema
13
+ */
14
+ export declare function findFieldInSchema(fieldName: string, schema: MetaResponse | null): {
15
+ field: MetaField;
16
+ cubeName: string;
17
+ fieldType: FieldType;
18
+ } | null;
19
+ /**
20
+ * Get display title for a field
21
+ */
22
+ export declare function getFieldTitle(fieldName: string, schema: MetaResponse | null): string;
23
+ /**
24
+ * Determine field type from metadata
25
+ */
26
+ export declare function getFieldType(field: MetaField): FieldType;
27
+ /**
28
+ * Convert schema to flat list of field options
29
+ */
30
+ export declare function schemaToFieldOptions(schema: MetaResponse | null, mode: 'metrics' | 'breakdown' | 'filter'): FieldOption[];
31
+ /**
32
+ * Filter field options by search term
33
+ */
34
+ export declare function filterFieldOptions(options: FieldOption[], searchTerm: string, selectedCube?: string | null): FieldOption[];
35
+ /**
36
+ * Group field options by cube
37
+ */
38
+ export declare function groupFieldsByCube(options: FieldOption[]): Map<string, FieldOption[]>;
39
+ /**
40
+ * Get list of cube names from schema
41
+ */
42
+ export declare function getCubeNames(schema: MetaResponse | null): string[];
43
+ /**
44
+ * Get cube title by name
45
+ */
46
+ export declare function getCubeTitle(cubeName: string, schema: MetaResponse | null): string;
@@ -0,0 +1,19 @@
1
+ import { Filter } from '../../../types';
2
+ /**
3
+ * Find date filter for a specific time dimension field
4
+ * Recursively searches filters (including nested and/or groups)
5
+ * Handles both UI format ({type: 'and'/'or', filters: [...]}) and simple filters
6
+ */
7
+ export declare function findDateFilterForField(filters: Filter[], field: string): {
8
+ dateRange: string | string[];
9
+ } | undefined;
10
+ /**
11
+ * Build compareDateRange for a time dimension based on its date filter
12
+ * When comparison is enabled, returns [[currentStart, currentEnd], [priorStart, priorEnd]]
13
+ */
14
+ export declare function buildCompareDateRangeFromFilter(timeDimensionField: string, filters: Filter[]): [string, string][] | undefined;
15
+ /**
16
+ * Remove date filter for a specific field from filters array
17
+ * Returns a new array with the filter removed (immutable)
18
+ */
19
+ export declare function removeComparisonDateFilter(filters: Filter[], field: string): Filter[];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * ID Generation Utilities for AnalysisBuilder
3
+ */
4
+ /**
5
+ * Generate a unique ID for items
6
+ */
7
+ export declare function generateId(): string;
8
+ /**
9
+ * Generate letter label for metrics (A, B, C, ..., AA, AB, ...)
10
+ */
11
+ export declare function generateMetricLabel(index: number): string;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * AnalysisBuilder Utilities - Barrel Export
3
+ *
4
+ * Re-exports all utility functions for convenient imports.
5
+ */
6
+ export { generateId, generateMetricLabel } from './idUtils';
7
+ export { findDateFilterForField, buildCompareDateRangeFromFilter, removeComparisonDateFilter } from './filterUtils';
8
+ export { buildCubeQuery, hasQueryContent } from './queryUtils';
9
+ export { STORAGE_KEY, createInitialState, loadInitialStateFromStorage, saveStateToStorage, loadStateFromStorage, clearStateFromStorage } from './storageUtils';
10
+ export { getCubeNameFromField, getFieldShortName, findFieldInSchema, getFieldTitle, getFieldType, schemaToFieldOptions, filterFieldOptions, groupFieldsByCube, getCubeNames, getCubeTitle } from './fieldUtils';
11
+ export { getRecentFields, addRecentField, getRecentFieldOptions } from './recentFieldsUtils';
@@ -0,0 +1,11 @@
1
+ import { CubeQuery, Filter } from '../../../types';
2
+ import { MetricItem, BreakdownItem } from '../types';
3
+ /**
4
+ * Convert metrics and breakdowns to CubeQuery format
5
+ * Handles comparison mode by building compareDateRange for time dimensions
6
+ */
7
+ export declare function buildCubeQuery(metrics: MetricItem[], breakdowns: BreakdownItem[], filters: Filter[], order?: Record<string, 'asc' | 'desc'>, preserveComparisonFilters?: boolean): CubeQuery;
8
+ /**
9
+ * Check if a query has any content
10
+ */
11
+ export declare function hasQueryContent(metrics: MetricItem[], breakdowns: BreakdownItem[], filters: Filter[]): boolean;
@@ -0,0 +1,14 @@
1
+ import { FieldOption, RecentFieldsStorage } from '../types';
2
+ import { MetaResponse } from '../../../shared/types';
3
+ /**
4
+ * Get recent fields from localStorage
5
+ */
6
+ export declare function getRecentFields(): RecentFieldsStorage;
7
+ /**
8
+ * Add a field to recent fields
9
+ */
10
+ export declare function addRecentField(fieldName: string, mode: 'metrics' | 'breakdowns'): void;
11
+ /**
12
+ * Get recent field options from schema
13
+ */
14
+ export declare function getRecentFieldOptions(schema: MetaResponse | null, mode: 'metrics' | 'breakdown' | 'filter', recentFieldNames: string[]): FieldOption[];
@@ -0,0 +1,42 @@
1
+ import { Filter } from '../../../types';
2
+ import { MetricItem, BreakdownItem, AnalysisBuilderState, AnalysisBuilderStorageState } from '../types';
3
+ export declare const STORAGE_KEY = "drizzle-cube-analysis-builder-state";
4
+ /**
5
+ * Create initial empty state for AnalysisBuilder
6
+ *
7
+ * Note: Only client-side configuration state is stored.
8
+ * Server state (execution results, loading, errors) is managed by TanStack Query.
9
+ */
10
+ export declare function createInitialState(): AnalysisBuilderState;
11
+ /**
12
+ * Load all state from localStorage once (to avoid repeated parsing)
13
+ */
14
+ export declare function loadInitialStateFromStorage(disableLocalStorage: boolean): AnalysisBuilderStorageState | null;
15
+ /**
16
+ * Save state to localStorage
17
+ */
18
+ export declare function saveStateToStorage(state: {
19
+ metrics: MetricItem[];
20
+ breakdowns: BreakdownItem[];
21
+ filters: Filter[];
22
+ chartType: string;
23
+ chartConfig: object;
24
+ displayConfig: object;
25
+ activeView: string;
26
+ }): void;
27
+ /**
28
+ * Load state from localStorage (legacy format for backward compatibility)
29
+ */
30
+ export declare function loadStateFromStorage(): {
31
+ metrics: MetricItem[];
32
+ breakdowns: BreakdownItem[];
33
+ filters: Filter[];
34
+ chartType: string;
35
+ chartConfig: object;
36
+ displayConfig: object;
37
+ activeView: string;
38
+ } | null;
39
+ /**
40
+ * Clear state from localStorage
41
+ */
42
+ export declare function clearStateFromStorage(): void;
@@ -0,0 +1,30 @@
1
+ import { default as React } from 'react';
2
+ export interface ConfirmModalProps {
3
+ isOpen: boolean;
4
+ onClose: () => void;
5
+ onConfirm: () => void | Promise<void>;
6
+ title?: string;
7
+ message: React.ReactNode;
8
+ confirmText?: string;
9
+ cancelText?: string;
10
+ confirmVariant?: 'danger' | 'primary' | 'warning';
11
+ isLoading?: boolean;
12
+ }
13
+ /**
14
+ * A reusable confirmation modal component.
15
+ *
16
+ * Usage:
17
+ * ```tsx
18
+ * <ConfirmModal
19
+ * isOpen={showConfirm}
20
+ * onClose={() => setShowConfirm(false)}
21
+ * onConfirm={handleDelete}
22
+ * title="Delete Portlet"
23
+ * message="Are you sure you want to delete this portlet? This action cannot be undone."
24
+ * confirmText="Delete"
25
+ * confirmVariant="danger"
26
+ * />
27
+ * ```
28
+ */
29
+ declare const ConfirmModal: React.FC<ConfirmModalProps>;
30
+ export default ConfirmModal;
@@ -0,0 +1,13 @@
1
+ import { default as React } from 'react';
2
+ import { DashboardFilter, CubeMeta } from '../../types';
3
+ interface CompactFilterBarProps {
4
+ dashboardFilters: DashboardFilter[];
5
+ schema: CubeMeta | null;
6
+ isEditMode: boolean;
7
+ onDashboardFiltersChange: (filters: DashboardFilter[]) => void;
8
+ onAddFilter?: () => void;
9
+ onEditFilter?: (filterId: string) => void;
10
+ onRemoveFilter?: (filterId: string) => void;
11
+ }
12
+ declare const CompactFilterBar: React.FC<CompactFilterBarProps>;
13
+ export default CompactFilterBar;
@@ -0,0 +1,10 @@
1
+ import { default as React } from 'react';
2
+ interface CustomDateDropdownProps {
3
+ isOpen: boolean;
4
+ onClose: () => void;
5
+ onDateRangeChange: (dateRange: string | string[]) => void;
6
+ currentDateRange?: string | string[];
7
+ anchorRef: React.RefObject<HTMLElement>;
8
+ }
9
+ declare const CustomDateDropdown: React.FC<CustomDateDropdownProps>;
10
+ export default CustomDateDropdown;
@@ -0,0 +1,20 @@
1
+ import { DashboardFilter } from '../../types';
2
+ import { MetaResponse } from '../../shared/types';
3
+ interface DashboardFilterConfigModalProps {
4
+ /** The dashboard filter being edited */
5
+ filter: DashboardFilter;
6
+ /** Full schema (unfiltered) */
7
+ fullSchema: MetaResponse | null;
8
+ /** Filtered schema (dashboard fields only) */
9
+ filteredSchema: MetaResponse | null;
10
+ /** Whether the modal is open */
11
+ isOpen: boolean;
12
+ /** Callback when user saves changes */
13
+ onSave: (filter: DashboardFilter) => void;
14
+ /** Callback when user deletes the filter */
15
+ onDelete: () => void;
16
+ /** Callback when user closes/cancels */
17
+ onClose: () => void;
18
+ }
19
+ export default function DashboardFilterConfigModal({ filter: initialFilter, fullSchema, filteredSchema, isOpen, onSave, onDelete, onClose }: DashboardFilterConfigModalProps): import("react/jsx-runtime").JSX.Element | null;
20
+ export {};
@@ -0,0 +1,14 @@
1
+ import { DashboardFilter } from '../../types';
2
+ import { MetaResponse } from '../../shared/types';
3
+ interface DashboardFilterItemProps {
4
+ /** The dashboard filter to display */
5
+ filter: DashboardFilter;
6
+ /** Schema for field metadata */
7
+ schema: MetaResponse | null;
8
+ /** Callback when filter chip is clicked (opens edit modal) */
9
+ onClick: () => void;
10
+ /** Callback to remove this filter */
11
+ onRemove: () => void;
12
+ }
13
+ export default function DashboardFilterItem({ filter, schema, onClick, onRemove }: DashboardFilterItemProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -0,0 +1,8 @@
1
+ import { default as React } from 'react';
2
+ interface DatePresetChipsProps {
3
+ activePreset: string | null;
4
+ onPresetSelect: (presetValue: string) => void;
5
+ disabled?: boolean;
6
+ }
7
+ declare const DatePresetChips: React.FC<DatePresetChipsProps>;
8
+ export default DatePresetChips;
@@ -0,0 +1,12 @@
1
+ import { default as React } from 'react';
2
+ import { DashboardFilter, CubeMeta } from '../../types';
3
+ interface FilterChipProps {
4
+ filter: DashboardFilter;
5
+ schema: CubeMeta | null;
6
+ isEditMode: boolean;
7
+ onChange: (updatedFilter: DashboardFilter) => void;
8
+ onEdit?: () => void;
9
+ onRemove?: () => void;
10
+ }
11
+ declare const FilterChip: React.FC<FilterChipProps>;
12
+ export default FilterChip;
@@ -0,0 +1,11 @@
1
+ import { default as React } from 'react';
2
+ import { SimpleFilter, CubeMeta } from '../../types';
3
+ interface FilterValuePopoverProps {
4
+ filter: SimpleFilter;
5
+ schema: CubeMeta | null;
6
+ onValuesChange: (values: any[]) => void;
7
+ onClose: () => void;
8
+ anchorRef: React.RefObject<HTMLElement>;
9
+ }
10
+ declare const FilterValuePopover: React.FC<FilterValuePopoverProps>;
11
+ export default FilterValuePopover;
@@ -0,0 +1,10 @@
1
+ import { default as React } from 'react';
2
+ interface XTDDropdownProps {
3
+ isOpen: boolean;
4
+ onClose: () => void;
5
+ onSelect: (xtdValue: string) => void;
6
+ currentXTD?: string | null;
7
+ anchorRef: React.RefObject<HTMLElement>;
8
+ }
9
+ declare const XTDDropdown: React.FC<XTDDropdownProps>;
10
+ export default XTDDropdown;