@gooddata/sdk-ui-dashboard 11.43.0-alpha.0 → 11.43.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/NOTICE +128 -128
  2. package/esm/__version.d.ts +1 -1
  3. package/esm/__version.js +1 -1
  4. package/esm/index.d.ts +3 -2
  5. package/esm/index.d.ts.map +1 -1
  6. package/esm/index.js +1 -1
  7. package/esm/model/commandHandlers/filterContext/dateFilter/removeDateFiltersHandler.d.ts.map +1 -1
  8. package/esm/model/commandHandlers/filterContext/dateFilter/removeDateFiltersHandler.js +22 -4
  9. package/esm/model/commandHandlers/scheduledEmail/createScheduledEmailHandler.d.ts.map +1 -1
  10. package/esm/model/commandHandlers/scheduledEmail/createScheduledEmailHandler.js +3 -1
  11. package/esm/model/commandHandlers/scheduledEmail/csvRawExecutionDefinition.d.ts +2 -2
  12. package/esm/model/commandHandlers/scheduledEmail/csvRawExecutionDefinition.d.ts.map +1 -1
  13. package/esm/model/commandHandlers/scheduledEmail/csvRawExecutionDefinition.js +36 -3
  14. package/esm/model/commandHandlers/scheduledEmail/saveScheduledEmailHandler.d.ts.map +1 -1
  15. package/esm/model/commandHandlers/scheduledEmail/saveScheduledEmailHandler.js +3 -1
  16. package/esm/model/store/executionResults/executionResultsSelectors.d.ts +4 -0
  17. package/esm/model/store/executionResults/executionResultsSelectors.d.ts.map +1 -1
  18. package/esm/model/store/executionResults/executionResultsSelectors.js +4 -1
  19. package/esm/model/store/filtering/dashboardFilterSelectors.d.ts +1 -30
  20. package/esm/model/store/filtering/dashboardFilterSelectors.d.ts.map +1 -1
  21. package/esm/model/store/filtering/dashboardFilterSelectors.js +3 -0
  22. package/esm/model/store/filtering/types.d.ts +33 -0
  23. package/esm/model/store/filtering/types.d.ts.map +1 -0
  24. package/esm/model/store/filtering/types.js +2 -0
  25. package/esm/model/store/tabs/dateFilterConfig/dateFilterConfigSelectors.d.ts +14 -0
  26. package/esm/model/store/tabs/dateFilterConfig/dateFilterConfigSelectors.d.ts.map +1 -1
  27. package/esm/model/store/tabs/dateFilterConfig/dateFilterConfigSelectors.js +35 -0
  28. package/esm/model/store/tabs/parameters/parametersSelectors.d.ts +12 -0
  29. package/esm/model/store/tabs/parameters/parametersSelectors.d.ts.map +1 -1
  30. package/esm/model/store/tabs/parameters/parametersSelectors.js +37 -12
  31. package/esm/presentation/automations/_staging/alerting/DefaultAlertingDialog/DefaultAlertingDialog.d.ts.map +1 -1
  32. package/esm/presentation/automations/_staging/alerting/DefaultAlertingDialog/DefaultAlertingDialog.js +9 -3
  33. package/esm/presentation/automations/_staging/alerting/DefaultAlertingManagementDialog/components/PauseAlertRunner.js +1 -1
  34. package/esm/presentation/automations/connectors/hooks/useBuildAutomationsContext.d.ts +3 -0
  35. package/esm/presentation/automations/connectors/hooks/useBuildAutomationsContext.d.ts.map +1 -0
  36. package/esm/presentation/automations/connectors/hooks/useBuildAutomationsContext.js +110 -0
  37. package/esm/presentation/automations/connectors/hooks/useBuildAutomationsContext.test.d.ts +2 -0
  38. package/esm/presentation/automations/connectors/hooks/useBuildAutomationsContext.test.d.ts.map +1 -0
  39. package/esm/presentation/automations/connectors/hooks/useBuildAutomationsContext.test.js +99 -0
  40. package/esm/presentation/automations/connectors/hooks/useWidgetAutomationFilters.d.ts +4 -0
  41. package/esm/presentation/automations/connectors/hooks/useWidgetAutomationFilters.d.ts.map +1 -0
  42. package/esm/presentation/automations/connectors/hooks/useWidgetAutomationFilters.js +46 -0
  43. package/esm/presentation/automations/contexts/AutomationsContext.d.ts +42 -2
  44. package/esm/presentation/automations/contexts/AutomationsContext.d.ts.map +1 -1
  45. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/DefaultScheduledEmailDialog.d.ts.map +1 -1
  46. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/DefaultScheduledEmailDialog.js +8 -2
  47. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/hooks/useEditScheduledEmail.d.ts +1 -1
  48. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/hooks/useEditScheduledEmail.d.ts.map +1 -1
  49. package/esm/presentation/automations/scheduledEmail/hooks/useWidgetAutomationFilters.d.ts +2 -3
  50. package/esm/presentation/automations/scheduledEmail/hooks/useWidgetAutomationFilters.d.ts.map +1 -1
  51. package/esm/presentation/automations/scheduledEmail/hooks/useWidgetAutomationFilters.js +4 -45
  52. package/esm/presentation/automations/shared/automationFilters/components/AutomationDateFilter.d.ts.map +1 -1
  53. package/esm/presentation/automations/shared/automationFilters/components/AutomationDateFilter.js +9 -16
  54. package/esm/presentation/automations/shared/automationFilters/components/AutomationDateFilter.test.d.ts +2 -0
  55. package/esm/presentation/automations/shared/automationFilters/components/AutomationDateFilter.test.d.ts.map +1 -0
  56. package/esm/presentation/automations/shared/automationFilters/components/AutomationDateFilter.test.js +131 -0
  57. package/esm/presentation/automations/shared/automationFilters/components/AutomationFiltersSelect.d.ts +1 -1
  58. package/esm/presentation/automations/shared/automationFilters/components/AutomationFiltersSelect.d.ts.map +1 -1
  59. package/esm/presentation/automations/shared/automationFilters/hooks/useAutomationVisibleFilters.d.ts.map +1 -1
  60. package/esm/presentation/automations/shared/automationFilters/hooks/useAutomationVisibleFilters.js +1 -1
  61. package/esm/presentation/automations/shared/automationFilters/hooks/useDefaultSelectedFiltersForExistingAutomation.d.ts +1 -1
  62. package/esm/presentation/automations/shared/automationFilters/hooks/useDefaultSelectedFiltersForExistingAutomation.d.ts.map +1 -1
  63. package/esm/presentation/automations/shared/automationFilters/hooks/useDefaultSelectedFiltersForExistingAutomation.js +4 -6
  64. package/esm/presentation/automations/shared/automationFilters/hooks/useDefaultSelectedFiltersForNewAutomation.d.ts +1 -1
  65. package/esm/presentation/automations/shared/automationFilters/hooks/useDefaultSelectedFiltersForNewAutomation.d.ts.map +1 -1
  66. package/esm/presentation/automations/shared/automationFilters/hooks/useDefaultSelectedFiltersForNewAutomation.js +3 -4
  67. package/esm/presentation/automations/shared/automationFilters/hooks/useValidateExistingAutomationFilters.d.ts +1 -1
  68. package/esm/presentation/automations/shared/automationFilters/hooks/useValidateExistingAutomationFilters.d.ts.map +1 -1
  69. package/esm/presentation/automations/shared/automationFilters/hooks/useValidateExistingAutomationFilters.js +4 -10
  70. package/esm/presentation/automations/shared/automationFilters/useAutomationFilters.d.ts +1 -1
  71. package/esm/presentation/automations/shared/automationFilters/useAutomationFilters.d.ts.map +1 -1
  72. package/esm/presentation/automations/shared/automationFilters/useAutomationFilters.js +3 -28
  73. package/esm/presentation/automations/shared/automationFilters/useAutomationFiltersSelect.d.ts +2 -2
  74. package/esm/presentation/automations/shared/automationFilters/useAutomationFiltersSelect.d.ts.map +1 -1
  75. package/esm/presentation/automations/shared/automationFilters/useAutomationFiltersSelect.js +2 -5
  76. package/esm/presentation/automations/shared/automationFilters/utils.d.ts +1 -1
  77. package/esm/presentation/automations/shared/automationFilters/utils.d.ts.map +1 -1
  78. package/esm/presentation/automations/shared/dateFilterConfig/dateFilterConfigConverters.d.ts +10 -0
  79. package/esm/presentation/automations/shared/dateFilterConfig/dateFilterConfigConverters.d.ts.map +1 -0
  80. package/esm/presentation/automations/shared/dateFilterConfig/dateFilterConfigConverters.js +89 -0
  81. package/esm/presentation/automations/shared/dateFilterConfig/dateFilterOptionMapping.d.ts +34 -0
  82. package/esm/presentation/automations/shared/dateFilterConfig/dateFilterOptionMapping.d.ts.map +1 -0
  83. package/esm/presentation/automations/shared/dateFilterConfig/dateFilterOptionMapping.js +261 -0
  84. package/esm/presentation/automations/shared/dateFilterConfig/defaultConfig.d.ts +12 -0
  85. package/esm/presentation/automations/shared/dateFilterConfig/defaultConfig.d.ts.map +1 -0
  86. package/esm/presentation/automations/shared/dateFilterConfig/defaultConfig.js +437 -0
  87. package/esm/presentation/automations/shared/exportUtils/filterUtils.d.ts +3 -0
  88. package/esm/presentation/automations/shared/exportUtils/filterUtils.d.ts.map +1 -0
  89. package/esm/presentation/automations/shared/exportUtils/filterUtils.js +14 -0
  90. package/esm/presentation/automations/shared/hooks/useAttributeFilterDisplayFormFromMap.d.ts +3 -0
  91. package/esm/presentation/automations/shared/hooks/useAttributeFilterDisplayFormFromMap.d.ts.map +1 -0
  92. package/esm/presentation/automations/shared/hooks/useAttributeFilterDisplayFormFromMap.js +7 -0
  93. package/esm/presentation/automations/shared/hooks/useCommonDateFilterTitle.d.ts +3 -0
  94. package/esm/presentation/automations/shared/hooks/useCommonDateFilterTitle.d.ts.map +1 -0
  95. package/esm/presentation/automations/shared/hooks/useCommonDateFilterTitle.js +6 -0
  96. package/esm/presentation/automations/shared/hooks/useDateFiltersTitles.d.ts +4 -0
  97. package/esm/presentation/automations/shared/hooks/useDateFiltersTitles.d.ts.map +1 -0
  98. package/esm/presentation/automations/shared/hooks/useDateFiltersTitles.js +19 -0
  99. package/esm/presentation/automations/shared/hooks/useFiltersNamings.d.ts +13 -0
  100. package/esm/presentation/automations/shared/hooks/useFiltersNamings.d.ts.map +1 -0
  101. package/esm/presentation/automations/shared/hooks/useFiltersNamings.js +196 -0
  102. package/esm/presentation/automations/shared/utils/automationUtils.d.ts +24 -0
  103. package/esm/presentation/automations/shared/utils/automationUtils.d.ts.map +1 -0
  104. package/esm/presentation/automations/shared/utils/automationUtils.js +135 -0
  105. package/esm/presentation/automations/shared/utils/filterContextUtils.d.ts +4 -0
  106. package/esm/presentation/automations/shared/utils/filterContextUtils.d.ts.map +1 -0
  107. package/esm/presentation/automations/shared/utils/filterContextUtils.js +67 -0
  108. package/esm/presentation/filterBar/attributeFilter/dashboardDropdownBody/configuration/hooks/useDependentDateFilterConfigurationState.d.ts.map +1 -1
  109. package/esm/presentation/filterBar/attributeFilter/dashboardDropdownBody/configuration/hooks/useDependentDateFilterConfigurationState.js +34 -16
  110. package/esm/presentation/filterBar/attributeFilter/dashboardDropdownBody/configuration/hooks/useDependentDateFiltersConfiguration.d.ts.map +1 -1
  111. package/esm/presentation/filterBar/attributeFilter/dashboardDropdownBody/configuration/hooks/useDependentDateFiltersConfiguration.js +23 -8
  112. package/esm/presentation/filterBar/attributeFilter/useDependentDateFilters.d.ts.map +1 -1
  113. package/esm/presentation/filterBar/attributeFilter/useDependentDateFilters.js +17 -6
  114. package/esm/presentation/topBar/menuButton/DefaultMenuButton.js +1 -1
  115. package/esm/presentation/widget/insight/ViewModeDashboardInsight/InsightDrillDialog/DrillDialogInsight.d.ts.map +1 -1
  116. package/esm/presentation/widget/insight/ViewModeDashboardInsight/InsightDrillDialog/DrillDialogInsight.js +11 -2
  117. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useCreateAlert.d.ts.map +1 -0
  118. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useInsightWidgetAlerting.d.ts.map +1 -1
  119. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useInsightWidgetAlerting.js +2 -2
  120. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useSaveAlertToBackend.d.ts.map +1 -0
  121. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useUpdateAlert.d.ts.map +1 -0
  122. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useWidgetAutomationFilters.d.ts +4 -0
  123. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useWidgetAutomationFilters.d.ts.map +1 -0
  124. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useWidgetAutomationFilters.js +46 -0
  125. package/esm/presentation/widget/insight/configuration/InsightAlertsNew.d.ts.map +1 -1
  126. package/esm/presentation/widget/insight/configuration/InsightAlertsNew.js +1 -1
  127. package/esm/presentation/widget/insightMenu/DefaultDashboardInsightMenu/DashboardInsightMenu/DashboardInsightMenuItemButton.d.ts.map +1 -1
  128. package/esm/presentation/widget/insightMenu/DefaultDashboardInsightMenu/DashboardInsightMenu/DashboardInsightMenuItemButton.js +2 -2
  129. package/esm/presentation/widget/insightMenu/types.js +1 -1
  130. package/esm/presentation/widget/widget/warningPartialResult/InsightWidgetWarningPartialResult.d.ts.map +1 -1
  131. package/esm/presentation/widget/widget/warningPartialResult/InsightWidgetWarningPartialResult.js +1 -1
  132. package/esm/sdk-ui-dashboard.d.ts +11 -2
  133. package/esm/tsdoc-metadata.json +1 -1
  134. package/package.json +20 -20
  135. package/esm/presentation/automations/_staging/alerting/DefaultAlertingDialog/hooks/useCreateAlert.d.ts.map +0 -1
  136. package/esm/presentation/automations/_staging/alerting/DefaultAlertingDialog/hooks/useSaveAlertToBackend.d.ts.map +0 -1
  137. package/esm/presentation/automations/_staging/alerting/DefaultAlertingDialog/hooks/useUpdateAlert.d.ts.map +0 -1
  138. /package/esm/presentation/{automations/_staging/alerting/DefaultAlertingDialog → widget/insight/configuration/InsightAlertConfig}/hooks/useCreateAlert.d.ts +0 -0
  139. /package/esm/presentation/{automations/_staging/alerting/DefaultAlertingDialog → widget/insight/configuration/InsightAlertConfig}/hooks/useCreateAlert.js +0 -0
  140. /package/esm/presentation/{automations/_staging/alerting/DefaultAlertingDialog → widget/insight/configuration/InsightAlertConfig}/hooks/useSaveAlertToBackend.d.ts +0 -0
  141. /package/esm/presentation/{automations/_staging/alerting/DefaultAlertingDialog → widget/insight/configuration/InsightAlertConfig}/hooks/useSaveAlertToBackend.js +0 -0
  142. /package/esm/presentation/{automations/_staging/alerting/DefaultAlertingDialog → widget/insight/configuration/InsightAlertConfig}/hooks/useUpdateAlert.d.ts +0 -0
  143. /package/esm/presentation/{automations/_staging/alerting/DefaultAlertingDialog → widget/insight/configuration/InsightAlertConfig}/hooks/useUpdateAlert.js +0 -0
