@gooddata/sdk-ui-dashboard 11.42.0-alpha.2 → 11.42.0-alpha.3
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.
- package/NOTICE +6 -6
- package/esm/__version.d.ts +1 -1
- package/esm/__version.js +1 -1
- package/esm/_staging/automation/index.d.ts +14 -1
- package/esm/_staging/automation/index.d.ts.map +1 -1
- package/esm/_staging/automation/index.js +50 -0
- package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/DefaultScheduledEmailDialog.d.ts.map +1 -1
- package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/DefaultScheduledEmailDialog.js +16 -2
- package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/hooks/useEditScheduledEmail.d.ts +2 -1
- package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/hooks/useEditScheduledEmail.d.ts.map +1 -1
- package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/hooks/useEditScheduledEmail.js +32 -12
- package/esm/presentation/automations/shared/automationFilters/automationParameters.d.ts +39 -1
- package/esm/presentation/automations/shared/automationFilters/automationParameters.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/automationParameters.js +66 -1
- package/esm/presentation/automations/shared/automationFilters/components/AutomationFiltersSelect.d.ts +27 -3
- package/esm/presentation/automations/shared/automationFilters/components/AutomationFiltersSelect.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/components/AutomationFiltersSelect.js +44 -12
- package/esm/presentation/automations/shared/automationFilters/components/AutomationParameter.d.ts +2 -1
- package/esm/presentation/automations/shared/automationFilters/components/AutomationParameter.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/components/AutomationParameter.js +7 -1
- package/esm/presentation/automations/shared/automationFilters/hooks/useValidateExistingAutomationFilters.d.ts +23 -1
- package/esm/presentation/automations/shared/automationFilters/hooks/useValidateExistingAutomationFilters.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/hooks/useValidateExistingAutomationFilters.js +93 -2
- package/esm/presentation/automations/shared/automationFilters/useAutomationExportParameters.d.ts +75 -0
- package/esm/presentation/automations/shared/automationFilters/useAutomationExportParameters.d.ts.map +1 -0
- package/esm/presentation/automations/shared/automationFilters/useAutomationExportParameters.js +143 -0
- package/esm/presentation/automations/shared/automationFilters/useAutomationFilters.d.ts +1 -0
- package/esm/presentation/automations/shared/automationFilters/useAutomationFilters.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/useAutomationFilters.js +1 -0
- package/esm/presentation/automations/shared/automationFilters/useAutomationFiltersSelect.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/useAutomationFiltersSelect.js +15 -2
- package/esm/presentation/automations/shared/automationFilters/useParameterAnnouncements.d.ts +15 -0
- package/esm/presentation/automations/shared/automationFilters/useParameterAnnouncements.d.ts.map +1 -0
- package/esm/presentation/automations/shared/automationFilters/useParameterAnnouncements.js +28 -0
- package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts +16 -0
- package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/en-US.localization-bundle.js +16 -0
- package/esm/presentation/widget/widget/InsightWidget/EditableDashboardInsightWidget.d.ts.map +1 -1
- package/esm/presentation/widget/widget/InsightWidget/EditableDashboardInsightWidget.js +10 -1
- package/esm/presentation/widget/widget/warningPartialResult/InsightWidgetWarningPartialResult.d.ts +5 -4
- package/esm/presentation/widget/widget/warningPartialResult/InsightWidgetWarningPartialResult.d.ts.map +1 -1
- package/esm/presentation/widget/widget/warningPartialResult/InsightWidgetWarningPartialResult.js +3 -3
- package/package.json +20 -20
- package/styles/css/main.css +1 -1
- package/styles/css/warningPartialResult.css +1 -1
- package/styles/scss/warningPartialResult.scss +1 -1
package/esm/presentation/automations/shared/automationFilters/components/AutomationFiltersSelect.js
CHANGED
|
@@ -7,18 +7,21 @@ import { Bubble, BubbleHoverTrigger, Typography, UiButton, UiIconButton, UiToolt
|
|
|
7
7
|
import { AUTOMATION_FILTERS_DIALOG_ID, AUTOMATION_FILTERS_DIALOG_TITLE_ID, AUTOMATION_FILTERS_GROUP_LABEL_ID, } from "../../../../constants/automations.js";
|
|
8
8
|
import { AttributesDropdown, } from "../../../../filterBar/attributeFilter/addAttributeFilter/AttributesDropdown.js";
|
|
9
9
|
import { useAutomationFilters, useAutomationFiltersByTab } from "../useAutomationFilters.js";
|
|
10
|
+
import { useParameterAnnouncements } from "../useParameterAnnouncements.js";
|
|
10
11
|
import { AutomationAttributeFilter } from "./AutomationAttributeFilter.js";
|
|
11
12
|
import { AutomationDateFilter } from "./AutomationDateFilter.js";
|
|
12
13
|
import { AutomationMeasureValueFilter } from "./AutomationMeasureValueFilter.js";
|
|
13
14
|
import { AutomationParameter } from "./AutomationParameter.js";
|
|
14
15
|
const COLLAPSED_FILTERS_COUNT = 2;
|
|
15
|
-
function AutomationCheckboxOrNote({ isDashboardAutomation, storeFilters, handleStoreFiltersChange, handleKeyDown, automationFilterSelectTooltipId, }) {
|
|
16
|
+
function AutomationCheckboxOrNote({ isDashboardAutomation, storeFilters, handleStoreFiltersChange, handleKeyDown, automationFilterSelectTooltipId, parametersEnabled, }) {
|
|
16
17
|
const intl = useIntl();
|
|
18
|
+
// Keep both message ids as static literals so the i18n extractor sees them.
|
|
19
|
+
const useFiltersTooltip = parametersEnabled ? (_jsx(FormattedMessage, { id: "dialogs.automation.filters.useFiltersMessage.parameters.tooltip" })) : (_jsx(FormattedMessage, { id: "dialogs.automation.filters.useFiltersMessage.tooltip" }));
|
|
17
20
|
return isDashboardAutomation ? (_jsxs("label", { className: "input-checkbox-label gd-automation-filters__use-filters-checkbox s-automation-filters-use-filters-checkbox", children: [
|
|
18
21
|
_jsx("input", { type: "checkbox", className: "input-checkbox s-checkbox", checked: storeFilters, onChange: (e) => handleStoreFiltersChange(e.target.checked), onKeyDown: handleKeyDown, "aria-label": intl.formatMessage({
|
|
19
22
|
id: "dialogs.automation.filters.useFiltersMessage",
|
|
20
23
|
}) }), _jsxs("span", { className: "input-label-text gd-automation-filters__use-filters-message", children: [
|
|
21
|
-
_jsx("div", { id: automationFilterSelectTooltipId, className: "sr-only", children:
|
|
24
|
+
_jsx("div", { id: automationFilterSelectTooltipId, className: "sr-only", children: useFiltersTooltip }), _jsx(FormattedMessage, { id: "dialogs.automation.filters.useFiltersMessage" }), _jsx(UiTooltip, { arrowPlacement: "left", triggerBy: ["hover", "focus"], optimalPlacement: true, width: 300, content: useFiltersTooltip, anchor: _jsx(UiIconButton, { icon: "question", variant: "tertiary", size: "xsmall", accessibilityConfig: {
|
|
22
25
|
ariaLabel: intl.formatMessage({
|
|
23
26
|
id: "dialogs.automation.filters.schedule.ariaLabel",
|
|
24
27
|
}),
|
|
@@ -30,7 +33,7 @@ function AutomationCheckboxOrNote({ isDashboardAutomation, storeFilters, handleS
|
|
|
30
33
|
function Divider() {
|
|
31
34
|
return _jsx("div", { className: "gd-automation-filters__divider", style: { height: "20px" } });
|
|
32
35
|
}
|
|
33
|
-
export function AutomationFiltersSelect({ availableFilters = [], selectedFilters = [], onFiltersChange, isDashboardAutomation, storeFilters, onStoreFiltersChange, areFiltersMissing, overlayPositionType, hideTitle = false, showAllFilters = false, disableDateFilters = false, filtersByTab, editedFiltersByTab, onFiltersByTabChange, parameters = [], onParameterChange, onParameterDelete,
|
|
36
|
+
export function AutomationFiltersSelect({ availableFilters = [], selectedFilters = [], onFiltersChange, isDashboardAutomation, storeFilters, onStoreFiltersChange, areFiltersMissing, overlayPositionType, hideTitle = false, showAllFilters = false, disableDateFilters = false, filtersByTab, editedFiltersByTab, onFiltersByTabChange, parameters = [], availableParameters = [], onParameterAdd, onParameterChange, onParameterDelete, parametersByTab, availableParametersByTab, onParameterAddByTab, onParameterChangeByTab, onParameterDeleteByTab, parametersEnabled, }) {
|
|
34
37
|
// Determine rendering mode first
|
|
35
38
|
const shouldRenderByTab = !!filtersByTab && filtersByTab.length > 1;
|
|
36
39
|
// Use appropriate hook based on rendering mode
|
|
@@ -50,6 +53,7 @@ export function AutomationFiltersSelect({ availableFilters = [], selectedFilters
|
|
|
50
53
|
});
|
|
51
54
|
// Use data from the appropriate hook based on rendering mode
|
|
52
55
|
const { commonDateFilterId, lockedFilters, attributeConfigs, dateConfigs, filterAnnouncement, filterGroupRef, makeFilterGroupUnfocusable, setAddFilterButtonRefs, } = shouldRenderByTab && tabFiltersData.processedFiltersByTab ? tabFiltersData : flatFiltersData;
|
|
56
|
+
const { focusAddFilterButton } = flatFiltersData;
|
|
53
57
|
const filters = shouldRenderByTab ? [] : flatFiltersData.visibleFilters;
|
|
54
58
|
const visibleParameters = shouldRenderByTab ? [] : parameters;
|
|
55
59
|
const attributes = shouldRenderByTab ? [] : flatFiltersData.attributes;
|
|
@@ -90,6 +94,8 @@ export function AutomationFiltersSelect({ availableFilters = [], selectedFilters
|
|
|
90
94
|
};
|
|
91
95
|
const tooltipText = isAddButtonDisabled ? tooltipTextValues.addDisabled : tooltipTextValues.add;
|
|
92
96
|
const searchAriaLabel = intl.formatMessage({ id: "dialogs.automation.filters.searchAriaLabel" });
|
|
97
|
+
// Screen-reader announcements for parameter chip add/remove/change
|
|
98
|
+
const { parameterAnnouncement, announceParameterAdded, announceParameterChanged, announceParameterRemoved, } = useParameterAnnouncements();
|
|
93
99
|
const disableFilters = !storeFilters && !!isDashboardAutomation;
|
|
94
100
|
return (_jsxs("div", { className: "gd-input-component gd-notification-channels-automation-filters s-gd-notifications-channels-dialog-automation-filters", children: [hideTitle ? (_jsx("div", { className: "sr-only", id: AUTOMATION_FILTERS_GROUP_LABEL_ID, children: _jsx(FormattedMessage, { id: "dialogs.schedule.email.filters", values: { count: chipCount } }) })) : (_jsx("div", { className: "gd-label", id: AUTOMATION_FILTERS_GROUP_LABEL_ID, children: isExpandable && !shouldRenderByTab ? (_jsxs(BubbleHoverTrigger, { showDelay: 500, hideDelay: 0, children: [
|
|
95
101
|
_jsx(UiButton, { label: intl.formatMessage({ id: "dialogs.schedule.email.filters" }, { count: chipCount }), variant: "tertiary", onClick: () => setIsExpanded(!isExpanded), iconAfter: isExpanded ? "navigateUp" : "navigateDown", accessibilityConfig: {
|
|
@@ -104,21 +110,38 @@ export function AutomationFiltersSelect({ availableFilters = [], selectedFilters
|
|
|
104
110
|
iconAriaHidden: true,
|
|
105
111
|
} }), _jsx(Bubble, { className: "bubble-primary", alignPoints: [{ align: "bc tc" }], children: isExpanded ? (_jsx(FormattedMessage, { id: "dialogs.automation.filters.showLess" })) : (_jsx(FormattedMessage, { id: "dialogs.automation.filters.showAll" })) })
|
|
106
112
|
] })) : (_jsx(FormattedMessage, { id: "dialogs.schedule.email.filters", values: { count: chipCount } })) })), _jsxs("div", { className: "gd-automation-filters", children: [showAllFilters ? (_jsxs(_Fragment, { children: [
|
|
107
|
-
_jsx(AutomationCheckboxOrNote, { isDashboardAutomation: isDashboardAutomation, storeFilters: storeFilters, handleStoreFiltersChange: handleStoreFiltersChange, handleKeyDown: handleKeyDown, automationFilterSelectTooltipId: automationFilterSelectTooltipId }), _jsx(Divider, {})
|
|
113
|
+
_jsx(AutomationCheckboxOrNote, { isDashboardAutomation: isDashboardAutomation, storeFilters: storeFilters, handleStoreFiltersChange: handleStoreFiltersChange, handleKeyDown: handleKeyDown, automationFilterSelectTooltipId: automationFilterSelectTooltipId, parametersEnabled: parametersEnabled }), _jsx(Divider, {})
|
|
108
114
|
] })) : null, _jsxs("div", { className: `gd-automation-filters__wrapper${storeFilters || !isDashboardAutomation ? "" : " gd-automation-filters__wrapper--disabled"}`, children: [disableFilters ? _jsx("div", { className: "gd-automation-filters__overlay" }) : null, shouldRenderByTab && tabFiltersData.processedFiltersByTab ? (
|
|
109
115
|
// Render filters grouped by tab with dividers between sections
|
|
110
116
|
_jsx("div", { className: "gd-automation-filters__list gd-automation-filters__list--tabbed", role: "group", "aria-labelledby": AUTOMATION_FILTERS_GROUP_LABEL_ID, ref: filterGroupRef, onBlur: makeFilterGroupUnfocusable, children: tabFiltersData.processedFiltersByTab.map((tab, index) => {
|
|
111
117
|
// Check if all filters for this tab are already selected
|
|
112
118
|
const tabEditedFilters = editedFiltersByTab?.[tab.tabId] ?? [];
|
|
113
119
|
const tabAvailableFilters = filtersByTab?.find((t) => t.tabId === tab.tabId)?.availableFilters ?? [];
|
|
114
|
-
const
|
|
120
|
+
const tabParameters = parametersByTab?.[tab.tabId] ?? [];
|
|
121
|
+
const tabAvailableParameters = availableParametersByTab?.[tab.tabId] ?? [];
|
|
122
|
+
const tabParameterDropdownItems = tabAvailableParameters.map((parameter) => ({
|
|
123
|
+
type: "parameter",
|
|
124
|
+
ref: parameter.ref,
|
|
125
|
+
title: parameter.title,
|
|
126
|
+
}));
|
|
127
|
+
const isTabAddButtonDisabled = tabEditedFilters.length >= tabAvailableFilters.length &&
|
|
128
|
+
tabAvailableParameters.length === 0;
|
|
115
129
|
const tabTooltipText = isTabAddButtonDisabled
|
|
116
130
|
? tooltipTextValues.addDisabled
|
|
117
131
|
: tooltipTextValues.add;
|
|
118
|
-
return (_jsx(AutomationFiltersTabSection, { tabTitle: tab.tabTitle, tabId: tab.tabId, filters: tab.visibleFilters, attributeConfigs: tab.attributeConfigs, commonDateFilterId: commonDateFilterId, lockedFilters: tab.lockedFilters, onChange: (filter) => tabFiltersData.handleTabFilterChange(tab.tabId, filter), onDelete: (filter) => tabFiltersData.handleTabFilterDelete(tab.tabId, filter),
|
|
132
|
+
return (_jsx(AutomationFiltersTabSection, { tabTitle: tab.tabTitle, tabId: tab.tabId, canAddItems: !isTabAddButtonDisabled, filters: tab.visibleFilters, attributeConfigs: tab.attributeConfigs, commonDateFilterId: commonDateFilterId, lockedFilters: tab.lockedFilters, onChange: (filter) => tabFiltersData.handleTabFilterChange(tab.tabId, filter), onDelete: (filter) => tabFiltersData.handleTabFilterDelete(tab.tabId, filter), parameters: tabParameters, onParameterChange: (ref, value) => {
|
|
133
|
+
announceParameterChanged(tabParameters, ref, value);
|
|
134
|
+
onParameterChangeByTab?.(tab.tabId, ref, value);
|
|
135
|
+
}, onParameterDelete: (ref) => {
|
|
136
|
+
announceParameterRemoved(tabParameters, ref);
|
|
137
|
+
onParameterDeleteByTab?.(tab.tabId, ref);
|
|
138
|
+
}, overlayPositionType: overlayPositionType, showDivider: index < tabFiltersData.processedFiltersByTab.length - 1, readonlyFilters: disableFilters,
|
|
119
139
|
// Add button for each tab section
|
|
120
140
|
addFilterButton: _jsx(AttributesDropdown, { id: `${AUTOMATION_FILTERS_DIALOG_ID}-${tab.tabId}`, onClose: () => { }, onSelect: (value) => {
|
|
121
141
|
tabFiltersData.handleTabFilterAdd(tab.tabId, value, tab.attributes, tab.dateDatasets);
|
|
142
|
+
}, parameters: tabParameterDropdownItems, onParameterSelect: (ref) => {
|
|
143
|
+
announceParameterAdded(tabAvailableParameters, ref);
|
|
144
|
+
onParameterAddByTab?.(tab.tabId, ref);
|
|
122
145
|
}, attributes: tab.attributes, dateDatasets: tab.dateDatasets, measures: tab.measures, openOnInit: false, overlayPositionType: overlayPositionType, className: "gd-automation-filters__dropdown s-automation-filters-add-filter-dropdown", getCustomItemTitle: (item) => getCatalogItemCustomTitle(item, availableFilters, tab.dateConfigs), accessibilityConfig: {
|
|
123
146
|
ariaLabelledBy: AUTOMATION_FILTERS_DIALOG_TITLE_ID,
|
|
124
147
|
searchAriaLabel: searchAriaLabel,
|
|
@@ -143,13 +166,22 @@ export function AutomationFiltersSelect({ availableFilters = [], selectedFilters
|
|
|
143
166
|
commonDateFilterId === filter.dateFilter.localIdentifier);
|
|
144
167
|
return (_jsx(AutomationFilter, { filter: filter, attributeConfigs: attributeConfigs, onChange: handleChangeFilter, onDelete: handleDeleteFilter, isCommonDateFilter: isCommonDateFilter, overlayPositionType: overlayPositionType, lockedFilters: lockedFilters, isReadOnly: disableFilters }, dashboardFilterLocalIdentifier(filter)));
|
|
145
168
|
}),
|
|
146
|
-
...visibleParameters.map((parameter) => (_jsx(AutomationParameter, { parameter: parameter, onChange:
|
|
169
|
+
...visibleParameters.map((parameter) => (_jsx(AutomationParameter, { parameter: parameter, onChange: (ref, value) => {
|
|
170
|
+
announceParameterChanged(visibleParameters, ref, value);
|
|
171
|
+
onParameterChange?.(ref, value);
|
|
172
|
+
}, onDelete: (ref) => {
|
|
173
|
+
announceParameterRemoved(visibleParameters, ref);
|
|
174
|
+
onParameterDelete?.(ref);
|
|
175
|
+
}, overlayPositionType: overlayPositionType, isReadOnly: disableFilters }, `parameter-${parameter.ref.identifier}`))),
|
|
147
176
|
].slice(0, showAllFilters || isExpanded ? undefined : COLLAPSED_FILTERS_COUNT), isExpanded || !isExpandable ? (_jsx(AttributesDropdown, { id: AUTOMATION_FILTERS_DIALOG_ID, onClose: () => { }, onSelect: (value) => {
|
|
148
177
|
handleAddFilter(value, attributes, dateDatasets);
|
|
149
178
|
setIsExpanded(true);
|
|
150
179
|
}, parameters: parameterDropdownItems, onParameterSelect: (ref) => {
|
|
180
|
+
announceParameterAdded(availableParameters, ref);
|
|
151
181
|
onParameterAdd?.(ref);
|
|
152
182
|
setIsExpanded(true);
|
|
183
|
+
// Restore "+" focus, else it falls to <body> when the chip's list node unmounts.
|
|
184
|
+
setTimeout(focusAddFilterButton);
|
|
153
185
|
}, attributes: attributes, dateDatasets: dateDatasets, measures: measures, openOnInit: false, overlayPositionType: overlayPositionType, className: "gd-automation-filters__dropdown s-automation-filters-add-filter-dropdown", getCustomItemTitle: (item) => getCatalogItemCustomTitle(item, availableFilters, dateConfigs), accessibilityConfig: {
|
|
154
186
|
ariaLabelledBy: AUTOMATION_FILTERS_DIALOG_TITLE_ID,
|
|
155
187
|
searchAriaLabel: searchAriaLabel,
|
|
@@ -165,10 +197,10 @@ export function AutomationFiltersSelect({ availableFilters = [], selectedFilters
|
|
|
165
197
|
ariaExpanded: isOpen,
|
|
166
198
|
ariaHaspopup: "dialog",
|
|
167
199
|
} }) }) })), DropdownTitleComponent: () => (_jsx("div", { className: "gd-automation-filters__dropdown-header", children: _jsx(Typography, { tagName: "h3", id: AUTOMATION_FILTERS_DIALOG_TITLE_ID, children: _jsx(FormattedMessage, { id: "dialogs.automation.filters.title" }) }) })), renderNoData: () => (_jsx("div", { className: "gd-automation-filters__dropdown-no-filters", children: _jsx(FormattedMessage, { id: "dialogs.automation.filters.noFilters" }) })) })) : null] }))] }), showAllFilters ? null : (_jsxs(_Fragment, { children: [
|
|
168
|
-
_jsx(Divider, {}), _jsx(AutomationCheckboxOrNote, { isDashboardAutomation: isDashboardAutomation, storeFilters: storeFilters, handleStoreFiltersChange: handleStoreFiltersChange, handleKeyDown: handleKeyDown, automationFilterSelectTooltipId: automationFilterSelectTooltipId })
|
|
200
|
+
_jsx(Divider, {}), _jsx(AutomationCheckboxOrNote, { isDashboardAutomation: isDashboardAutomation, storeFilters: storeFilters, handleStoreFiltersChange: handleStoreFiltersChange, handleKeyDown: handleKeyDown, automationFilterSelectTooltipId: automationFilterSelectTooltipId, parametersEnabled: parametersEnabled })
|
|
169
201
|
] })), areFiltersMissing ? (_jsx("div", { className: "gd-automation-filters__warning-message", children: _jsx(FormattedMessage, { id: "dialogs.automation.filters.missing", values: {
|
|
170
202
|
b: (chunk) => _jsx("strong", { children: chunk }),
|
|
171
|
-
} }) })) : null] }), _jsx("div", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", role: "status", children: filterAnnouncement })
|
|
203
|
+
} }) })) : null] }), _jsx("div", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", role: "status", children: filterAnnouncement }), _jsx("div", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", role: "status", children: parameterAnnouncement })
|
|
172
204
|
] }));
|
|
173
205
|
}
|
|
174
206
|
function AutomationFilter({ filter, attributeConfigs, onChange, onDelete, isCommonDateFilter, overlayPositionType, lockedFilters, isReadOnly, tabId, }) {
|
|
@@ -194,13 +226,13 @@ function AutomationFilter({ filter, attributeConfigs, onChange, onDelete, isComm
|
|
|
194
226
|
* Renders filters for a single tab with a tab title header.
|
|
195
227
|
* Used when displaying filters grouped by tab for whole dashboard automations.
|
|
196
228
|
*/
|
|
197
|
-
function AutomationFiltersTabSection({ tabId, tabTitle, filters, attributeConfigs, commonDateFilterId, lockedFilters, onChange, onDelete, overlayPositionType, showDivider = false, addFilterButton, readonlyFilters, }) {
|
|
229
|
+
function AutomationFiltersTabSection({ tabId, tabTitle, filters, attributeConfigs, commonDateFilterId, lockedFilters, onChange, onDelete, parameters, onParameterChange, onParameterDelete, overlayPositionType, showDivider = false, addFilterButton, canAddItems, readonlyFilters, }) {
|
|
198
230
|
const intl = useIntl();
|
|
199
231
|
const tabSectionLabelId = useIdPrefixed(`automation-filters-tab-${tabId}`);
|
|
200
232
|
// Display tab title or fallback to generic "Tab" label
|
|
201
233
|
const displayTitle = tabTitle || intl.formatMessage({ id: "dialogs.automation.filters.tab.untitled" });
|
|
202
234
|
const tabLabel = intl.formatMessage({ id: "dialogs.automation.filters.tab.label" });
|
|
203
|
-
if (filters.length === 0) {
|
|
235
|
+
if (filters.length === 0 && parameters.length === 0 && !canAddItems) {
|
|
204
236
|
return null;
|
|
205
237
|
}
|
|
206
238
|
const content = (_jsx("span", { className: "gd-automation-filters__tab-title s-automation-filters-tab-title", children: displayTitle }));
|
|
@@ -213,7 +245,7 @@ function AutomationFiltersTabSection({ tabId, tabTitle, filters, attributeConfig
|
|
|
213
245
|
(isDashboardDateFilter(filter) &&
|
|
214
246
|
commonDateFilterId === filter.dateFilter.localIdentifier);
|
|
215
247
|
return (_jsx(AutomationFilter, { filter: filter, attributeConfigs: attributeConfigs, onChange: onChange, onDelete: onDelete, isCommonDateFilter: isCommonDateFilter, overlayPositionType: overlayPositionType, lockedFilters: lockedFilters, isReadOnly: readonlyFilters, tabId: tabId }, dashboardFilterLocalIdentifier(filter)));
|
|
216
|
-
}), addFilterButton] })
|
|
248
|
+
}), parameters.map((parameter) => (_jsx(AutomationParameter, { parameter: parameter, onChange: onParameterChange, onDelete: onParameterDelete, overlayPositionType: overlayPositionType, isReadOnly: readonlyFilters }, `parameter-${parameter.ref.identifier}`))), addFilterButton] })
|
|
217
249
|
] }), showDivider ? _jsx("div", { className: "gd-automation-filters__tab-divider" }) : null] }));
|
|
218
250
|
}
|
|
219
251
|
/**
|
package/esm/presentation/automations/shared/automationFilters/components/AutomationParameter.d.ts
CHANGED
|
@@ -10,11 +10,12 @@ export interface IAutomationParameterProps {
|
|
|
10
10
|
onChange?: (ref: IdentifierRef, value: number) => void;
|
|
11
11
|
onDelete?: (ref: IdentifierRef) => void;
|
|
12
12
|
overlayPositionType?: OverlayPositionType;
|
|
13
|
+
isReadOnly?: boolean;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Workspace parameter as an editable chip in automation dialogs.
|
|
16
17
|
*
|
|
17
18
|
* @internal
|
|
18
19
|
*/
|
|
19
|
-
export declare function AutomationParameter({ parameter, onChange, onDelete, overlayPositionType }: IAutomationParameterProps): ReactNode;
|
|
20
|
+
export declare function AutomationParameter({ parameter, onChange, onDelete, overlayPositionType, isReadOnly }: IAutomationParameterProps): ReactNode;
|
|
20
21
|
//# sourceMappingURL=AutomationParameter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutomationParameter.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/automations/shared/automationFilters/components/AutomationParameter.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAI3D,OAAO,EAAgC,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAEH,KAAK,mBAAmB,EAM3B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACtC,SAAS,EAAE,oBAAoB,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"AutomationParameter.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/automations/shared/automationFilters/components/AutomationParameter.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAI3D,OAAO,EAAgC,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAEH,KAAK,mBAAmB,EAM3B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACtC,SAAS,EAAE,oBAAoB,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAChC,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,mBAAmB,EACnB,UAAU,EACb,EAAE,yBAAyB,GAAG,SAAS,CAmFvC"}
|
package/esm/presentation/automations/shared/automationFilters/components/AutomationParameter.js
CHANGED
|
@@ -7,7 +7,7 @@ import { Dropdown, ParameterControlDropdown, UiChip, UiTooltip, isActionKey, use
|
|
|
7
7
|
*
|
|
8
8
|
* @internal
|
|
9
9
|
*/
|
|
10
|
-
export function AutomationParameter({ parameter, onChange, onDelete, overlayPositionType, }) {
|
|
10
|
+
export function AutomationParameter({ parameter, onChange, onDelete, overlayPositionType, isReadOnly, }) {
|
|
11
11
|
const intl = useIntl();
|
|
12
12
|
const { ref, title, value, mode, constraints } = parameter;
|
|
13
13
|
const testId = `automation-parameter-${ref.identifier}`;
|
|
@@ -15,6 +15,12 @@ export function AutomationParameter({ parameter, onChange, onDelete, overlayPosi
|
|
|
15
15
|
const lockedTooltip = intl.formatMessage({ id: "dialogs.automation.filters.lockedTooltip" });
|
|
16
16
|
const deleteAriaLabel = intl.formatMessage({ id: "dialogs.automation.filters.deleteAriaLabel" });
|
|
17
17
|
const tooltipId = useIdPrefixed("automation-parameter-tooltip");
|
|
18
|
+
if (isReadOnly) {
|
|
19
|
+
// `isDisabled` renders a real disabled button — unfocusable, so keyboard users can't edit or
|
|
20
|
+
// delete. No lock icon/tooltip: unlike author-`READONLY` this chip is only frozen because
|
|
21
|
+
// persistence is off, so it must not read as author-locked.
|
|
22
|
+
return (_jsx(UiChip, { label: label, iconBefore: "parameter", isDisabled: true, isExpandable: false, dataTestId: testId }));
|
|
23
|
+
}
|
|
18
24
|
if (mode === DashboardParameterModeValues.READONLY) {
|
|
19
25
|
return (_jsx(UiChip, { label: label, iconBefore: "parameter", isLocked: true, isExpandable: false, dataTestId: testId, renderChipContent: (content) => (_jsx(UiTooltip, { id: tooltipId, arrowPlacement: "top-start", content: lockedTooltip, triggerBy: ["hover", "focus"], anchor: content, anchorWrapperStyles: { display: "flex", width: "100%", height: "100%", minWidth: 0 } })) }));
|
|
20
26
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DashboardAttributeFilterSelectionType, type FilterContextItem, type IAutomationMetadataObject, type IAutomationVisibleFilter, type IFilter, type IInsight } from "@gooddata/sdk-model";
|
|
1
|
+
import { type DashboardAttributeFilterSelectionType, type FilterContextItem, type IAutomationMetadataObject, type IAutomationVisibleFilter, type IDashboardExportParameter, type IDashboardParameter, type IFilter, type IInsight, type IParameterMetadataObject } from "@gooddata/sdk-model";
|
|
2
2
|
import type { ExtendedDashboardWidget } from "../../../../../model/types/layoutTypes.js";
|
|
3
3
|
export interface IAutomationValidationResult {
|
|
4
4
|
isValid: boolean;
|
|
@@ -12,12 +12,34 @@ export interface IAutomationValidationResult {
|
|
|
12
12
|
visibleFilterIsMissingInSavedFilters: boolean;
|
|
13
13
|
visibleFiltersAreMissing: boolean;
|
|
14
14
|
incompatibleSelectionTypeIsAppliedInSavedFilters: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* A stored parameter override is stale: its ref left the catalog, its tab is gone, or a
|
|
17
|
+
* `readonly`/`hidden` parameter's pinned value drifted from the current dashboard.
|
|
18
|
+
*/
|
|
19
|
+
parametersAreStale?: boolean;
|
|
15
20
|
}
|
|
16
21
|
export declare function useValidateExistingAutomationFilters({ automationToEdit, widget, insight }: {
|
|
17
22
|
automationToEdit?: IAutomationMetadataObject;
|
|
18
23
|
widget?: ExtendedDashboardWidget;
|
|
19
24
|
insight?: IInsight;
|
|
20
25
|
}): IAutomationValidationResult;
|
|
26
|
+
/**
|
|
27
|
+
* Flags stale stored parameter overrides for an existing automation: a ref that left the workspace
|
|
28
|
+
* catalog, a tab that no longer exists, or a `readonly`/`hidden` parameter whose pinned value
|
|
29
|
+
* drifted from the current dashboard. `active` parameters are user-owned, so their drift is allowed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateExistingAutomationParameters({ storedParametersByTab, catalog, dashboardParametersByTab, existingTabIds, widgetTabId }: {
|
|
32
|
+
storedParametersByTab: Record<string, IDashboardExportParameter[]> | undefined;
|
|
33
|
+
catalog: IParameterMetadataObject[];
|
|
34
|
+
dashboardParametersByTab: Record<string, IDashboardParameter[]>;
|
|
35
|
+
existingTabIds: Set<string>;
|
|
36
|
+
/**
|
|
37
|
+
* For widget schedules, the widget's current tab. A stored override under any other tab is
|
|
38
|
+
* orphaned (the widget was moved) and flagged stale. Undefined for dashboard schedules and when
|
|
39
|
+
* the tab can't be resolved, where every existing tab is accepted.
|
|
40
|
+
*/
|
|
41
|
+
widgetTabId?: string;
|
|
42
|
+
}): boolean;
|
|
21
43
|
/**
|
|
22
44
|
* Validate existing automation filters against current dashboard filter context and optionally saved widget / insight.
|
|
23
45
|
* Check for inconsistencies, that could lead to unwanted results when editing existing automation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useValidateExistingAutomationFilters.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/automations/shared/automationFilters/hooks/useValidateExistingAutomationFilters.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,qCAAqC,
|
|
1
|
+
{"version":3,"file":"useValidateExistingAutomationFilters.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/automations/shared/automationFilters/hooks/useValidateExistingAutomationFilters.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,qCAAqC,EAE1C,KAAK,iBAAiB,EAEtB,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,mBAAmB,EACxB,KAAK,OAAO,EAEZ,KAAK,QAAQ,EACb,KAAK,wBAAwB,EAmBhC,MAAM,qBAAqB,CAAC;AA+B7B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AA+DzF,MAAM,WAAW,2BAA2B;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,mCAAmC,EAAE,OAAO,CAAC;IAC7C,0CAA0C,EAAE,OAAO,CAAC;IACpD,mCAAmC,EAAE,OAAO,CAAC;IAC7C,0CAA0C,EAAE,OAAO,CAAC;IACpD,oCAAoC,EAAE,OAAO,CAAC;IAC9C,oCAAoC,EAAE,OAAO,CAAC;IAC9C,8CAA8C,EAAE,OAAO,CAAC;IACxD,oCAAoC,EAAE,OAAO,CAAC;IAC9C,wBAAwB,EAAE,OAAO,CAAC;IAClC,gDAAgD,EAAE,OAAO,CAAC;IAC1D;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC;AAiBD,wBAAgB,oCAAoC,CAAC,EACjD,gBAAgB,EAChB,MAAM,EACN,OAAO,EACV,EAAE;IACC,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;IAC7C,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,OAAO,CAAC,EAAE,QAAQ,CAAC;CACtB,GAAG,2BAA2B,CAoD9B;AAoHD;;;;GAIG;AACH,wBAAgB,oCAAoC,CAAC,EACjD,qBAAqB,EACrB,OAAO,EACP,wBAAwB,EACxB,cAAc,EACd,WAAW,EACd,EAAE;IACC,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,EAAE,CAAC,GAAG,SAAS,CAAC;IAC/E,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAChE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAoCV;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iCAAiC,CAAC,EAC9C,sBAAsB,EACtB,6BAA6B,EAC7B,aAAa,EACb,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,MAAM,EACN,OAAO,EACP,gBAAgB,EACnB,EAAE;IACC,sBAAsB,EAAE,OAAO,EAAE,CAAC;IAClC,6BAA6B,EAAE,SAAS,GAAG,wBAAwB,EAAE,CAAC;IACtE,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,cAAc,EAAE,iBAAiB,EAAE,CAAC;IACpC,gBAAgB,EAAE,iBAAiB,EAAE,CAAC;IACtC,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,qCAAqC,GAAG,SAAS,CAAC,CAAC;CACrF,GAAG,2BAA2B,CAgE9B;AAED,MAAM,WAAW,4BAA4B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,iBAAiB,EAAE,CAAC;IACtC,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,aAAa,EAAE,iBAAiB,EAAE,CAAC;CACtC;AAED;;;GAGG;AACH,wBAAgB,uCAAuC,CAAC,EACpD,0BAA0B,EAC1B,kCAAkC,EAClC,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,EACxB,EAAE;IACC,0BAA0B,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAChE,kCAAkC,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,EAAE,CAAC,CAAC;IAC/E,sBAAsB,EAAE,4BAA4B,EAAE,CAAC;IACvD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,qCAAqC,GAAG,SAAS,CAAC,CAAC,CAAC;CAC1G,GAAG,2BAA2B,CAoE9B"}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
// (C) 2025-2026 GoodData Corporation
|
|
2
2
|
import { differenceBy, omit } from "lodash-es";
|
|
3
|
-
import { dashboardFilterLocalIdentifier, filterLocalIdentifier, isAllDashboardMeasureValueFilter, isAllValuesAttributeFilter, isAllValuesDashboardAttributeFilter, isAttributeFilter, isAttributeFilterWithSelection, isDashboardAttributeFilter, isDashboardCommonDateFilter, isDateFilter, isInsightWidget, isLocalIdRef, isNegativeAttributeFilter, isPositiveAttributeFilter, isRelativeDateFilter, isSingleSelectionFilter, } from "@gooddata/sdk-model";
|
|
4
|
-
import { getAutomationAlertFilters, getAutomationDashboardFilters, getAutomationDashboardFiltersByTab, getAutomationVisualizationFilters, } from "../../../../../_staging/automation/index.js";
|
|
3
|
+
import { DashboardParameterModeValues, dashboardFilterLocalIdentifier, filterLocalIdentifier, isAllDashboardMeasureValueFilter, isAllValuesAttributeFilter, isAllValuesDashboardAttributeFilter, isAttributeFilter, isAttributeFilterWithSelection, isDashboardAttributeFilter, isDashboardCommonDateFilter, isDateFilter, isInsightWidget, isLocalIdRef, isNegativeAttributeFilter, isNumberParameterDefinition, isPositiveAttributeFilter, isRelativeDateFilter, isSingleSelectionFilter, } from "@gooddata/sdk-model";
|
|
4
|
+
import { getAutomationAlertFilters, getAutomationDashboardFilters, getAutomationDashboardFiltersByTab, getAutomationExportParametersByTab, getAutomationVisualizationFilters, } from "../../../../../_staging/automation/index.js";
|
|
5
5
|
import { filterContextItemsToDashboardFiltersByWidget } from "../../../../../converters/filterConverters.js";
|
|
6
6
|
import { isFilterTypeCompatibleWithSelectionType } from "../../../../../model/commandHandlers/dashboard/common/attributeFilterSelectionTypeCompatibility.js";
|
|
7
7
|
import { useDashboardSelector } from "../../../../../model/react/DashboardStoreProvider.js";
|
|
8
|
+
import { selectCatalogParameters, selectCatalogParametersIsLoaded, } from "../../../../../model/store/catalog/catalogSelectors.js";
|
|
9
|
+
import { selectEnableParameters } from "../../../../../model/store/config/configSelectors.js";
|
|
8
10
|
import { selectAutomationCommonDateFilterId, selectAutomationFiltersByTab, selectDashboardFiltersWithoutCrossFiltering, selectDashboardHiddenFilters, selectDashboardLockedFilters, } from "../../../../../model/store/filtering/dashboardFilterSelectors.js";
|
|
9
11
|
import { selectAttributeFilterConfigsSelectionTypeMap, selectAttributeFilterConfigsSelectionTypeMapByTab, } from "../../../../../model/store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
|
|
12
|
+
import { selectWidgetLocalIdToTabIdMap } from "../../../../../model/store/tabs/layout/layoutSelectors.js";
|
|
13
|
+
import { selectSmartPersistedTabsParameters } from "../../../../../model/store/tabs/parameters/parametersSelectors.js";
|
|
14
|
+
import { selectTabs } from "../../../../../model/store/tabs/tabsSelectors.js";
|
|
10
15
|
import { areFiltersEqual, isFilterIgnoredByWidget, isFilterMatch, isNoopAllTimeDateFilterFixed, } from "../utils.js";
|
|
11
16
|
function sanitizeCommonDateFilter(filter, commonDateFilterId) {
|
|
12
17
|
// Sanitize common date filters by removing date dataSet
|
|
@@ -51,6 +56,7 @@ const defaultValidState = {
|
|
|
51
56
|
visibleFilterIsMissingInSavedFilters: false,
|
|
52
57
|
visibleFiltersAreMissing: false,
|
|
53
58
|
incompatibleSelectionTypeIsAppliedInSavedFilters: false,
|
|
59
|
+
parametersAreStale: false,
|
|
54
60
|
};
|
|
55
61
|
export function useValidateExistingAutomationFilters({ automationToEdit, widget, insight, }) {
|
|
56
62
|
const lockedFilters = useDashboardSelector(selectDashboardLockedFilters);
|
|
@@ -60,6 +66,52 @@ export function useValidateExistingAutomationFilters({ automationToEdit, widget,
|
|
|
60
66
|
const dashboardFiltersByTab = useDashboardSelector(selectAutomationFiltersByTab);
|
|
61
67
|
const selectionTypeMap = useDashboardSelector(selectAttributeFilterConfigsSelectionTypeMap);
|
|
62
68
|
const selectionTypeMapByTab = useDashboardSelector(selectAttributeFilterConfigsSelectionTypeMapByTab);
|
|
69
|
+
const parametersEnabled = useDashboardSelector(selectEnableParameters);
|
|
70
|
+
const catalogParameters = useDashboardSelector(selectCatalogParameters);
|
|
71
|
+
const catalogParametersIsLoaded = useDashboardSelector(selectCatalogParametersIsLoaded);
|
|
72
|
+
const dashboardParametersByTab = useDashboardSelector(selectSmartPersistedTabsParameters);
|
|
73
|
+
const tabs = useDashboardSelector(selectTabs);
|
|
74
|
+
const widgetTabMap = useDashboardSelector(selectWidgetLocalIdToTabIdMap);
|
|
75
|
+
// A widget export covers exactly the widget's current tab, so its stored overrides must live under
|
|
76
|
+
// that tab; a dashboard export covers every tab. Resolved here so the validator can flag a widget
|
|
77
|
+
// whose stored override was orphaned under its previous tab after a move.
|
|
78
|
+
const widgetTabId = widget?.localIdentifier ? widgetTabMap[widget.localIdentifier] : undefined;
|
|
79
|
+
// Before the catalog loads, every stored ref looks removed — treat loading as not-stale.
|
|
80
|
+
const parametersAreStale = parametersEnabled && catalogParametersIsLoaded
|
|
81
|
+
? validateExistingAutomationParameters({
|
|
82
|
+
storedParametersByTab: getAutomationExportParametersByTab(automationToEdit),
|
|
83
|
+
catalog: catalogParameters,
|
|
84
|
+
dashboardParametersByTab,
|
|
85
|
+
existingTabIds: new Set((tabs ?? []).map((tab) => tab.localIdentifier)),
|
|
86
|
+
widgetTabId,
|
|
87
|
+
})
|
|
88
|
+
: false;
|
|
89
|
+
const filterValidation = resolveFilterValidation({
|
|
90
|
+
automationToEdit,
|
|
91
|
+
widget,
|
|
92
|
+
insight,
|
|
93
|
+
lockedFilters,
|
|
94
|
+
hiddenFilters,
|
|
95
|
+
dashboardFilters,
|
|
96
|
+
commonDateFilterId,
|
|
97
|
+
dashboardFiltersByTab,
|
|
98
|
+
selectionTypeMap,
|
|
99
|
+
selectionTypeMapByTab,
|
|
100
|
+
});
|
|
101
|
+
// Parameter staleness is resolved at this single hook boundary and folded into the filter result
|
|
102
|
+
// exactly once; the pure filter validators below never deal with the parameter concept.
|
|
103
|
+
return {
|
|
104
|
+
...filterValidation,
|
|
105
|
+
isValid: filterValidation.isValid && !parametersAreStale,
|
|
106
|
+
parametersAreStale,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolves the filter-only validation result (no parameter staleness) for an existing automation.
|
|
111
|
+
* Pure: it takes everything the hook already read from the store, so the early-return branching stays
|
|
112
|
+
* testable and free of React hooks.
|
|
113
|
+
*/
|
|
114
|
+
function resolveFilterValidation({ automationToEdit, widget, insight, lockedFilters, hiddenFilters, dashboardFilters, commonDateFilterId, dashboardFiltersByTab, selectionTypeMap, selectionTypeMapByTab, }) {
|
|
63
115
|
const savedAutomationVisibleFilters = automationToEdit?.metadata?.visibleFilters;
|
|
64
116
|
const savedAutomationVisibleFiltersByTab = automationToEdit?.metadata?.visibleFiltersByTab;
|
|
65
117
|
const ignoredFilters = widget ? dashboardFilters.filter((f) => isFilterIgnoredByWidget(f, widget)) : [];
|
|
@@ -114,6 +166,45 @@ export function useValidateExistingAutomationFilters({ automationToEdit, widget,
|
|
|
114
166
|
//
|
|
115
167
|
// Validations
|
|
116
168
|
//
|
|
169
|
+
/**
|
|
170
|
+
* Flags stale stored parameter overrides for an existing automation: a ref that left the workspace
|
|
171
|
+
* catalog, a tab that no longer exists, or a `readonly`/`hidden` parameter whose pinned value
|
|
172
|
+
* drifted from the current dashboard. `active` parameters are user-owned, so their drift is allowed.
|
|
173
|
+
*/
|
|
174
|
+
export function validateExistingAutomationParameters({ storedParametersByTab, catalog, dashboardParametersByTab, existingTabIds, widgetTabId, }) {
|
|
175
|
+
if (!storedParametersByTab) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
const workspaceById = new Map(catalog.map((parameter) => [parameter.id, parameter]));
|
|
179
|
+
for (const [tabId, storedParameters] of Object.entries(storedParametersByTab)) {
|
|
180
|
+
const tabIsValid = widgetTabId === undefined ? existingTabIds.has(tabId) : tabId === widgetTabId;
|
|
181
|
+
if (!tabIsValid) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
const dashboardById = new Map((dashboardParametersByTab[tabId] ?? []).map((parameter) => [parameter.ref.identifier, parameter]));
|
|
185
|
+
for (const stored of storedParameters) {
|
|
186
|
+
const workspaceParameter = workspaceById.get(stored.id);
|
|
187
|
+
if (!workspaceParameter) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
const dashboardParameter = dashboardById.get(stored.id);
|
|
191
|
+
const mode = dashboardParameter?.mode ?? DashboardParameterModeValues.ACTIVE;
|
|
192
|
+
const isPinnedByAuthor = mode === DashboardParameterModeValues.READONLY ||
|
|
193
|
+
mode === DashboardParameterModeValues.HIDDEN;
|
|
194
|
+
if (!isPinnedByAuthor) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const workspaceDefault = isNumberParameterDefinition(workspaceParameter.definition)
|
|
198
|
+
? workspaceParameter.definition.defaultValue
|
|
199
|
+
: undefined;
|
|
200
|
+
const currentValue = dashboardParameter?.value ?? workspaceDefault;
|
|
201
|
+
if (typeof currentValue === "number" && Number(stored.value) !== currentValue) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
117
208
|
/**
|
|
118
209
|
* Validate existing automation filters against current dashboard filter context and optionally saved widget / insight.
|
|
119
210
|
* Check for inconsistencies, that could lead to unwanted results when editing existing automation.
|
package/esm/presentation/automations/shared/automationFilters/useAutomationExportParameters.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type IAutomationMetadataObject, type IDashboardExportParameter, type IdentifierRef } from "@gooddata/sdk-model";
|
|
2
|
+
import type { ExtendedDashboardWidget } from "../../../../model/types/layoutTypes.js";
|
|
3
|
+
import { type IAutomationParameter } from "./automationParameters.js";
|
|
4
|
+
/**
|
|
5
|
+
* Per-tab parameter working set, keyed by tab `localIdentifier`.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export type EditedParametersByTab = Record<string, IAutomationParameter[]>;
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export interface IUseAutomationExportParametersProps {
|
|
13
|
+
automationToEdit?: IAutomationMetadataObject;
|
|
14
|
+
widget?: ExtendedDashboardWidget;
|
|
15
|
+
/**
|
|
16
|
+
* Persist parameter values onto the export, versus omitting them so the server resolves the
|
|
17
|
+
* latest dashboard defaults. Widget schedules force-store (no store-filters checkbox).
|
|
18
|
+
*/
|
|
19
|
+
storeParameters?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Writes the per-tab parameter wire back onto the edited automation; `undefined` clears it.
|
|
22
|
+
* The user-edit path into `content.parametersByTab` (export-definition rebuilds re-carry it separately).
|
|
23
|
+
*/
|
|
24
|
+
setParametersWire: (wire: Record<string, IDashboardExportParameter[]> | undefined) => void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Scheduled-export parameter editing behind one seam: chips and addable sets to render, the
|
|
28
|
+
* add/change/delete handlers (flat + per-tab), and the wire write-back onto the edited automation.
|
|
29
|
+
* The working set (full execution set incl. `hidden` entries) is implementation — it is held outside
|
|
30
|
+
* the automation so turning persistence off can drop the stored wire without losing the chips. Mirrors
|
|
31
|
+
* {@link useAutomationAlertParameters} in role.
|
|
32
|
+
*
|
|
33
|
+
* The automation is written only at user interactions, never on mount — new automations are seeded
|
|
34
|
+
* at document creation by the owning dialog, and an edited automation's stored wire must survive an
|
|
35
|
+
* open-and-close untouched, or the dialog would open dirty (reconstruct → re-encode is lossy).
|
|
36
|
+
*
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
export interface IUseAutomationExportParameters {
|
|
40
|
+
parametersEnabled: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Per-tab visible parameters to render as chips (the execution set minus `hidden` entries).
|
|
43
|
+
*/
|
|
44
|
+
visibleParametersByTab: EditedParametersByTab;
|
|
45
|
+
/**
|
|
46
|
+
* Per-tab workspace parameters addable via the "+" menu (catalog minus selected/hidden/readonly).
|
|
47
|
+
*/
|
|
48
|
+
availableParametersByTab: EditedParametersByTab;
|
|
49
|
+
/**
|
|
50
|
+
* The tab the flat (non-tabbed) UI edits — the widget's owning tab or the single dashboard tab.
|
|
51
|
+
* Undefined for multi-tab dashboards (per-tab rendering) or when no parameter context applies.
|
|
52
|
+
*/
|
|
53
|
+
flatTabId: string | undefined;
|
|
54
|
+
onParameterAdd: (ref: IdentifierRef) => void;
|
|
55
|
+
onParameterChange: (ref: IdentifierRef, value: number) => void;
|
|
56
|
+
onParameterDelete: (ref: IdentifierRef) => void;
|
|
57
|
+
onParameterAddByTab: (tabId: string, ref: IdentifierRef) => void;
|
|
58
|
+
onParameterChangeByTab: (tabId: string, ref: IdentifierRef, value: number) => void;
|
|
59
|
+
onParameterDeleteByTab: (tabId: string, ref: IdentifierRef) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Resets the working set to the current dashboard's effective values and writes it back — the
|
|
62
|
+
* "apply latest" flow, dropping any stale stored entries the staleness gate flagged.
|
|
63
|
+
*/
|
|
64
|
+
applyLatest: () => void;
|
|
65
|
+
/**
|
|
66
|
+
* `storeParameters` still holds the old value at call time, so the new value is passed in;
|
|
67
|
+
* re-encodes the working set onto the automation with it.
|
|
68
|
+
*/
|
|
69
|
+
onStoreParametersChange: (storeParameters: boolean) => void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @internal
|
|
73
|
+
*/
|
|
74
|
+
export declare function useAutomationExportParameters({ automationToEdit, widget, storeParameters, setParametersWire }: IUseAutomationExportParametersProps): IUseAutomationExportParameters;
|
|
75
|
+
//# sourceMappingURL=useAutomationExportParameters.d.ts.map
|
package/esm/presentation/automations/shared/automationFilters/useAutomationExportParameters.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAutomationExportParameters.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/automations/shared/automationFilters/useAutomationExportParameters.ts"],"names":[],"mappings":"AAMA,OAAO,EAEH,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAG9B,KAAK,aAAa,EAGrB,MAAM,qBAAqB,CAAC;AAa7B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AAEtF,OAAO,EACH,KAAK,oBAAoB,EAM5B,MAAM,2BAA2B,CAAC;AAEnC;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAC;AAE3E;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAChD,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;IAC7C,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,CAAC;CAC9F;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,8BAA8B;IAC3C,iBAAiB,EAAE,OAAO,CAAC;IAC3B;;OAEG;IACH,sBAAsB,EAAE,qBAAqB,CAAC;IAC9C;;OAEG;IACH,wBAAwB,EAAE,qBAAqB,CAAC;IAChD;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,cAAc,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,iBAAiB,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,iBAAiB,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IACjE,sBAAsB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnF,sBAAsB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IACpE;;;OAGG;IACH,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB;;;OAGG;IACH,uBAAuB,EAAE,CAAC,eAAe,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/D;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,EAC1C,gBAAgB,EAChB,MAAM,EACN,eAAe,EACf,iBAAiB,EACpB,EAAE,mCAAmC,GAAG,8BAA8B,CAuLtE"}
|
package/esm/presentation/automations/shared/automationFilters/useAutomationExportParameters.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { useCallback, useMemo, useState } from "react";
|
|
3
|
+
import { mapValues } from "lodash-es";
|
|
4
|
+
import { DashboardParameterModeValues, areObjRefsEqual, objRefToString, } from "@gooddata/sdk-model";
|
|
5
|
+
import { getAutomationExportParametersByTab } from "../../../../_staging/automation/index.js";
|
|
6
|
+
import { useDashboardSelector } from "../../../../model/react/DashboardStoreProvider.js";
|
|
7
|
+
import { selectCatalogParameters } from "../../../../model/store/catalog/catalogSelectors.js";
|
|
8
|
+
import { selectEnableParameters } from "../../../../model/store/config/configSelectors.js";
|
|
9
|
+
import { selectWidgetLocalIdToTabIdMap } from "../../../../model/store/tabs/layout/layoutSelectors.js";
|
|
10
|
+
import { selectExportEffectiveParameters, selectSmartPersistedTabsParameters, } from "../../../../model/store/tabs/parameters/parametersSelectors.js";
|
|
11
|
+
import { selectTabs } from "../../../../model/store/tabs/tabsSelectors.js";
|
|
12
|
+
import { availableAutomationParameters, exportParametersToValues, reconstructAutomationParametersFromExportParameters, shouldStoreExportParameters, toEffectiveParametersByTab, } from "./automationParameters.js";
|
|
13
|
+
/**
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export function useAutomationExportParameters({ automationToEdit, widget, storeParameters, setParametersWire, }) {
|
|
17
|
+
const parametersEnabled = useDashboardSelector(selectEnableParameters);
|
|
18
|
+
const catalog = useDashboardSelector(selectCatalogParameters);
|
|
19
|
+
const dashboardParametersByTab = useDashboardSelector(selectSmartPersistedTabsParameters);
|
|
20
|
+
const tabs = useDashboardSelector(selectTabs);
|
|
21
|
+
const widgetTabMap = useDashboardSelector(selectWidgetLocalIdToTabIdMap);
|
|
22
|
+
const widgetIds = widget ? [objRefToString(widget.ref)] : undefined;
|
|
23
|
+
const effectiveParametersByTab = useDashboardSelector(selectExportEffectiveParameters(widgetIds));
|
|
24
|
+
const flatTabId = resolveFlatTabId(widget, widgetTabMap, tabs);
|
|
25
|
+
const shouldStore = shouldStoreExportParameters(!!widget, storeParameters);
|
|
26
|
+
const [editedParametersByTab, setEditedParametersByTab] = useState(() => {
|
|
27
|
+
if (!parametersEnabled) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
const storedByTab = getAutomationExportParametersByTab(automationToEdit);
|
|
31
|
+
return reconstructParametersByTab(storedByTab ?? effectiveParametersByTab, dashboardParametersByTab, catalog);
|
|
32
|
+
});
|
|
33
|
+
// Encodes the gated wire shape and writes it onto the automation — the only path to the document.
|
|
34
|
+
const persistWire = useCallback((parametersByTab, store) => setParametersWire(toEffectiveParametersByTab(parametersByTab, parametersEnabled && store)), [parametersEnabled, setParametersWire]);
|
|
35
|
+
const visibleParametersByTab = useMemo(() => mapValues(editedParametersByTab, (parameters) => parameters.filter(isVisibleParameter)), [editedParametersByTab]);
|
|
36
|
+
const availableParametersByTab = useMemo(() => {
|
|
37
|
+
if (!parametersEnabled) {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
// The set of tabs the export covers: the widget's owning tab, or every dashboard tab. Keyed by
|
|
41
|
+
// every in-scope tab (not just seeded ones) so tabs with no current override can still add params.
|
|
42
|
+
const scopeTabIds = widget
|
|
43
|
+
? flatTabId
|
|
44
|
+
? [flatTabId]
|
|
45
|
+
: []
|
|
46
|
+
: (tabs ?? []).map((tab) => tab.localIdentifier);
|
|
47
|
+
const result = {};
|
|
48
|
+
for (const tabId of scopeTabIds) {
|
|
49
|
+
// Widget schedules seed addable chips from the widget-effective values, mirroring the alert
|
|
50
|
+
// path; dashboard schedules have no widget context and fall back to dashboard/default
|
|
51
|
+
// inside availableAutomationParameters.
|
|
52
|
+
const widgetParameterValues = widget
|
|
53
|
+
? exportParametersToValues(effectiveParametersByTab[tabId] ?? [])
|
|
54
|
+
: [];
|
|
55
|
+
result[tabId] = availableAutomationParameters(catalog, editedParametersByTab[tabId] ?? [], dashboardParametersByTab[tabId] ?? [], widgetParameterValues);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}, [
|
|
59
|
+
parametersEnabled,
|
|
60
|
+
widget,
|
|
61
|
+
flatTabId,
|
|
62
|
+
tabs,
|
|
63
|
+
editedParametersByTab,
|
|
64
|
+
catalog,
|
|
65
|
+
dashboardParametersByTab,
|
|
66
|
+
effectiveParametersByTab,
|
|
67
|
+
]);
|
|
68
|
+
// The fresh per-tab execution set reconstructed from the current dashboard's effective values.
|
|
69
|
+
const parametersForNewAutomation = useMemo(() => parametersEnabled
|
|
70
|
+
? reconstructParametersByTab(effectiveParametersByTab, dashboardParametersByTab, catalog)
|
|
71
|
+
: {}, [parametersEnabled, effectiveParametersByTab, dashboardParametersByTab, catalog]);
|
|
72
|
+
const applyLatest = useCallback(() => {
|
|
73
|
+
setEditedParametersByTab(parametersForNewAutomation);
|
|
74
|
+
persistWire(parametersForNewAutomation, shouldStore);
|
|
75
|
+
}, [parametersForNewAutomation, persistWire, shouldStore]);
|
|
76
|
+
const onStoreParametersChange = useCallback((nextStoreParameters) => {
|
|
77
|
+
persistWire(editedParametersByTab, nextStoreParameters);
|
|
78
|
+
}, [editedParametersByTab, persistWire]);
|
|
79
|
+
// Patches one tab's parameter execution set by ref, updates the working state, and writes the
|
|
80
|
+
// gated wire shape onto the automation. Edits never touch `hidden`/untouched entries.
|
|
81
|
+
const patchTabParameters = useCallback((tabId, update) => {
|
|
82
|
+
const next = {
|
|
83
|
+
...editedParametersByTab,
|
|
84
|
+
[tabId]: update(editedParametersByTab[tabId] ?? []),
|
|
85
|
+
};
|
|
86
|
+
setEditedParametersByTab(next);
|
|
87
|
+
persistWire(next, shouldStore);
|
|
88
|
+
}, [editedParametersByTab, persistWire, shouldStore]);
|
|
89
|
+
const onParameterChangeByTab = useCallback((tabId, ref, value) => {
|
|
90
|
+
patchTabParameters(tabId, (current) => current.map((parameter) => areObjRefsEqual(parameter.ref, ref) ? { ...parameter, value } : parameter));
|
|
91
|
+
}, [patchTabParameters]);
|
|
92
|
+
const onParameterDeleteByTab = useCallback((tabId, ref) => {
|
|
93
|
+
patchTabParameters(tabId, (current) => current.filter((parameter) => !areObjRefsEqual(parameter.ref, ref)));
|
|
94
|
+
}, [patchTabParameters]);
|
|
95
|
+
const onParameterAddByTab = useCallback((tabId, ref) => {
|
|
96
|
+
const parameter = availableParametersByTab[tabId]?.find((candidate) => areObjRefsEqual(candidate.ref, ref));
|
|
97
|
+
if (parameter) {
|
|
98
|
+
patchTabParameters(tabId, (current) => [...current, parameter]);
|
|
99
|
+
}
|
|
100
|
+
}, [availableParametersByTab, patchTabParameters]);
|
|
101
|
+
// Flat handlers serve the non-tabbed UI; they delegate to the per-tab ones via `flatTabId`.
|
|
102
|
+
const onParameterChange = useCallback((ref, value) => {
|
|
103
|
+
if (flatTabId) {
|
|
104
|
+
onParameterChangeByTab(flatTabId, ref, value);
|
|
105
|
+
}
|
|
106
|
+
}, [flatTabId, onParameterChangeByTab]);
|
|
107
|
+
const onParameterDelete = useCallback((ref) => {
|
|
108
|
+
if (flatTabId) {
|
|
109
|
+
onParameterDeleteByTab(flatTabId, ref);
|
|
110
|
+
}
|
|
111
|
+
}, [flatTabId, onParameterDeleteByTab]);
|
|
112
|
+
const onParameterAdd = useCallback((ref) => {
|
|
113
|
+
if (flatTabId) {
|
|
114
|
+
onParameterAddByTab(flatTabId, ref);
|
|
115
|
+
}
|
|
116
|
+
}, [flatTabId, onParameterAddByTab]);
|
|
117
|
+
return {
|
|
118
|
+
parametersEnabled,
|
|
119
|
+
visibleParametersByTab,
|
|
120
|
+
availableParametersByTab,
|
|
121
|
+
flatTabId,
|
|
122
|
+
onParameterAdd,
|
|
123
|
+
onParameterChange,
|
|
124
|
+
onParameterDelete,
|
|
125
|
+
onParameterAddByTab,
|
|
126
|
+
onParameterChangeByTab,
|
|
127
|
+
onParameterDeleteByTab,
|
|
128
|
+
applyLatest,
|
|
129
|
+
onStoreParametersChange,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function resolveFlatTabId(widget, widgetTabMap, tabs) {
|
|
133
|
+
if (widget) {
|
|
134
|
+
return widget.localIdentifier ? widgetTabMap[widget.localIdentifier] : undefined;
|
|
135
|
+
}
|
|
136
|
+
return (tabs?.length ?? 0) <= 1 ? tabs?.[0]?.localIdentifier : undefined;
|
|
137
|
+
}
|
|
138
|
+
function isVisibleParameter(parameter) {
|
|
139
|
+
return parameter.mode !== DashboardParameterModeValues.HIDDEN;
|
|
140
|
+
}
|
|
141
|
+
function reconstructParametersByTab(byTab, dashboardParametersByTab, catalog) {
|
|
142
|
+
return mapValues(byTab, (exportParameters, tabId) => reconstructAutomationParametersFromExportParameters(exportParameters, dashboardParametersByTab[tabId] ?? [], catalog));
|
|
143
|
+
}
|