@@ -0,0 +1,196 @@
1
+ // (C) 2024-2026 GoodData Corporation
2
+ import { useIntl } from "react-intl";
3
+ import { areObjRefsEqual, dashboardAttributeFilterItemLocalIdentifier, dashboardAttributeFilterItemTitle, getAttributeElementsItems, isAllTimeDashboardDateFilter, isAllValuesDashboardAttributeFilter, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardDateFilterWithDimension, isDashboardMatchAttributeFilter, isDashboardMeasureValueFilter, objRefToString, serializeObjRef, } from "@gooddata/sdk-model";
4
+ import { DateFilterHelpers, getAttributeFilterSubtitle, getLocalizedIcuDateFormatPattern, getMeasureValueFilterConditionLabel, getTextFilterStateText, } from "@gooddata/sdk-ui-filters";
5
+ import { useAutomationsContext } from "../../contexts/AutomationsContext.js";
6
+ import { convertDateFilterConfigToDateFilterOptions } from "../dateFilterConfig/dateFilterConfigConverters.js";
7
+ import { matchDateFilterToDateFilterOptionWithPreference } from "../dateFilterConfig/dateFilterOptionMapping.js";
8
+ import { defaultDateFilterConfig } from "../dateFilterConfig/defaultConfig.js";
9
+ import { ensureAllTimeFilterForExport } from "../exportUtils/filterUtils.js";
10
+ import { useAttributeFilterDisplayFormFromMap } from "./useAttributeFilterDisplayFormFromMap.js";
11
+ import { useCommonDateFilterTitle } from "./useCommonDateFilterTitle.js";
12
+ import { useDateFiltersTitles } from "./useDateFiltersTitles.js";
13
+ const PERCENT_FORMAT_REGEX = /%/;
14
+ function isPercentageFormat(format) {
15
+ return !!format && PERCENT_FORMAT_REGEX.test(format);
16
+ }
17
+ /**
18
+ * Hook that gathers all dependencies needed for filter naming transformations.
19
+ * Reusable across different hooks that need to transform filters to namings.
20
+ *
21
+ * @param filtersForTitles - Array of filters (date filters will be extracted for titles)
22
+ * @returns Object containing all dependencies needed for filter naming transformations
23
+ */
24
+ function useFilterNamingDependencies(filtersForTitles) {
25
+ const intl = useIntl();
26
+ const { locale, settings, catalogMeasures: measures, separators, getCatalogAttributeByRef, } = useAutomationsContext();
27
+ const dateFormat = settings?.formatLocale
28
+ ? getLocalizedIcuDateFormatPattern(settings.formatLocale)
29
+ : settings?.responsiveUiDateFormat;
30
+ const getAttributeFilterDisplayFormFromMap = useAttributeFilterDisplayFormFromMap();
31
+ const dateFiltersForTitles = filtersForTitles.filter(isDashboardDateFilterWithDimension);
32
+ const commonDateFilterTitle = useCommonDateFilterTitle(intl);
33
+ const allDateFiltersTitlesObj = useDateFiltersTitles(dateFiltersForTitles, intl);
34
+ return {
35
+ intl,
36
+ locale,
37
+ dateFormat,
38
+ getAttributeFilterDisplayFormFromMap,
39
+ getCatalogAttributeByRef,
40
+ measures,
41
+ separators,
42
+ commonDateFilterTitle,
43
+ allDateFiltersTitlesObj,
44
+ };
45
+ }
46
+ /**
47
+ * Pure function that transforms filters into filter namings.
48
+ * Extracted to avoid code duplication between useFiltersNamings and useFiltersByTabNamings.
49
+ */
50
+ function transformFiltersToNamings(filtersToDisplay, deps) {
51
+ const { intl, dateFormat, getAttributeFilterDisplayFormFromMap, getCatalogAttributeByRef, measures, separators, commonDateFilterTitle, allDateFiltersTitlesObj, } = deps;
52
+ // we want to show all time filter in the list of filters even if it is not stored
53
+ const extendedFiltersToDisplay = ensureAllTimeFilterForExport(filtersToDisplay);
54
+ return extendedFiltersToDisplay.map((filter) => {
55
+ if (isDashboardAttributeFilter(filter)) {
56
+ const displayForm = getAttributeFilterDisplayFormFromMap(filter.attributeFilter.displayForm);
57
+ if (!displayForm) {
58
+ return undefined;
59
+ }
60
+ const attribute = getCatalogAttributeByRef(displayForm.attribute);
61
+ if (!attribute) {
62
+ return undefined;
63
+ }
64
+ const valuesAsAttributeElements = getAttributeElementsItems(filter.attributeFilter.attributeElements)?.map((element) => ({
65
+ title: element,
66
+ uri: element,
67
+ }));
68
+ const subtitle = getAttributeFilterSubtitle(filter.attributeFilter.negativeSelection, valuesAsAttributeElements, intl);
69
+ return {
70
+ type: "attributeFilter",
71
+ all: isAllValuesDashboardAttributeFilter(filter),
72
+ id: filter.attributeFilter.localIdentifier,
73
+ title: filter.attributeFilter.title ?? attribute.attribute.title,
74
+ subtitle,
75
+ };
76
+ }
77
+ else if (isDashboardDateFilter(filter)) {
78
+ /**
79
+ * Shenanigans inspired by core date filter and dashboard date filter implementation
80
+ * to get the date filter option for its subtitle.
81
+ */
82
+ const dateFilterOptionInfo = matchDateFilterToDateFilterOptionWithPreference(filter, convertDateFilterConfigToDateFilterOptions(defaultDateFilterConfig), undefined);
83
+ const dateFilterOption = DateFilterHelpers.applyExcludeCurrentPeriod(dateFilterOptionInfo.dateFilterOption, dateFilterOptionInfo.excludeCurrentPeriod);
84
+ const subtitle = DateFilterHelpers.getDateFilterTitleUsingTranslator(dateFilterOption, intl, "full", dateFormat);
85
+ const a = filter;
86
+ if (isDashboardDateFilterWithDimension(a)) {
87
+ const key = serializeObjRef(a.dateFilter.dataSet);
88
+ return {
89
+ type: "dateFilter",
90
+ all: isAllTimeDashboardDateFilter(a),
91
+ id: a.dateFilter.localIdentifier ?? `dateFilter:${key}`,
92
+ title: allDateFiltersTitlesObj[key],
93
+ subtitle,
94
+ dataSet: a.dateFilter.dataSet,
95
+ };
96
+ }
97
+ const b = filter;
98
+ if (isDashboardCommonDateFilter(b)) {
99
+ return {
100
+ type: "dateFilter",
101
+ common: true,
102
+ all: isAllTimeDashboardDateFilter(b),
103
+ id: b.dateFilter.localIdentifier ?? "dateFilter:common",
104
+ title: commonDateFilterTitle || intl.formatMessage({ id: "dateFilterDropdown.title" }),
105
+ subtitle,
106
+ };
107
+ }
108
+ return undefined;
109
+ }
110
+ else if (isDashboardArbitraryAttributeFilter(filter)) {
111
+ const { values, negativeSelection, displayForm } = filter.arbitraryAttributeFilter;
112
+ const filterDisplayForm = getAttributeFilterDisplayFormFromMap(displayForm);
113
+ if (!filterDisplayForm) {
114
+ return undefined;
115
+ }
116
+ const attribute = getCatalogAttributeByRef(filterDisplayForm.attribute);
117
+ if (!attribute) {
118
+ return undefined;
119
+ }
120
+ const operator = values.length === 0 && negativeSelection ? "all" : negativeSelection ? "isNot" : "is";
121
+ const subtitle = getTextFilterStateText(operator, values, "", intl);
122
+ const title = dashboardAttributeFilterItemTitle(filter) ?? attribute.attribute.title;
123
+ return {
124
+ type: "attributeFilter",
125
+ all: values.length === 0 && negativeSelection,
126
+ id: dashboardAttributeFilterItemLocalIdentifier(filter),
127
+ title,
128
+ subtitle,
129
+ };
130
+ }
131
+ else if (isDashboardMeasureValueFilter(filter)) {
132
+ const { measure, localIdentifier, title: customTitle, conditions, } = filter.dashboardMeasureValueFilter;
133
+ const catalogMetric = measures.find((m) => areObjRefsEqual(m.measure.ref, measure));
134
+ const defaultTitle = catalogMetric?.measure.title ?? objRefToString(measure);
135
+ const title = customTitle ?? defaultTitle;
136
+ const format = catalogMetric?.measure.format;
137
+ const usePercentage = isPercentageFormat(format);
138
+ const subtitle = getMeasureValueFilterConditionLabel(intl, conditions, {
139
+ usePercentage,
140
+ separators,
141
+ });
142
+ return {
143
+ type: "measureValueFilter",
144
+ all: !conditions || conditions.length === 0,
145
+ id: localIdentifier,
146
+ title,
147
+ subtitle,
148
+ };
149
+ }
150
+ else if (isDashboardMatchAttributeFilter(filter)) {
151
+ const { operator: matchOperator, literal, negativeSelection, displayForm, } = filter.matchAttributeFilter;
152
+ const filterDisplayForm = getAttributeFilterDisplayFormFromMap(displayForm);
153
+ if (!filterDisplayForm) {
154
+ return undefined;
155
+ }
156
+ const attribute = getCatalogAttributeByRef(filterDisplayForm.attribute);
157
+ if (!attribute) {
158
+ return undefined;
159
+ }
160
+ const isNegative = negativeSelection ?? false;
161
+ const operatorMap = {
162
+ contains: isNegative ? "doesNotContain" : "contains",
163
+ startsWith: isNegative ? "doesNotStartWith" : "startsWith",
164
+ endsWith: isNegative ? "doesNotEndWith" : "endsWith",
165
+ };
166
+ const operator = operatorMap[matchOperator] ?? "contains";
167
+ const subtitle = getTextFilterStateText(operator, [], literal, intl);
168
+ const title = dashboardAttributeFilterItemTitle(filter) ?? attribute.attribute.title;
169
+ return {
170
+ type: "attributeFilter",
171
+ all: false,
172
+ id: dashboardAttributeFilterItemLocalIdentifier(filter),
173
+ title,
174
+ subtitle,
175
+ };
176
+ }
177
+ else {
178
+ return undefined;
179
+ }
180
+ });
181
+ }
182
+ export function useFiltersNamings(filtersToDisplay) {
183
+ const deps = useFilterNamingDependencies(filtersToDisplay);
184
+ return transformFiltersToNamings(filtersToDisplay, deps);
185
+ }
186
+ export function useFiltersByTabNamings(tabFilters) {
187
+ // Collect all filters from all tabs for titles lookup
188
+ const allFilters = Object.values(tabFilters).flat();
189
+ const deps = useFilterNamingDependencies(allFilters);
190
+ // Process each tab's filters separately using the shared transformation function
191
+ const result = {};
192
+ Object.entries(tabFilters).forEach(([tabId, filters]) => {
193
+ result[tabId] = transformFiltersToNamings(filters, deps);
194
+ });
195
+ return result;
196
+ }
@@ -0,0 +1,24 @@
1
+ import { type FilterContextItem, type IAutomationMetadataObject, type IAutomationMetadataObjectDefinition, type IAutomationRecipient, type IExportDefinitionMetadataObject, type IExportDefinitionMetadataObjectDefinition, type IFilter, type IUser, type IWorkspaceUser } from "@gooddata/sdk-model";
2
+ export declare const isDashboardAutomation: (automation: IAutomationMetadataObject | IAutomationMetadataObjectDefinition | undefined) => boolean;
3
+ export declare const isVisualisationAutomation: (automation: IAutomationMetadataObject | IAutomationMetadataObjectDefinition | undefined) => boolean;
4
+ export declare const isCsvVisualizationAutomation: (automation: IAutomationMetadataObject | IAutomationMetadataObjectDefinition | undefined) => boolean;
5
+ export declare const isCsvVisualizationExportDefinition: (exportDefinition: IExportDefinitionMetadataObject | IExportDefinitionMetadataObjectDefinition | undefined) => boolean;
6
+ export declare const isXlsxVisualizationAutomation: (automation: IAutomationMetadataObject | IAutomationMetadataObjectDefinition | undefined) => boolean;
7
+ export declare const isXlsxVisualizationExportDefinition: (exportDefinition: IExportDefinitionMetadataObject | IExportDefinitionMetadataObjectDefinition | undefined) => boolean;
8
+ export declare const getAutomationDashboardFilters: (automation: IAutomationMetadataObject | IAutomationMetadataObjectDefinition | undefined) => FilterContextItem[] | undefined;
9
+ /**
10
+ * Extracts dashboard filters structured by tab from automation metadata object.
11
+ * Returns filtersByTab from the first dashboard export definition.
12
+ */
13
+ export declare const getAutomationDashboardFiltersByTab: (automation: IAutomationMetadataObject | IAutomationMetadataObjectDefinition | undefined) => Record<string, FilterContextItem[]> | undefined;
14
+ export declare const getAutomationVisualizationFilters: (automation: IAutomationMetadataObject | IAutomationMetadataObjectDefinition | undefined) => {
15
+ executionFilters: IFilter[] | undefined;
16
+ filterContextItems: FilterContextItem[] | undefined;
17
+ };
18
+ export declare const getAutomationAlertFilters: (automation: IAutomationMetadataObject | IAutomationMetadataObjectDefinition | undefined) => IFilter[] | undefined;
19
+ export declare const areAutomationsEqual: (originalAutomation: IAutomationMetadataObjectDefinition, updatedAutomation: IAutomationMetadataObjectDefinition) => boolean;
20
+ export declare const convertCurrentUserToAutomationRecipient: (users: IWorkspaceUser[], user: IUser) => IAutomationRecipient;
21
+ export declare const convertCurrentUserToWorkspaceUser: (users: IWorkspaceUser[], user: IUser) => IWorkspaceUser;
22
+ export declare const convertUserToAutomationRecipient: (user: IUser) => IAutomationRecipient;
23
+ export declare const convertExternalRecipientToAutomationRecipient: (externalRecipient: string) => IAutomationRecipient;
24
+ //# sourceMappingURL=automationUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automationUtils.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/automations/shared/utils/automationUtils.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,mCAAmC,EACxC,KAAK,oBAAoB,EAEzB,KAAK,+BAA+B,EACpC,KAAK,yCAAyC,EAE9C,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,cAAc,EAKtB,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,qBAAqB,sGAYjC,CAAC;AAEF,eAAO,MAAM,yBAAyB,sGAYrC,CAAC;AAEF,eAAO,MAAM,4BAA4B,sGAQxC,CAAC;AAEF,eAAO,MAAM,kCAAkC,wHAW9C,CAAC;AAEF,eAAO,MAAM,6BAA6B,sGAQzC,CAAC;AAEF,eAAO,MAAM,mCAAmC,wHAW/C,CAAC;AAEF,eAAO,MAAM,6BAA6B,8HAYzC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kCAAkC,8IAY9C,CAAC;AAEF,eAAO,MAAM,iCAAiC;;;CAuB7C,CAAC;AAEF,eAAO,MAAM,yBAAyB,oHAQrC,CAAC;AAOF,eAAO,MAAM,mBAAmB,8HAqB/B,CAAC;AAEF,eAAO,MAAM,uCAAuC,gEAOnD,CAAC;AAEF,eAAO,MAAM,iCAAiC,0DAe7C,CAAC;AAEF,eAAO,MAAM,gCAAgC,uCAO5C,CAAC;AAEF,eAAO,MAAM,6CAA6C,qDASzD,CAAC"}
@@ -0,0 +1,135 @@
1
+ // (C) 2024-2026 GoodData Corporation
2
+ import { isEqual, omit, pick } from "lodash-es";
3
+ import { isExportDefinitionDashboardRequestPayload, isExportDefinitionVisualizationObjectRequestPayload, isFilter, isFilterContextItem, } from "@gooddata/sdk-model";
4
+ export const isDashboardAutomation = (automation) => {
5
+ if (!automation) {
6
+ return false;
7
+ }
8
+ return (automation.exportDefinitions?.some((exportDefinition) => {
9
+ return isExportDefinitionDashboardRequestPayload(exportDefinition.requestPayload);
10
+ }) ?? false);
11
+ };
12
+ export const isVisualisationAutomation = (automation) => {
13
+ if (!automation) {
14
+ return false;
15
+ }
16
+ return (automation.exportDefinitions?.some((exportDefinition) => {
17
+ return isExportDefinitionVisualizationObjectRequestPayload(exportDefinition.requestPayload);
18
+ }) ?? false);
19
+ };
20
+ export const isCsvVisualizationAutomation = (automation) => {
21
+ if (!automation) {
22
+ return false;
23
+ }
24
+ return automation.exportDefinitions?.some(isCsvVisualizationExportDefinition) ?? false;
25
+ };
26
+ export const isCsvVisualizationExportDefinition = (exportDefinition) => {
27
+ if (!exportDefinition) {
28
+ return false;
29
+ }
30
+ return (isExportDefinitionVisualizationObjectRequestPayload(exportDefinition.requestPayload) &&
31
+ exportDefinition.requestPayload.format === "CSV");
32
+ };
33
+ export const isXlsxVisualizationAutomation = (automation) => {
34
+ if (!automation) {
35
+ return false;
36
+ }
37
+ return automation.exportDefinitions?.some(isXlsxVisualizationExportDefinition) ?? false;
38
+ };
39
+ export const isXlsxVisualizationExportDefinition = (exportDefinition) => {
40
+ if (!exportDefinition) {
41
+ return false;
42
+ }
43
+ return (isExportDefinitionVisualizationObjectRequestPayload(exportDefinition.requestPayload) &&
44
+ exportDefinition.requestPayload.format === "XLSX");
45
+ };
46
+ export const getAutomationDashboardFilters = (automation) => {
47
+ if (!automation) {
48
+ return undefined;
49
+ }
50
+ return automation.exportDefinitions?.find((exportDefinition) => {
51
+ return isExportDefinitionDashboardRequestPayload(exportDefinition.requestPayload);
52
+ })?.requestPayload?.content.filters?.filter((f) => isFilterContextItem(f));
53
+ };
54
+ /**
55
+ * Extracts dashboard filters structured by tab from automation metadata object.
56
+ * Returns filtersByTab from the first dashboard export definition.
57
+ */
58
+ export const getAutomationDashboardFiltersByTab = (automation) => {
59
+ if (!automation) {
60
+ return undefined;
61
+ }
62
+ return automation.exportDefinitions?.find((exportDefinition) => {
63
+ return isExportDefinitionDashboardRequestPayload(exportDefinition.requestPayload);
64
+ })?.requestPayload?.content.filtersByTab;
65
+ };
66
+ export const getAutomationVisualizationFilters = (automation) => {
67
+ if (!automation) {
68
+ return { executionFilters: undefined, filterContextItems: undefined };
69
+ }
70
+ const executionFilters = automation.exportDefinitions?.find((exportDefinition) => {
71
+ return isExportDefinitionVisualizationObjectRequestPayload(exportDefinition.requestPayload);
72
+ })?.requestPayload?.content.filters?.filter((f) => isFilter(f));
73
+ const filterContextItems = automation.exportDefinitions?.find((exportDefinition) => {
74
+ return isExportDefinitionVisualizationObjectRequestPayload(exportDefinition.requestPayload);
75
+ })?.requestPayload?.content.filters?.filter((f) => isFilterContextItem(f));
76
+ return {
77
+ executionFilters: (executionFilters ?? []).length > 0 ? executionFilters : undefined,
78
+ filterContextItems: (filterContextItems ?? []).length > 0 ? filterContextItems : undefined,
79
+ };
80
+ };
81
+ export const getAutomationAlertFilters = (automation) => {
82
+ if (!automation) {
83
+ return undefined;
84
+ }
85
+ return automation.alert?.execution?.filters?.filter(isFilter);
86
+ };
87
+ const sortByFormat = (a, b) => a.requestPayload.format.localeCompare(b.requestPayload.format);
88
+ export const areAutomationsEqual = (originalAutomation, updatedAutomation) => {
89
+ const automationWithoutExportDefinitions = omit(updatedAutomation, "exportDefinitions");
90
+ const origAutomationWithoutExportDefinitions = omit(originalAutomation, "exportDefinitions");
91
+ // We only want to compare requestPayload and title of exportDefinitions, rest may be omitted as it is just arbitrary
92
+ // metadata that is not relevant for the comparison and causes false positive results when comparing new and old def.
93
+ // Sorting is done just to avoid false positive result of different order of export definitions.
94
+ const automationExportDefinitions = updatedAutomation.exportDefinitions
95
+ ?.map((exportDefinition) => pick(exportDefinition, ["requestPayload", "title"]))
96
+ .sort(sortByFormat);
97
+ const origAutomationExportDefinitions = originalAutomation.exportDefinitions
98
+ ?.map((exportDefinition) => pick(exportDefinition, ["requestPayload", "title"]))
99
+ .sort(sortByFormat);
100
+ return (isEqual(automationWithoutExportDefinitions, origAutomationWithoutExportDefinitions) &&
101
+ isEqual(automationExportDefinitions, origAutomationExportDefinitions));
102
+ };
103
+ export const convertCurrentUserToAutomationRecipient = (users, user) => {
104
+ const foundUser = users.find((u) => u.login === user.login);
105
+ return convertUserToAutomationRecipient(foundUser ?? user);
106
+ };
107
+ export const convertCurrentUserToWorkspaceUser = (users, user) => {
108
+ const foundUser = users.find((u) => u.login === user.login);
109
+ return (foundUser ?? {
110
+ email: user.email ?? "",
111
+ fullName: user.fullName,
112
+ status: "ENABLED",
113
+ login: user.login,
114
+ lastName: user.lastName,
115
+ firstName: user.firstName,
116
+ uri: user.login,
117
+ ref: user.ref,
118
+ });
119
+ };
120
+ export const convertUserToAutomationRecipient = (user) => {
121
+ return {
122
+ id: user.login,
123
+ email: user.email,
124
+ name: user.fullName,
125
+ type: "user",
126
+ };
127
+ };
128
+ export const convertExternalRecipientToAutomationRecipient = (externalRecipient) => {
129
+ return {
130
+ id: externalRecipient,
131
+ email: externalRecipient,
132
+ name: externalRecipient,
133
+ type: "externalUser",
134
+ };
135
+ };
@@ -0,0 +1,4 @@
1
+ import { type FilterContextItem } from "@gooddata/sdk-model";
2
+ import { type IDashboardFilter } from "../../../../types.js";
3
+ export declare function dashboardFilterToFilterContextItem(filter: IDashboardFilter, keepDatasets: boolean): FilterContextItem | undefined;
4
+ //# sourceMappingURL=filterContextUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filterContextUtils.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/automations/shared/utils/filterContextUtils.ts"],"names":[],"mappings":"AAKA,OAAO,EAEH,KAAK,iBAAiB,EAmBzB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,wBAAgB,kCAAkC,CAC9C,MAAM,EAAE,gBAAgB,EACxB,YAAY,EAAE,OAAO,GACtB,iBAAiB,GAAG,SAAS,CA8E/B"}
@@ -0,0 +1,67 @@
1
+ // (C) 2021-2026 GoodData Corporation
2
+ // Derived from _staging/dashboard/dashboardFilterContext.ts
3
+ import { NotSupported } from "@gooddata/sdk-backend-spi";
4
+ import { filterAttributeElements, filterLocalIdentifier, filterObjRef, isAbsoluteDateFilter, isAllTimeDateFilter, isArbitraryAttributeFilter, isAttributeFilterWithSelection, isMatchAttributeFilter, isMeasureValueFilter, isNegativeAttributeFilter, isObjRef, isRelativeBoundedDateFilter, isRelativeDateFilter, measureValueFilterConditions, measureValueFilterMeasure, newAbsoluteDashboardDateFilter, newAllTimeDashboardDateFilter, newRelativeDashboardDateFilter, } from "@gooddata/sdk-model";
5
+ export function dashboardFilterToFilterContextItem(filter, keepDatasets) {
6
+ if (isAttributeFilterWithSelection(filter)) {
7
+ return {
8
+ attributeFilter: {
9
+ negativeSelection: isNegativeAttributeFilter(filter),
10
+ displayForm: filterObjRef(filter),
11
+ attributeElements: filterAttributeElements(filter),
12
+ selectionMode: "multi",
13
+ localIdentifier: filterLocalIdentifier(filter),
14
+ },
15
+ };
16
+ }
17
+ else if (isArbitraryAttributeFilter(filter)) {
18
+ return {
19
+ arbitraryAttributeFilter: {
20
+ displayForm: filterObjRef(filter),
21
+ values: filter.arbitraryAttributeFilter.values,
22
+ negativeSelection: filter.arbitraryAttributeFilter.negativeSelection ?? false,
23
+ localIdentifier: filterLocalIdentifier(filter),
24
+ },
25
+ };
26
+ }
27
+ else if (isMatchAttributeFilter(filter)) {
28
+ return {
29
+ matchAttributeFilter: {
30
+ displayForm: filterObjRef(filter),
31
+ operator: filter.matchAttributeFilter.operator,
32
+ literal: filter.matchAttributeFilter.literal,
33
+ caseSensitive: filter.matchAttributeFilter.caseSensitive,
34
+ negativeSelection: filter.matchAttributeFilter.negativeSelection,
35
+ localIdentifier: filterLocalIdentifier(filter),
36
+ },
37
+ };
38
+ }
39
+ else if (isAbsoluteDateFilter(filter)) {
40
+ return newAbsoluteDashboardDateFilter(filter.absoluteDateFilter.from, filter.absoluteDateFilter.to, keepDatasets ? filter.absoluteDateFilter.dataSet : undefined, filter.absoluteDateFilter.localIdentifier, filter.absoluteDateFilter.emptyValueHandling);
41
+ }
42
+ else if (isAllTimeDateFilter(filter)) {
43
+ return newAllTimeDashboardDateFilter(keepDatasets ? filter.relativeDateFilter.dataSet : undefined, filter.relativeDateFilter.localIdentifier, filter.relativeDateFilter.emptyValueHandling);
44
+ }
45
+ else if (isRelativeDateFilter(filter)) {
46
+ return newRelativeDashboardDateFilter(filter.relativeDateFilter.granularity, filter.relativeDateFilter.from, filter.relativeDateFilter.to, keepDatasets ? filter.relativeDateFilter.dataSet : undefined, filter.relativeDateFilter.localIdentifier, isRelativeBoundedDateFilter(filter) ? filter.relativeDateFilter.boundedFilter : undefined, filter.relativeDateFilter.emptyValueHandling);
47
+ }
48
+ else if (isMeasureValueFilter(filter)) {
49
+ const measure = measureValueFilterMeasure(filter);
50
+ // Dashboard MVFs always reference the measure by ObjRef. Insight MVFs use LocalIdRef
51
+ // (bucket localId) and have no honest representation in the dashboard filter context —
52
+ // they reach this converter only because automation execution filters mix dashboard and
53
+ // insight filters. Caller is responsible for dropping these undefined entries.
54
+ if (!isObjRef(measure)) {
55
+ return undefined;
56
+ }
57
+ const conditions = measureValueFilterConditions(filter);
58
+ return {
59
+ dashboardMeasureValueFilter: {
60
+ measure,
61
+ localIdentifier: filter.measureValueFilter.localIdentifier ?? `mvf:${JSON.stringify(measure)}`,
62
+ ...(conditions ? { conditions } : {}),
63
+ },
64
+ };
65
+ }
66
+ throw new NotSupported(`Unsupported filter type! Please provide valid dashboard filter. Filter: ${JSON.stringify(filter)}`);
67
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"useDependentDateFilterConfigurationState.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/filterBar/attributeFilter/dashboardDropdownBody/configuration/hooks/useDependentDateFilterConfigurationState.ts"],"names":[],"mappings":"AAMA,OAAO,EACH,KAAK,+BAA+B,EACpC,KAAK,oBAAoB,EAE5B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,kDAAkD,CAAC;AAEtG;;GAEG;AACH,wBAAgB,wCAAwC,CACpD,eAAe,EAAE,oBAAoB,EAAE,EACvC,gBAAgB,CAAC,EAAE,oBAAoB,EACvC,oBAAoB,CAAC,EAAE,+BAA+B,EAAE,mCAoD3D;AAED,wBAAgB,8CAA8C,CAC1D,gBAAgB,CAAC,EAAE,oBAAoB,GACxC,oBAAoB,CAWtB"}
1
+ {"version":3,"file":"useDependentDateFilterConfigurationState.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/filterBar/attributeFilter/dashboardDropdownBody/configuration/hooks/useDependentDateFilterConfigurationState.ts"],"names":[],"mappings":"AAMA,OAAO,EACH,KAAK,+BAA+B,EACpC,KAAK,oBAAoB,EAG5B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,kDAAkD,CAAC;AAEtG;;GAEG;AACH,wBAAgB,wCAAwC,CACpD,eAAe,EAAE,oBAAoB,EAAE,EACvC,gBAAgB,CAAC,EAAE,oBAAoB,EACvC,oBAAoB,CAAC,EAAE,+BAA+B,EAAE,mCAyE3D;AAED,wBAAgB,8CAA8C,CAC1D,gBAAgB,CAAC,EAAE,oBAAoB,GACxC,oBAAoB,CAWtB"}
@@ -8,12 +8,22 @@ import { isUriRef, } from "@gooddata/sdk-model";
8
8
  export function useDependentDateFilterConfigurationState(neighborFilters, commonDateFilter, filterElementsByDate) {
9
9
  const neighborDateFiltersWithDimensions = useMemo(() => {
10
10
  return neighborFilters.map((neighborFilter) => {
11
- const neighborLocalId = neighborFilter.dateFilter.dataSet;
12
- invariant(!isUriRef(neighborLocalId));
13
- const isSelected = filterElementsByDate?.some((by) => by.filterLocalIdentifier === neighborLocalId?.identifier && !by.isCommonDate) || false;
14
- invariant(neighborLocalId?.identifier, "Cannot initialize the attribute filter configuration panel, neighbor date filter has missing 'localIdentifier' property.");
11
+ const neighborDataSet = neighborFilter.dateFilter.dataSet;
12
+ invariant(!isUriRef(neighborDataSet));
13
+ // Prefer the filter's own localIdentifier over the dataset identifier so that multiple date
14
+ // filters backed by the same dataset can be told apart. Falls back to the dataset identifier
15
+ // for date filters that don't carry a localIdentifier (e.g. older backend records).
16
+ const filterLocalIdentifier = neighborFilter.dateFilter.localIdentifier ?? neighborDataSet?.identifier;
17
+ const isSelected = filterElementsByDate?.some((by) =>
18
+ // Match against both the new (localIdentifier) and legacy (dataset identifier)
19
+ // formats: dashboards saved before this change store the dataset identifier in
20
+ // filterLocalIdentifier, so we must accept either form to stay backwards compatible.
21
+ (by.filterLocalIdentifier === filterLocalIdentifier ||
22
+ by.filterLocalIdentifier === neighborDataSet?.identifier) &&
23
+ !by.isCommonDate) || false;
24
+ invariant(filterLocalIdentifier, "Cannot initialize the attribute filter configuration panel, neighbor date filter has missing 'localIdentifier' property.");
15
25
  return {
16
- localIdentifier: neighborLocalId.identifier,
26
+ localIdentifier: filterLocalIdentifier,
17
27
  isSelected,
18
28
  dataSet: neighborFilter.dateFilter.dataSet,
19
29
  from: neighborFilter.dateFilter.from,
@@ -28,19 +38,27 @@ export function useDependentDateFilterConfigurationState(neighborFilters, common
28
38
  const commonDate = parseCommonDateFilter(commonDateFilter);
29
39
  return (filterElementsByDate ?? [])
30
40
  .filter((parent) => parent.isCommonDate)
31
- .map((parent) => ({
32
- localIdentifier: parent.filterLocalIdentifier,
33
- isSelected: true,
34
- dataSet: {
41
+ .map((parent) => {
42
+ // Resolve the target dimension. New format: the explicit `dataSet` field carries it
43
+ // (filterLocalIdentifier references the common date filter itself). Legacy format: no
44
+ // `dataSet`, so the dimension identifier was stored directly in filterLocalIdentifier.
45
+ // The config item is keyed by the dimension so it matches the per-dimension picker items.
46
+ const dimension = parent.dataSet ?? {
35
47
  identifier: parent.filterLocalIdentifier,
36
48
  type: "dataSet",
37
- },
38
- from: commonDate.from,
39
- to: commonDate.to,
40
- granularity: commonDate.granularity,
41
- type: commonDate.type,
42
- isCommonDate: true,
43
- }));
49
+ };
50
+ invariant(!isUriRef(dimension));
51
+ return {
52
+ localIdentifier: dimension.identifier,
53
+ isSelected: true,
54
+ dataSet: dimension,
55
+ from: commonDate.from,
56
+ to: commonDate.to,
57
+ granularity: commonDate.granularity,
58
+ type: commonDate.type,
59
+ isCommonDate: true,
60
+ };
61
+ });
44
62
  }, [filterElementsByDate, commonDateFilter]);
45
63
  return [...neighborDateFiltersWithDimensions, ...commonDateFilters];
46
64
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useDependentDateFiltersConfiguration.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/filterBar/attributeFilter/dashboardDropdownBody/configuration/hooks/useDependentDateFiltersConfiguration.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,yBAAyB,EAAE,KAAK,oBAAoB,EAAY,MAAM,qBAAqB,CAAC;AAI1G,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,kDAAkD,CAAC;AAOtG,wBAAgB,oCAAoC,CAChD,mBAAmB,EAAE,oBAAoB,EAAE,EAC3C,aAAa,EAAE,yBAAyB,EACxC,gBAAgB,CAAC,EAAE,oBAAoB;;;;;;;EAmG1C"}
1
+ {"version":3,"file":"useDependentDateFiltersConfiguration.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/filterBar/attributeFilter/dashboardDropdownBody/configuration/hooks/useDependentDateFiltersConfiguration.ts"],"names":[],"mappings":"AAQA,OAAO,EACH,KAAK,yBAAyB,EAE9B,KAAK,oBAAoB,EAE5B,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,kDAAkD,CAAC;AAOtG,wBAAgB,oCAAoC,CAChD,mBAAmB,EAAE,oBAAoB,EAAE,EAC3C,aAAa,EAAE,yBAAyB,EACxC,gBAAgB,CAAC,EAAE,oBAAoB;;;;;;;EAqH1C"}
@@ -2,7 +2,8 @@
2
2
  import { useCallback, useMemo, useState } from "react";
3
3
  import { isEqual } from "lodash-es";
4
4
  import { invariant } from "ts-invariant";
5
- import { isUriRef } from "@gooddata/sdk-model";
5
+ import { generateDateFilterLocalIdentifier } from "@gooddata/sdk-backend-base";
6
+ import { isUriRef, } from "@gooddata/sdk-model";
6
7
  import { setAttributeFilterDependentDateFilters } from "../../../../../../model/commands/filters.js";
7
8
  import { useDispatchDashboardCommand } from "../../../../../../model/react/useDispatchDashboardCommand.js";
8
9
  import { useDependentCommonDateFilterConfigurationState, useDependentDateFilterConfigurationState, } from "./useDependentDateFilterConfigurationState.js";
@@ -15,9 +16,11 @@ export function useDependentDateFiltersConfiguration(neighborDateFilters, curren
15
16
  const [dependentDateFilters, setDependentDateFilters] = useState(originalState);
16
17
  const [dependentCommonDateFilter, setDependentCommonDateFilter] = useState(originalCommonDateFilterState);
17
18
  const onDependentDateFiltersSelect = (item, isSelected, isCommonDate) => {
18
- const localIdentifier = item.dataSet;
19
- invariant(!isUriRef(localIdentifier));
20
- const changedParentIndex = dependentDateFilters.findIndex((dependentDateFilter) => dependentDateFilter.localIdentifier === localIdentifier?.identifier &&
19
+ invariant(!isUriRef(item.dataSet));
20
+ // Match on item.localIdentifier rather than item.dataSet?.identifier (the dataset identifier):
21
+ // state entries now store dateFilter.localIdentifier as their key, which matches item.localIdentifier
22
+ // since both come from the same useDependentDateFilterConfigurationState initializer.
23
+ const changedParentIndex = dependentDateFilters.findIndex((dependentDateFilter) => dependentDateFilter.localIdentifier === item.localIdentifier &&
21
24
  dependentDateFilter.isCommonDate === isCommonDate);
22
25
  if (changedParentIndex === -1) {
23
26
  setDependentDateFilters([...dependentDateFilters, item]);
@@ -44,18 +47,30 @@ export function useDependentDateFiltersConfiguration(neighborDateFilters, curren
44
47
  const onDependentDateFiltersChange = useCallback(() => {
45
48
  // dispatch the command only if the configuration changed
46
49
  if (onDependentDateFiltersConfigurationChanged) {
50
+ // Common date filter has no dimension of its own, so we reference it by its own
51
+ // localIdentifier (falling back to the canonical common-date id) and carry the chosen
52
+ // dimension in the separate `dataSet` field. Specific date filters are referenced by their
53
+ // localIdentifier and need no dataSet (it is derived from the referenced filter).
54
+ const commonDateLocalId = commonDateFilter?.dateFilter.localIdentifier ?? generateDateFilterLocalIdentifier(0);
47
55
  const dateFilters = dependentDateFilters
48
56
  .filter((filter) => filter.isSelected)
49
- .map((filter) => ({
50
- filterLocalIdentifier: filter.localIdentifier,
51
- isCommonDate: filter.isCommonDate,
52
- }));
57
+ .map((filter) => filter.isCommonDate
58
+ ? {
59
+ filterLocalIdentifier: commonDateLocalId,
60
+ isCommonDate: true,
61
+ dataSet: filter.dataSet,
62
+ }
63
+ : {
64
+ filterLocalIdentifier: filter.localIdentifier,
65
+ isCommonDate: false,
66
+ });
53
67
  saveDependentDateFilterCommand(currentFilter.attributeFilter.localIdentifier, dateFilters);
54
68
  }
55
69
  }, [
56
70
  dependentDateFilters,
57
71
  onDependentDateFiltersConfigurationChanged,
58
72
  currentFilter,
73
+ commonDateFilter,
59
74
  saveDependentDateFilterCommand,
60
75
  ]);
61
76
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"useDependentDateFilters.d.ts","sourceRoot":"","sources":["../../../../src/presentation/filterBar/attributeFilter/useDependentDateFilters.ts"],"names":[],"mappings":"AAMA,OAAO,EACH,KAAK,4BAA4B,EAKpC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAe1E;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,yBAAyB,EAAE,sBAAsB,CAAC,CAAC;AAE7F;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,8FAqEnC,CAAC"}
1
+ {"version":3,"file":"useDependentDateFilters.d.ts","sourceRoot":"","sources":["../../../../src/presentation/filterBar/attributeFilter/useDependentDateFilters.ts"],"names":[],"mappings":"AAMA,OAAO,EACH,KAAK,4BAA4B,EAMpC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAe1E;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,yBAAyB,EAAE,sBAAsB,CAAC,CAAC;AAE7F;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,8FAiFnC,CAAC"}
@@ -34,23 +34,34 @@ export const useDependentDateFilters = (filter, tabId) => {
34
34
  const dependentDateFilters = useMemo(() => {
35
35
  return filterElementsByDate?.map((dependentDateFilter) => {
36
36
  if (dependentDateFilter.isCommonDate) {
37
+ // The dimension to apply the common date range through.
38
+ // New format: the explicit `dataSet` field (filterLocalIdentifier references the common
39
+ // date filter itself). Legacy format: no `dataSet` field, so the dataset identifier was
40
+ // stored directly in filterLocalIdentifier.
41
+ const dataSet = dependentDateFilter.dataSet ?? {
42
+ identifier: dependentDateFilter.filterLocalIdentifier,
43
+ type: "dataSet",
44
+ };
37
45
  const commonDashboardDateFilter = {
38
46
  dateFilter: {
39
47
  type: commonDateFilterWithAllTime?.dateFilter.type,
40
48
  granularity: commonDateFilterWithAllTime?.dateFilter.granularity,
41
49
  from: commonDateFilterWithAllTime?.dateFilter.from,
42
50
  to: commonDateFilterWithAllTime?.dateFilter.to,
43
- dataSet: {
44
- identifier: dependentDateFilter.filterLocalIdentifier,
45
- type: "dataSet",
46
- },
51
+ dataSet,
47
52
  },
48
53
  };
49
54
  return commonDashboardDateFilter;
50
55
  }
51
56
  else {
52
- const matchingFilter = allDateFilters.find((filter) => objRefToString(filter.dateFilter.dataSet) ===
53
- dependentDateFilter.filterLocalIdentifier);
57
+ // Try localIdentifier first (new format), then fall back to dataset identifier
58
+ // (legacy format) for dashboards saved before the localIdentifier-based approach.
59
+ // NOTE: If a filter's localIdentifier happens to equal another filter's dataset
60
+ // identifier the wrong filter could be matched — this is an accepted theoretical
61
+ // risk given the practical namespace separation between the two.
62
+ const matchingFilter = allDateFilters.find((filter) => filter.dateFilter.localIdentifier === dependentDateFilter.filterLocalIdentifier ||
63
+ objRefToString(filter.dateFilter.dataSet) ===
64
+ dependentDateFilter.filterLocalIdentifier);
54
65
  invariant(matchingFilter); // if this blows up, the state is inconsistent
55
66
  return matchingFilter;
56
67
  }
@@ -236,5 +236,5 @@ function MenuItem({ menuItem, selectedMenuItem, setMenuItemRef, setSelectedMenuI
236
236
  });
237
237
  }
238
238
  function renderMenuItemWarning(info, suppressHover) {
239
- return (_jsx("span", { className: "gd-list-icon gd-list-icon-right", children: _jsx(UiTooltip, { triggerBy: ["hover"], disabled: suppressHover, arrowPlacement: "left", optimalPlacement: true, content: info, anchor: _jsx(UiIcon, { type: "warning", size: 16, color: "warning", accessibilityConfig: { ariaHidden: true } }) }) }));
239
+ return (_jsx("span", { className: "gd-list-icon gd-list-icon-right", "data-testid": "menu-item-info-icon", children: _jsx(UiTooltip, { triggerBy: ["hover"], disabled: suppressHover, arrowPlacement: "left", optimalPlacement: true, content: info, anchor: _jsx(UiIcon, { type: "warning", size: 16, color: "warning", accessibilityConfig: { ariaHidden: true } }) }) }));
240
240
  }