@gooddata/sdk-ui-dashboard 11.39.0-alpha.3 → 11.39.0

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 (114) hide show
  1. package/NOTICE +3 -3
  2. package/esm/__version.d.ts +1 -1
  3. package/esm/__version.js +1 -1
  4. package/esm/converters/filterConverters.d.ts +3 -2
  5. package/esm/converters/filterConverters.js +5 -3
  6. package/esm/index.d.ts +2 -2
  7. package/esm/index.js +2 -2
  8. package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.js +6 -0
  9. package/esm/model/commandHandlers/filterContext/measureValueFilter/changeMeasureValueFilterDimensionalityHandler.d.ts +4 -0
  10. package/esm/model/commandHandlers/filterContext/measureValueFilter/changeMeasureValueFilterDimensionalityHandler.js +24 -0
  11. package/esm/model/commandHandlers/index.js +2 -0
  12. package/esm/model/commands/base.d.ts +1 -1
  13. package/esm/model/commands/filters.d.ts +35 -0
  14. package/esm/model/commands/filters.js +19 -0
  15. package/esm/model/commands/index.d.ts +2 -2
  16. package/esm/model/events/base.d.ts +1 -1
  17. package/esm/model/events/filters.d.ts +36 -0
  18. package/esm/model/events/filters.js +19 -0
  19. package/esm/model/events/index.d.ts +2 -2
  20. package/esm/model/queryServices/queryWidgetFilters.js +10 -2
  21. package/esm/model/react/useDashboardCommand.d.ts +2 -1
  22. package/esm/model/react/useDashboardCommandProcessing.d.ts +8 -0
  23. package/esm/model/store/tabs/filterContext/filterContextReducers.d.ts +8 -0
  24. package/esm/model/store/tabs/filterContext/filterContextReducers.js +22 -0
  25. package/esm/model/store/tabs/filterContext/filterContextState.d.ts +2 -1
  26. package/esm/model/store/tabs/index.d.ts +7 -0
  27. package/esm/presentation/automations/alerting/DefaultAlertingDialog/DefaultAlertingDialog.js +11 -7
  28. package/esm/presentation/automations/alerting/DefaultAlertingDialog/components/AlertAttributeSelect.d.ts +2 -1
  29. package/esm/presentation/automations/alerting/DefaultAlertingDialog/components/AlertAttributeSelect.js +3 -3
  30. package/esm/presentation/automations/alerting/DefaultAlertingDialog/components/AlertComparisonPeriodSelect.d.ts +4 -3
  31. package/esm/presentation/automations/alerting/DefaultAlertingDialog/components/AlertComparisonPeriodSelect.js +56 -25
  32. package/esm/presentation/automations/alerting/DefaultAlertingDialog/components/AlertMeasureSelect.d.ts +2 -1
  33. package/esm/presentation/automations/alerting/DefaultAlertingDialog/components/AlertMeasureSelect.js +2 -2
  34. package/esm/presentation/automations/alerting/DefaultAlertingDialog/hooks/useAlertValidation.js +3 -1
  35. package/esm/presentation/automations/alerting/DefaultAlertingDialog/hooks/useEditAlert.d.ts +4 -2
  36. package/esm/presentation/automations/alerting/DefaultAlertingDialog/hooks/useEditAlert.js +10 -5
  37. package/esm/presentation/automations/alerting/DefaultAlertingDialog/utils/getters.js +8 -1
  38. package/esm/presentation/automations/alerting/DefaultAlertingDialog/utils/items.d.ts +6 -3
  39. package/esm/presentation/automations/alerting/DefaultAlertingDialog/utils/items.js +235 -127
  40. package/esm/presentation/automations/alerting/DefaultAlertingDialog/utils/transformation.d.ts +3 -2
  41. package/esm/presentation/automations/alerting/DefaultAlertingDialog/utils/transformation.js +5 -2
  42. package/esm/presentation/filterBar/measureValueFilter/DefaultDashboardMeasureValueFilter.js +40 -7
  43. package/esm/presentation/filterBar/measureValueFilter/MeasureValueFilterConfiguration.d.ts +7 -2
  44. package/esm/presentation/filterBar/measureValueFilter/MeasureValueFilterConfiguration.js +7 -3
  45. package/esm/presentation/filterBar/measureValueFilter/useDashboardMeasureValueFilterData.d.ts +9 -0
  46. package/esm/presentation/filterBar/measureValueFilter/useDashboardMeasureValueFilterData.js +125 -6
  47. package/esm/presentation/localization/bundles/de-DE.localization-bundle.d.ts +1 -0
  48. package/esm/presentation/localization/bundles/de-DE.localization-bundle.js +2 -1
  49. package/esm/presentation/localization/bundles/en-AU.localization-bundle.d.ts +1 -0
  50. package/esm/presentation/localization/bundles/en-AU.localization-bundle.js +2 -1
  51. package/esm/presentation/localization/bundles/en-GB.localization-bundle.d.ts +1 -0
  52. package/esm/presentation/localization/bundles/en-GB.localization-bundle.js +2 -1
  53. package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts +8 -0
  54. package/esm/presentation/localization/bundles/en-US.localization-bundle.js +9 -1
  55. package/esm/presentation/localization/bundles/es-419.localization-bundle.d.ts +1 -0
  56. package/esm/presentation/localization/bundles/es-419.localization-bundle.js +2 -1
  57. package/esm/presentation/localization/bundles/es-ES.localization-bundle.d.ts +1 -0
  58. package/esm/presentation/localization/bundles/es-ES.localization-bundle.js +2 -1
  59. package/esm/presentation/localization/bundles/fi-FI.localization-bundle.d.ts +1 -0
  60. package/esm/presentation/localization/bundles/fi-FI.localization-bundle.js +2 -1
  61. package/esm/presentation/localization/bundles/fr-CA.localization-bundle.d.ts +1 -0
  62. package/esm/presentation/localization/bundles/fr-CA.localization-bundle.js +2 -1
  63. package/esm/presentation/localization/bundles/fr-FR.localization-bundle.d.ts +1 -0
  64. package/esm/presentation/localization/bundles/fr-FR.localization-bundle.js +2 -1
  65. package/esm/presentation/localization/bundles/id-ID.localization-bundle.d.ts +1 -0
  66. package/esm/presentation/localization/bundles/id-ID.localization-bundle.js +2 -1
  67. package/esm/presentation/localization/bundles/it-IT.localization-bundle.d.ts +1 -0
  68. package/esm/presentation/localization/bundles/it-IT.localization-bundle.js +2 -1
  69. package/esm/presentation/localization/bundles/ja-JP.localization-bundle.d.ts +1 -0
  70. package/esm/presentation/localization/bundles/ja-JP.localization-bundle.js +2 -1
  71. package/esm/presentation/localization/bundles/ko-KR.localization-bundle.d.ts +1 -0
  72. package/esm/presentation/localization/bundles/ko-KR.localization-bundle.js +2 -1
  73. package/esm/presentation/localization/bundles/nl-NL.localization-bundle.d.ts +1 -0
  74. package/esm/presentation/localization/bundles/nl-NL.localization-bundle.js +2 -1
  75. package/esm/presentation/localization/bundles/pl-PL.localization-bundle.d.ts +1 -0
  76. package/esm/presentation/localization/bundles/pl-PL.localization-bundle.js +2 -1
  77. package/esm/presentation/localization/bundles/pt-BR.localization-bundle.d.ts +1 -0
  78. package/esm/presentation/localization/bundles/pt-BR.localization-bundle.js +2 -1
  79. package/esm/presentation/localization/bundles/pt-PT.localization-bundle.d.ts +1 -0
  80. package/esm/presentation/localization/bundles/pt-PT.localization-bundle.js +2 -1
  81. package/esm/presentation/localization/bundles/ru-RU.localization-bundle.d.ts +1 -0
  82. package/esm/presentation/localization/bundles/ru-RU.localization-bundle.js +2 -1
  83. package/esm/presentation/localization/bundles/sl-SI.localization-bundle.d.ts +1 -0
  84. package/esm/presentation/localization/bundles/sl-SI.localization-bundle.js +2 -1
  85. package/esm/presentation/localization/bundles/th-TH.localization-bundle.d.ts +1 -0
  86. package/esm/presentation/localization/bundles/th-TH.localization-bundle.js +2 -1
  87. package/esm/presentation/localization/bundles/tr-TR.localization-bundle.d.ts +1 -0
  88. package/esm/presentation/localization/bundles/tr-TR.localization-bundle.js +2 -1
  89. package/esm/presentation/localization/bundles/uk-UA.localization-bundle.d.ts +1 -0
  90. package/esm/presentation/localization/bundles/uk-UA.localization-bundle.js +2 -1
  91. package/esm/presentation/localization/bundles/vi-VN.localization-bundle.d.ts +1 -0
  92. package/esm/presentation/localization/bundles/vi-VN.localization-bundle.js +2 -1
  93. package/esm/presentation/localization/bundles/zh-HK.localization-bundle.d.ts +1 -0
  94. package/esm/presentation/localization/bundles/zh-HK.localization-bundle.js +2 -1
  95. package/esm/presentation/localization/bundles/zh-Hans.localization-bundle.d.ts +1 -0
  96. package/esm/presentation/localization/bundles/zh-Hans.localization-bundle.js +2 -1
  97. package/esm/presentation/localization/bundles/zh-Hant.localization-bundle.d.ts +1 -0
  98. package/esm/presentation/localization/bundles/zh-Hant.localization-bundle.js +2 -1
  99. package/esm/presentation/widget/common/configuration/FilterConfiguration.js +5 -2
  100. package/esm/presentation/widget/common/configuration/MeasureValueFilterConfigurationItem.d.ts +2 -1
  101. package/esm/presentation/widget/common/configuration/MeasureValueFilterConfigurationItem.js +9 -6
  102. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/EditAlert.js +2 -2
  103. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useEditAlert.d.ts +2 -2
  104. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useEditAlert.js +2 -2
  105. package/esm/presentation/widget/insight/configuration/InsightAlertConfig/hooks/useInsightWidgetAlerting.js +1 -1
  106. package/esm/sdk-ui-dashboard.d.ts +96 -4
  107. package/package.json +20 -20
  108. package/styles/css/dashboard.css +30 -2
  109. package/styles/css/dashboard.css.map +1 -1
  110. package/styles/css/main.css +247 -2
  111. package/styles/css/main.css.map +1 -1
  112. package/styles/css/notifications_channels_dialog.css +30 -2
  113. package/styles/css/notifications_channels_dialog.css.map +1 -1
  114. package/styles/scss/notifications_channels_dialog.scss +46 -5
@@ -159,8 +159,12 @@ export declare const useDashboardCommandProcessing: <TCommand extends DashboardC
159
159
  type: TSuccessEventType;
160
160
  }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterAdded, {
161
161
  type: TSuccessEventType;
162
+ }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterConditionChanged, {
163
+ type: TSuccessEventType;
162
164
  }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterConfigModeChanged, {
163
165
  type: TSuccessEventType;
166
+ }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterDimensionalityChanged, {
167
+ type: TSuccessEventType;
164
168
  }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterMoved, {
165
169
  type: TSuccessEventType;
166
170
  }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterRemoved, {
@@ -398,8 +402,12 @@ export declare const useDashboardCommandProcessing: <TCommand extends DashboardC
398
402
  type: TErrorEventType;
399
403
  }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterAdded, {
400
404
  type: TErrorEventType;
405
+ }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterConditionChanged, {
406
+ type: TErrorEventType;
401
407
  }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterConfigModeChanged, {
402
408
  type: TErrorEventType;
409
+ }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterDimensionalityChanged, {
410
+ type: TErrorEventType;
403
411
  }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterMoved, {
404
412
  type: TErrorEventType;
405
413
  }> | Extract<import("../events/filters.js").IDashboardMeasureValueFilterRemoved, {
@@ -247,6 +247,10 @@ type IChangeMeasureValueFilterTitleReducerPayload = {
247
247
  readonly filterLocalId: string;
248
248
  readonly title?: string;
249
249
  };
250
+ type ISetMeasureValueFilterDimensionalityReducerPayload = {
251
+ readonly localIdentifier: string;
252
+ readonly dimensionality?: ObjRef[];
253
+ };
250
254
  export declare const filterContextReducers: {
251
255
  setFilterContext: FilterContextReducer<{
252
256
  payload: SetFilterContextPayload;
@@ -372,6 +376,10 @@ export declare const filterContextReducers: {
372
376
  payload: IChangeMeasureValueFilterConditionReducerPayload;
373
377
  type: string;
374
378
  }>;
379
+ setMeasureValueFilterDimensionality: FilterContextReducer<{
380
+ payload: ISetMeasureValueFilterDimensionalityReducerPayload;
381
+ type: string;
382
+ }>;
375
383
  changeMeasureValueFilterTitle: FilterContextReducer<{
376
384
  payload: IChangeMeasureValueFilterTitleReducerPayload;
377
385
  type: string;
@@ -866,6 +866,27 @@ const changeMeasureValueFilterCondition = (state, action) => {
866
866
  },
867
867
  };
868
868
  };
869
+ const setMeasureValueFilterDimensionality = (state, action) => {
870
+ const activeTab = getActiveTab(state);
871
+ if (!activeTab) {
872
+ return;
873
+ }
874
+ invariant(activeTab.filterContext?.filterContextDefinition?.filters, "Attempt to edit uninitialized filter context");
875
+ const filters = activeTab.filterContext.filterContextDefinition.filters;
876
+ const index = filters.findIndex((item) => isDashboardMeasureValueFilter(item) &&
877
+ dashboardFilterLocalIdentifier(item) === action.payload.localIdentifier);
878
+ invariant(index >= 0, `Attempt to change dimensionality of measure value filter ${action.payload.localIdentifier} that does not exist`);
879
+ const filter = filters[index];
880
+ const dimensionality = action.payload.dimensionality && action.payload.dimensionality.length > 0
881
+ ? action.payload.dimensionality
882
+ : undefined;
883
+ filters[index] = {
884
+ dashboardMeasureValueFilter: {
885
+ ...filter.dashboardMeasureValueFilter,
886
+ dimensionality,
887
+ },
888
+ };
889
+ };
869
890
  const changeMeasureValueFilterTitle = (state, action) => {
870
891
  const activeTab = getActiveTab(state);
871
892
  if (!activeTab) {
@@ -919,5 +940,6 @@ export const filterContextReducers = {
919
940
  resetWorkingSelection,
920
941
  setDefaultFilterOverrides,
921
942
  changeMeasureValueFilterCondition,
943
+ setMeasureValueFilterDimensionality,
922
944
  changeMeasureValueFilterTitle,
923
945
  };
@@ -1,5 +1,5 @@
1
1
  import { type IAttributeWithReferences } from "@gooddata/sdk-backend-spi";
2
- import { type FilterContextItem, type IAttributeDisplayFormMetadataObject, type IDashboardAttributeFilter, type IDashboardDateFilter, type IDashboardObjectIdentity, type IFilterContextDefinition, type MeasureValueFilterCondition } from "@gooddata/sdk-model";
2
+ import { type FilterContextItem, type IAttributeDisplayFormMetadataObject, type IDashboardAttributeFilter, type IDashboardDateFilter, type IDashboardObjectIdentity, type IFilterContextDefinition, type MeasureValueFilterCondition, type ObjRef } from "@gooddata/sdk-model";
3
3
  /**
4
4
  * Partial working attribute filter used in working filter context.
5
5
  *
@@ -17,6 +17,7 @@ export type WorkingDashboardMeasureValueFilter = {
17
17
  dashboardMeasureValueFilter: {
18
18
  localIdentifier: string;
19
19
  conditions?: MeasureValueFilterCondition[];
20
+ dimensionality?: ObjRef[];
20
21
  };
21
22
  };
22
23
  /**
@@ -238,6 +238,13 @@ export declare const tabsActions: import("@reduxjs/toolkit").CaseReducerActions<
238
238
  payload: import("./filterContext/filterContextReducers.js").IChangeMeasureValueFilterConditionReducerPayload;
239
239
  type: string;
240
240
  }) => void | import("./tabsState.js").ITabsState | import("immer").WritableDraft<import("./tabsState.js").ITabsState>;
241
+ readonly setMeasureValueFilterDimensionality: (state: import("immer").WritableDraft<import("./tabsState.js").ITabsState>, action: {
242
+ payload: {
243
+ readonly localIdentifier: string;
244
+ readonly dimensionality?: import("@gooddata/sdk-model").ObjRef[] | undefined;
245
+ };
246
+ type: string;
247
+ }) => void | import("./tabsState.js").ITabsState | import("immer").WritableDraft<import("./tabsState.js").ITabsState>;
241
248
  readonly changeMeasureValueFilterTitle: (state: import("immer").WritableDraft<import("./tabsState.js").ITabsState>, action: {
242
249
  payload: {
243
250
  readonly filterLocalId: string;
@@ -63,7 +63,7 @@ export function AlertingDialogRenderer({ alertToEdit, users, usersError, notific
63
63
  automationToEdit: alertToEdit,
64
64
  widget,
65
65
  });
66
- const { onTitleChange, onRecipientsChange, onFiltersChange, onApplyCurrentFilters, onMeasureChange, getAttributeValues, onAttributeChange, onComparisonOperatorChange, onRelativeOperatorChange, onAnomalyDetectionChange, onChange, onBlur, onComparisonTypeChange, onDestinationChange, onTriggerModeChange, onTriggerIntervalChange, selectedMeasure, supportedMeasures, canManageAttributes, selectedAttribute, selectedValue, supportedAttributes, catalogAttributes, catalogDateDatasets, isResultLoading, selectedComparisonOperator, selectedRelativeOperator, selectedAiOperator, value, selectedComparator, selectedSensitivity, onSensitivityChange, selectedGranularity, onGranularityChange, canManageComparison, separators, defaultUser, originalAutomation, editedAutomation, allowOnlyLoggedUserRecipients, allowExternalRecipients, warningMessage, validationErrorMessage, isSubmitDisabled, isParentValid, thresholdErrorMessage, allowHourlyRecurrence, } = useEditAlert({
66
+ const { onTitleChange, onRecipientsChange, onFiltersChange, onApplyCurrentFilters, onMeasureChange, getAttributeValues, onAttributeChange, onComparisonOperatorChange, onRelativeOperatorChange, onAnomalyDetectionChange, onChange, onBlur, onComparisonTypeChange, onDestinationChange, onTriggerModeChange, onTriggerIntervalChange, selectedMeasure, canChangeMeasure, supportedMeasures, canManageAttributes, selectedAttribute, selectedValue, supportedAttributes, catalogAttributes, catalogDateDatasets, isResultLoading, selectedComparisonOperator, selectedRelativeOperator, selectedAiOperator, value, selectedComparator, selectedSensitivity, onSensitivityChange, selectedGranularity, onGranularityChange, canManageComparison, separators, defaultUser, originalAutomation, editedAutomation, allowOnlyLoggedUserRecipients, allowExternalRecipients, warningMessage, validationErrorMessage, isInvalidConnectionToInsight, isSubmitDisabled, isParentValid, thresholdErrorMessage, allowHourlyRecurrence, } = useEditAlert({
67
67
  insight,
68
68
  widget,
69
69
  alertToEdit,
@@ -118,10 +118,14 @@ export function AlertingDialogRenderer({ alertToEdit, users, usersError, notific
118
118
  const invalidDatapoints = getInvalidDatapoints();
119
119
  useEffect(() => {
120
120
  setInvalidDatapoints(() => [
121
- !!validationErrorMessage && createInvalidDatapoint({ message: validationErrorMessage }),
121
+ !!validationErrorMessage &&
122
+ createInvalidDatapoint({
123
+ message: validationErrorMessage,
124
+ severity: isInvalidConnectionToInsight ? "error" : "warning",
125
+ }),
122
126
  !!thresholdErrorMessage && createInvalidDatapoint({ message: thresholdErrorMessage }),
123
127
  ]);
124
- }, [validationErrorMessage, thresholdErrorMessage, setInvalidDatapoints]);
128
+ }, [validationErrorMessage, thresholdErrorMessage, isInvalidConnectionToInsight, setInvalidDatapoints]);
125
129
  const helpTextId = isMobileView()
126
130
  ? defineMessage({ id: "dialogs.alerting.footer.title.short" }).id
127
131
  : defineMessage({ id: "dialogs.alerting.footer.title" }).id;
@@ -164,10 +168,10 @@ export function AlertingDialogRenderer({ alertToEdit, users, usersError, notific
164
168
  _jsx("div", { className: "gd-divider-with-margin" }), _jsxs(_Fragment, { children: [
165
169
  _jsx(AutomationFiltersSelect, { availableFilters: availableFilters, selectedFilters: editedAutomationFilters, onFiltersChange: onFiltersChange, storeFilters: true, onStoreFiltersChange: () => { }, isDashboardAutomation: false, overlayPositionType: OVERLAY_POSITION_TYPE, disableDateFilters: isAnomalyDetection(editedAutomation?.alert) }), _jsx(ContentDivider, { className: "gd-divider-with-margin" })
166
170
  ] }), _jsxs(FormFieldGroup, { label: _jsx(FormattedMessage, { id: "insightAlert.config.when" }), children: [
167
- _jsx(FormField, { label: intl.formatMessage({ id: "insightAlert.config.metric" }), htmlFor: "alert.measure", children: _jsx(AlertMeasureSelect, { selectedMeasure: selectedMeasure, onMeasureChange: onMeasureChange, measures: supportedMeasures, overlayPositionType: OVERLAY_POSITION_TYPE, id: "alert.measure", closeOnParentScroll: CLOSE_ON_PARENT_SCROLL }) }), Boolean(canManageAttributes) &&
171
+ _jsx(FormField, { label: intl.formatMessage({ id: "insightAlert.config.metric" }), htmlFor: "alert.measure", children: _jsx(AlertMeasureSelect, { selectedMeasure: selectedMeasure, onMeasureChange: onMeasureChange, measures: supportedMeasures, disabled: !canChangeMeasure, overlayPositionType: OVERLAY_POSITION_TYPE, id: "alert.measure", closeOnParentScroll: CLOSE_ON_PARENT_SCROLL }) }), Boolean(canManageAttributes) &&
168
172
  supportedAttributes.filter((a) => a.type === "attribute").length >
169
- 0 && (_jsx(FormField, { label: _jsx(FormattedMessage, { id: "insightAlert.config.for" }), htmlFor: "alert.attribute", children: _jsx(AlertAttributeSelect, { id: "alert.attribute", selectedAttribute: selectedAttribute, selectedValue: selectedValue, onAttributeChange: onAttributeChange, attributes: supportedAttributes, catalogAttributes: catalogAttributes, catalogDateDatasets: catalogDateDatasets, getAttributeValues: getAttributeValues, isResultLoading: isResultLoading, showLabel: false, closeOnParentScroll: CLOSE_ON_PARENT_SCROLL }) })), _jsx(FormField, { label: _jsx(FormattedMessage, { id: "insightAlert.config.condition" }), htmlFor: "alert.condition", children: _jsx(AlertComparisonOperatorSelect, { id: "alert.condition", measure: selectedMeasure, enableAnomalyDetectionAlert: enableAnomalyDetectionAlert ? enableAiAssistant : false, selectedComparisonOperator: selectedComparisonOperator, selectedRelativeOperator: selectedRelativeOperator, selectedAiOperator: selectedAiOperator, onAnomalyDetectionChange: onAnomalyDetectionChange, onComparisonOperatorChange: onComparisonOperatorChange, onRelativeOperatorChange: onRelativeOperatorChange, overlayPositionType: OVERLAY_POSITION_TYPE, closeOnParentScroll: CLOSE_ON_PARENT_SCROLL }) }), !isAnomalyDetection(editedAutomation?.alert) && (_jsx(FormField, { label: _jsx(FormattedMessage, { id: "insightAlert.config.threshold" }), htmlFor: "alert.value", children: _jsx(AlertThresholdInput, { id: "alert.value", value: value, onChange: onChange, onBlur: onBlur, suffix: getValueSuffix(editedAutomation?.alert), errorMessage: thresholdErrorMessage }) })), isChangeOrDifferenceOperator(editedAutomation?.alert) && (_jsx(FormField, { label: _jsx(FormattedMessage, { id: "insightAlert.config.comparison" }), htmlFor: "alert.comparison", children: _jsx(AlertComparisonPeriodSelect, { id: "alert.comparison", measure: selectedMeasure, alert: editedAutomation, selectedComparison: selectedComparator?.comparator, onComparisonChange: (comparisonType) => {
170
- onComparisonTypeChange(selectedMeasure, selectedRelativeOperator, comparisonType);
173
+ 0 && (_jsx(FormField, { label: _jsx(FormattedMessage, { id: "insightAlert.config.for" }), htmlFor: "alert.attribute", children: _jsx(AlertAttributeSelect, { id: "alert.attribute", disabled: !canChangeMeasure, selectedAttribute: selectedAttribute, selectedValue: selectedValue, onAttributeChange: onAttributeChange, attributes: supportedAttributes, catalogAttributes: catalogAttributes, catalogDateDatasets: catalogDateDatasets, getAttributeValues: getAttributeValues, isResultLoading: isResultLoading, showLabel: false, closeOnParentScroll: CLOSE_ON_PARENT_SCROLL }) })), _jsx(FormField, { label: _jsx(FormattedMessage, { id: "insightAlert.config.condition" }), htmlFor: "alert.condition", children: _jsx(AlertComparisonOperatorSelect, { id: "alert.condition", measure: selectedMeasure, enableAnomalyDetectionAlert: enableAnomalyDetectionAlert ? enableAiAssistant : false, selectedComparisonOperator: selectedComparisonOperator, selectedRelativeOperator: selectedRelativeOperator, selectedAiOperator: selectedAiOperator, onAnomalyDetectionChange: onAnomalyDetectionChange, onComparisonOperatorChange: onComparisonOperatorChange, onRelativeOperatorChange: onRelativeOperatorChange, overlayPositionType: OVERLAY_POSITION_TYPE, closeOnParentScroll: CLOSE_ON_PARENT_SCROLL }) }), !isAnomalyDetection(editedAutomation?.alert) && (_jsx(FormField, { label: _jsx(FormattedMessage, { id: "insightAlert.config.threshold" }), htmlFor: "alert.value", children: _jsx(AlertThresholdInput, { id: "alert.value", value: value, onChange: onChange, onBlur: onBlur, suffix: getValueSuffix(editedAutomation?.alert), errorMessage: thresholdErrorMessage }) })), isChangeOrDifferenceOperator(editedAutomation?.alert) && (_jsx(FormField, { label: _jsx(FormattedMessage, { id: "insightAlert.config.comparison" }), htmlFor: "alert.comparison", children: _jsx(AlertComparisonPeriodSelect, { id: "alert.comparison", measure: selectedMeasure, alert: editedAutomation, selectedComparison: selectedComparator?.comparator, selectedGranularity: selectedComparator?.granularity, onComparisonChange: (comparisonType, granularity) => {
174
+ onComparisonTypeChange(selectedMeasure, selectedRelativeOperator, comparisonType, granularity);
171
175
  }, overlayPositionType: OVERLAY_POSITION_TYPE, canManageComparison: canManageComparison, closeOnParentScroll: CLOSE_ON_PARENT_SCROLL }) })), isAnomalyDetection(editedAutomation?.alert) && (_jsxs(_Fragment, { children: [
172
176
  _jsx(FormField, { label: _jsx(FormattedMessage, { id: "insightAlert.config.sensitivity" }), htmlFor: "alert.sensitivity", children: _jsx(AlertSensitivitySelect, { id: "alert.sensitivity", selectedSensitivity: selectedSensitivity, onSensitivityChange: onSensitivityChange, overlayPositionType: OVERLAY_POSITION_TYPE, closeOnParentScroll: CLOSE_ON_PARENT_SCROLL }) }), _jsx(FormField, { label: _jsxs("div", { className: "gd-dashboard-alerting-dialog-form-field__content-container-tooltip", children: [
173
177
  _jsx(FormattedMessage, { id: "insightAlert.config.granularity" }), _jsx(UiTooltip, { anchor: _jsx(UiIconButton, { icon: "question", variant: "tertiary", size: "xsmall", accessibilityConfig: {
@@ -185,7 +189,7 @@ export function AlertingDialogRenderer({ alertToEdit, users, usersError, notific
185
189
  }),
186
190
  } }), content: _jsx(FormattedMessage, { id: "insightAlert.config.interval.tooltip" }), arrowPlacement: "left", optimalPlacement: true, offset: 10, width: 280, triggerBy: ["hover", "click"] })
187
191
  ] }), htmlFor: "alert.interval", children: _jsx(AlertTriggerIntervalSelect, { id: "alert.interval", selectedTriggerInterval: editedAutomation?.alert?.trigger.interval ?? "DAY", onTriggerIntervalChange: onTriggerIntervalChange, overlayPositionType: OVERLAY_POSITION_TYPE, closeOnParentScroll: CLOSE_ON_PARENT_SCROLL }) })) : null, _jsx(FormField, { label: _jsx(FormattedMessage, { id: "insightAlert.config.recipients" }), htmlFor: "alert.recipients", fullWidth: true, children: _jsx(RecipientsSelect, { id: "alert.recipients", loggedUser: defaultUser, users: users, usersError: usersError, value: editedAutomation?.recipients ?? [], originalValue: originalAutomation?.recipients || [], onChange: onRecipientsChange, allowEmptySelection: true, allowOnlyLoggedUserRecipients: allowOnlyLoggedUserRecipients, allowExternalRecipients: allowExternalRecipients, maxRecipients: maxAutomationsRecipients, notificationChannels: notificationChannels, notificationChannelId: editedAutomation?.notificationChannel, showLabel: false, externalRecipientOverride: externalRecipientOverride }) })
188
- ] }), warningMessage ? (_jsx(Message, { type: "warning", className: "gd-notifications-channels-dialog-error", children: warningMessage })) : null, invalidDatapoints.map((datapoint) => (_jsx(Message, { type: "error", id: datapoint.id, className: "gd-notifications-channels-dialog-error gd-notifications-channels-dialog-error-scrollable", children: datapoint.message }, datapoint.id)))] })
192
+ ] }), warningMessage ? (_jsx(Message, { type: "warning", className: "gd-notifications-channels-dialog-error", children: warningMessage })) : null, invalidDatapoints.map((datapoint) => (_jsx(Message, { id: datapoint.id, type: datapoint.severity === "info" ? "progress" : datapoint.severity, className: "gd-notifications-channels-dialog-error gd-notifications-channels-dialog-error-scrollable", children: datapoint.message }, datapoint.id)))] })
189
193
  ] }) }) }) }), alertToDelete ? (_jsx(DeleteAlertConfirmDialog, { alert: alertToDelete, onCancel: () => setAlertToDelete(null), onSuccess: handleAlertDeleteSuccess, onError: onDeleteError })) : null] }));
190
194
  }
191
195
  /**
@@ -3,6 +3,7 @@ import { type AlertAttribute } from "../../types.js";
3
3
  import { type AttributeValue } from "../hooks/useAttributeValuesFromExecResults.js";
4
4
  export interface IAlertAttributeSelectProps {
5
5
  id: string;
6
+ disabled?: boolean;
6
7
  selectedAttribute: AlertAttribute | undefined;
7
8
  selectedValue: string | null | undefined;
8
9
  onAttributeChange: (attribute: AlertAttribute | undefined, value: AttributeValue | undefined) => void;
@@ -14,4 +15,4 @@ export interface IAlertAttributeSelectProps {
14
15
  showLabel?: boolean;
15
16
  closeOnParentScroll?: boolean;
16
17
  }
17
- export declare function AlertAttributeSelect({ id, selectedAttribute: selectedAttributeProp, getAttributeValues, isResultLoading, selectedValue, onAttributeChange, attributes, catalogAttributes, catalogDateDatasets, showLabel, closeOnParentScroll }: IAlertAttributeSelectProps): import("react/jsx-runtime").JSX.Element | null;
18
+ export declare function AlertAttributeSelect({ id, disabled, selectedAttribute: selectedAttributeProp, getAttributeValues, isResultLoading, selectedValue, onAttributeChange, attributes, catalogAttributes, catalogDateDatasets, showLabel, closeOnParentScroll }: IAlertAttributeSelectProps): import("react/jsx-runtime").JSX.Element | null;
@@ -54,7 +54,7 @@ function AttributeValuesSearchContent({ attribute, values, isSelected, selectedA
54
54
  }, children: _jsx("div", { className: "gd-alert-attribute-select__menu-item s-menu-alert-attribute-item-value", children: (value.title ?? value.name) || `(${intl.formatMessage({ id: "empty_value" })})` }) }, index))) })
55
55
  ] }));
56
56
  }
57
- export function AlertAttributeSelect({ id, selectedAttribute: selectedAttributeProp, getAttributeValues, isResultLoading, selectedValue, onAttributeChange, attributes, catalogAttributes, catalogDateDatasets, showLabel = true, closeOnParentScroll, }) {
57
+ export function AlertAttributeSelect({ id, disabled, selectedAttribute: selectedAttributeProp, getAttributeValues, isResultLoading, selectedValue, onAttributeChange, attributes, catalogAttributes, catalogDateDatasets, showLabel = true, closeOnParentScroll, }) {
58
58
  const intl = useIntl();
59
59
  const availableAttributes = useMemo(() => {
60
60
  return attributes.filter((attr) => attr.type === "attribute");
@@ -135,10 +135,10 @@ export function AlertAttributeSelect({ id, selectedAttribute: selectedAttributeP
135
135
  return (_jsx(DropdownButton, { id: id, className: cx("gd-alert-attribute-select__button", {
136
136
  "is-active": isOpen,
137
137
  }), value: buttonValue, iconLeft: "gd-icon-attribute", onClick: () => {
138
- if (!isResultLoading) {
138
+ if (!isResultLoading && !disabled) {
139
139
  toggleDropdown();
140
140
  }
141
- }, disabled: isResultLoading, buttonRef: buttonRef, dropdownId: dropdownId, isOpen: isOpen, accessibilityConfig: {
141
+ }, disabled: isResultLoading || disabled, buttonRef: buttonRef, dropdownId: dropdownId, isOpen: isOpen, accessibilityConfig: {
142
142
  ariaExpanded: isOpen,
143
143
  popupType: "menu",
144
144
  } }));
@@ -1,4 +1,4 @@
1
- import { type IAutomationMetadataObject } from "@gooddata/sdk-model";
1
+ import { type DateAttributeGranularity, type IAutomationMetadataObject } from "@gooddata/sdk-model";
2
2
  import { type OverlayPositionType } from "@gooddata/sdk-ui-kit";
3
3
  import { type AlertMetric, AlertMetricComparatorType } from "../../types.js";
4
4
  export interface IAlertComparisonPeriodSelectProps {
@@ -6,9 +6,10 @@ export interface IAlertComparisonPeriodSelectProps {
6
6
  measure: AlertMetric | undefined;
7
7
  overlayPositionType?: OverlayPositionType;
8
8
  selectedComparison?: AlertMetricComparatorType;
9
- onComparisonChange: (comparison: AlertMetricComparatorType) => void;
9
+ selectedGranularity?: DateAttributeGranularity;
10
+ onComparisonChange: (comparison: AlertMetricComparatorType, granularity?: DateAttributeGranularity) => void;
10
11
  canManageComparison: boolean;
11
12
  id: string;
12
13
  closeOnParentScroll?: boolean;
13
14
  }
14
- export declare function AlertComparisonPeriodSelect({ alert, measure, overlayPositionType, selectedComparison, canManageComparison, onComparisonChange, id, closeOnParentScroll }: IAlertComparisonPeriodSelectProps): import("react/jsx-runtime").JSX.Element | null;
15
+ export declare function AlertComparisonPeriodSelect({ alert, measure, overlayPositionType, selectedComparison, selectedGranularity, canManageComparison, onComparisonChange, id, closeOnParentScroll }: IAlertComparisonPeriodSelectProps): import("react/jsx-runtime").JSX.Element | null;
@@ -3,43 +3,73 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useMemo } from "react";
4
4
  import cx from "classnames";
5
5
  import { useIntl } from "react-intl";
6
- import { DateGranularity } from "@gooddata/sdk-model";
6
+ import { DateGranularity, } from "@gooddata/sdk-model";
7
7
  import { Dropdown, DropdownButton, SingleSelectListItem, UiListbox, } from "@gooddata/sdk-ui-kit";
8
8
  import { AlertMetricComparatorType } from "../../types.js";
9
9
  import { translateGranularity } from "../utils/granularity.js";
10
10
  import { isChangeOrDifferenceOperator } from "../utils/guards.js";
11
- export function AlertComparisonPeriodSelect({ alert, measure, overlayPositionType, selectedComparison, canManageComparison, onComparisonChange, id, closeOnParentScroll, }) {
11
+ export function AlertComparisonPeriodSelect({ alert, measure, overlayPositionType, selectedComparison, selectedGranularity, canManageComparison, onComparisonChange, id, closeOnParentScroll, }) {
12
12
  const intl = useIntl();
13
13
  const selectedOperator = useMemo(() => {
14
- return measure?.comparators.find((a) => a.comparator === selectedComparison);
15
- }, [measure?.comparators, selectedComparison]);
14
+ return measure?.comparators.find((a) => a.comparator === selectedComparison &&
15
+ (selectedGranularity ? selectedGranularity === a.granularity : true));
16
+ }, [measure?.comparators, selectedComparison, selectedGranularity]);
16
17
  const comparisons = useMemo(() => {
17
- const sp = measure?.comparators.find((a) => a.comparator === AlertMetricComparatorType.SamePeriodPreviousYear);
18
- const pp = measure?.comparators.find((a) => a.comparator === AlertMetricComparatorType.PreviousPeriod);
19
- return [
20
- sp?.granularity && pp?.granularity !== DateGranularity["year"]
21
- ? {
22
- title: intl.formatMessage({ id: "insightAlert.config.compare_with_sp_granularity" }, {
23
- period: translateGranularity(intl, sp.granularity),
24
- }),
25
- type: AlertMetricComparatorType.SamePeriodPreviousYear,
18
+ const sps = measure?.comparators.filter((a) => a.comparator === AlertMetricComparatorType.SamePeriodPreviousYear) ?? [];
19
+ const pps = measure?.comparators.filter((a) => a.comparator === AlertMetricComparatorType.PreviousPeriod) ??
20
+ [];
21
+ const items = [];
22
+ // Iterate over SamePeriodPreviousYear comparators
23
+ sps.forEach((sp) => {
24
+ const pp = pps.find((pp) => pp.granularity === sp.granularity);
25
+ // Has granularity set and previous period is not year
26
+ if (sp.granularity) {
27
+ if (pp?.granularity === DateGranularity["year"]) {
28
+ items.push({
29
+ title: intl.formatMessage({ id: "insightAlert.config.compare_with_sp" }),
30
+ type: AlertMetricComparatorType.SamePeriodPreviousYear,
31
+ granularity: sp.granularity,
32
+ });
26
33
  }
27
- : {
34
+ else {
35
+ items.push({
36
+ title: intl.formatMessage({ id: "insightAlert.config.compare_with_sp_granularity" }, {
37
+ period: translateGranularity(intl, sp.granularity),
38
+ }),
39
+ type: AlertMetricComparatorType.SamePeriodPreviousYear,
40
+ granularity: sp.granularity,
41
+ });
42
+ }
43
+ }
44
+ else {
45
+ items.push({
28
46
  title: intl.formatMessage({ id: "insightAlert.config.compare_with_sp" }),
29
47
  type: AlertMetricComparatorType.SamePeriodPreviousYear,
30
- },
31
- pp?.granularity
32
- ? {
48
+ granularity: undefined,
49
+ });
50
+ }
51
+ });
52
+ // Iterate over PreviousPeriod comparators
53
+ pps.forEach((pp) => {
54
+ // Previous period has granularity set
55
+ if (pp.granularity) {
56
+ items.push({
33
57
  title: intl.formatMessage({ id: "insightAlert.config.compare_with_pp_granularity" }, {
34
58
  period: translateGranularity(intl, pp.granularity),
35
59
  }),
36
60
  type: AlertMetricComparatorType.PreviousPeriod,
37
- }
38
- : {
61
+ granularity: pp.granularity,
62
+ });
63
+ }
64
+ else {
65
+ items.push({
39
66
  title: intl.formatMessage({ id: "insightAlert.config.compare_with_pp" }),
40
67
  type: AlertMetricComparatorType.PreviousPeriod,
41
- },
42
- ];
68
+ granularity: undefined,
69
+ });
70
+ }
71
+ });
72
+ return items;
43
73
  }, [intl, measure?.comparators]);
44
74
  // If alert is not defined or the measure does not have any comparators, return null
45
75
  if (!alert || !isChangeOrDifferenceOperator(alert.alert)) {
@@ -62,13 +92,14 @@ export function AlertComparisonPeriodSelect({ alert, measure, overlayPositionTyp
62
92
  }, renderBody: ({ closeDropdown, ariaAttributes }) => {
63
93
  const listboxItems = comparisons.map((comparison) => ({
64
94
  type: "interactive",
65
- id: comparison.type.toString(),
95
+ id: `${comparison.type}-${comparison.granularity ?? "none"}`,
66
96
  stringTitle: comparison.title,
67
97
  data: comparison,
68
98
  }));
69
- return (_jsx(UiListbox, { shouldKeyboardActionStopPropagation: true, shouldKeyboardActionPreventDefault: true, dataTestId: "s-alert-comparison-select-list", items: listboxItems, selectedItemId: selectedComparison?.toString(), onSelect: (item) => {
70
- if (item.data.type !== selectedComparison) {
71
- onComparisonChange(item.data.type);
99
+ return (_jsx(UiListbox, { shouldKeyboardActionStopPropagation: true, shouldKeyboardActionPreventDefault: true, dataTestId: "s-alert-comparison-select-list", items: listboxItems, selectedItemId: `${selectedComparison ?? "none"}-${selectedGranularity ?? "none"}`, onSelect: (item) => {
100
+ if (item.data.type !== selectedComparison ||
101
+ item.data.granularity !== selectedGranularity) {
102
+ onComparisonChange(item.data.type, item.data.granularity);
72
103
  }
73
104
  }, onClose: closeDropdown, ariaAttributes: ariaAttributes, InteractiveItemComponent: ({ item, isSelected, onSelect, isFocused }) => {
74
105
  return (_jsx(SingleSelectListItem, { title: item.stringTitle, isSelected: isSelected, isFocused: isFocused, onClick: onSelect, className: "gd-alert-comparison-select__list-item" }));
@@ -2,10 +2,11 @@ import { type OverlayPositionType } from "@gooddata/sdk-ui-kit";
2
2
  import { type AlertMetric } from "../../types.js";
3
3
  export interface IAlertMetricSelectProps {
4
4
  id?: string;
5
+ disabled?: boolean;
5
6
  selectedMeasure: AlertMetric | undefined;
6
7
  onMeasureChange: (measure: AlertMetric) => void;
7
8
  measures: AlertMetric[];
8
9
  overlayPositionType?: OverlayPositionType;
9
10
  closeOnParentScroll?: boolean;
10
11
  }
11
- export declare function AlertMeasureSelect({ id, selectedMeasure, onMeasureChange, measures, overlayPositionType, closeOnParentScroll }: IAlertMetricSelectProps): import("react/jsx-runtime").JSX.Element;
12
+ export declare function AlertMeasureSelect({ id, disabled, selectedMeasure, onMeasureChange, measures, overlayPositionType, closeOnParentScroll }: IAlertMetricSelectProps): import("react/jsx-runtime").JSX.Element;
@@ -6,7 +6,7 @@ import { useIntl } from "react-intl";
6
6
  import { Dropdown, DropdownButton, SingleSelectListItem, UiListbox, } from "@gooddata/sdk-ui-kit";
7
7
  import { getMeasureTitle } from "../utils/getters.js";
8
8
  const measureIcon = _jsx("div", { className: "gd-alert-measure-select__icon gd-icon-metric" });
9
- export function AlertMeasureSelect({ id, selectedMeasure, onMeasureChange, measures, overlayPositionType, closeOnParentScroll, }) {
9
+ export function AlertMeasureSelect({ id, disabled, selectedMeasure, onMeasureChange, measures, overlayPositionType, closeOnParentScroll, }) {
10
10
  const intl = useIntl();
11
11
  const ref = useRef(null);
12
12
  const selectedMeasureTitle = selectedMeasure
@@ -15,7 +15,7 @@ export function AlertMeasureSelect({ id, selectedMeasure, onMeasureChange, measu
15
15
  return (_jsx(Dropdown, { closeOnParentScroll: closeOnParentScroll, overlayPositionType: overlayPositionType, autofocusOnOpen: true, renderButton: ({ isOpen, toggleDropdown, buttonRef, dropdownId }) => {
16
16
  return (_jsx("div", { ref: (item) => {
17
17
  ref.current = item;
18
- }, children: _jsx(DropdownButton, { id: id, className: cx("gd-alert-measure-select__button s-alert-measure-select"), value: selectedMeasureTitle, iconLeft: selectedMeasure ? "gd-icon-metric" : undefined, onClick: toggleDropdown, buttonRef: buttonRef, dropdownId: dropdownId, isOpen: isOpen, accessibilityConfig: {
18
+ }, children: _jsx(DropdownButton, { id: id, disabled: disabled, className: cx("gd-alert-measure-select__button s-alert-measure-select"), value: selectedMeasureTitle, iconLeft: selectedMeasure ? "gd-icon-metric" : undefined, onClick: toggleDropdown, buttonRef: buttonRef, dropdownId: dropdownId, isOpen: isOpen, accessibilityConfig: {
19
19
  ariaExpanded: isOpen,
20
20
  popupType: "listbox",
21
21
  } }) }));
@@ -1,6 +1,7 @@
1
1
  // (C) 2024-2026 GoodData Corporation
2
2
  import { useDashboardSelector } from "../../../../../model/react/DashboardStoreProvider.js";
3
3
  import { selectCatalogDateDatasets } from "../../../../../model/store/catalog/catalogSelectors.js";
4
+ import { selectEnableComparisonInAlerting } from "../../../../../model/store/config/configSelectors.js";
4
5
  import { selectInsightByWidgetRef } from "../../../../../model/store/insights/insightsSelectors.js";
5
6
  import { selectWidgetByRef } from "../../../../../model/store/tabs/layout/layoutSelectors.js";
6
7
  import { getAlertMeasure } from "../utils/getters.js";
@@ -8,10 +9,11 @@ import { getSupportedInsightMeasuresByInsight } from "../utils/items.js";
8
9
  export const useAlertValidation = (alert, isNewAlert) => {
9
10
  const widgetLocalId = alert?.metadata?.widget;
10
11
  const widgetRef = widgetLocalId ? { identifier: widgetLocalId } : undefined;
12
+ const canManageComparison = useDashboardSelector(selectEnableComparisonInAlerting);
11
13
  const widget = useDashboardSelector(selectWidgetByRef(widgetRef));
12
14
  const insight = useDashboardSelector(selectInsightByWidgetRef(widget?.ref));
13
15
  const dateDatasets = useDashboardSelector(selectCatalogDateDatasets);
14
- const supportedMeasures = getSupportedInsightMeasuresByInsight(insight, dateDatasets);
16
+ const supportedMeasures = getSupportedInsightMeasuresByInsight(insight, dateDatasets, canManageComparison, alert);
15
17
  const selectedMeasureExists = alert ? getAlertMeasure(supportedMeasures, alert.alert) : undefined;
16
18
  const isValid = isNewAlert || Boolean(!!widget && selectedMeasureExists);
17
19
  let invalidityReason = undefined;
@@ -1,4 +1,4 @@
1
- import { type FilterContextItem, type IAlertAnomalyDetectionGranularity, type IAlertAnomalyDetectionSensitivity, type IAlertComparisonOperator, type IAlertRelativeArithmeticOperator, type IAlertRelativeOperator, type IAlertTriggerInterval, type IAlertTriggerMode, type IAutomationMetadataObject, type IAutomationMetadataObjectDefinition, type IAutomationRecipient, type IAutomationVisibleFilter, type IInsight, type INotificationChannelIdentifier, type INotificationChannelMetadataObject } from "@gooddata/sdk-model";
1
+ import { type DateAttributeGranularity, type FilterContextItem, type IAlertAnomalyDetectionGranularity, type IAlertAnomalyDetectionSensitivity, type IAlertComparisonOperator, type IAlertRelativeArithmeticOperator, type IAlertRelativeOperator, type IAlertTriggerInterval, type IAlertTriggerMode, type IAutomationMetadataObject, type IAutomationMetadataObjectDefinition, type IAutomationRecipient, type IAutomationVisibleFilter, type IInsight, type INotificationChannelIdentifier, type INotificationChannelMetadataObject } from "@gooddata/sdk-model";
2
2
  import type { ExtendedDashboardWidget } from "../../../../../model/types/layoutTypes.js";
3
3
  import { type AlertAttribute, type AlertMetric, type AlertMetricComparatorType } from "../../types.js";
4
4
  export interface IUseEditAlertProps {
@@ -32,11 +32,12 @@ export declare function useEditAlert({ alertToEdit, notificationChannels, insigh
32
32
  onGranularityChange: (measure: AlertMetric | undefined, granularity: IAlertAnomalyDetectionGranularity) => void;
33
33
  onChange: (e: string | number, event?: import("react").ChangeEvent<HTMLInputElement> | undefined) => void;
34
34
  onBlur: (event: import("react").FocusEvent<HTMLInputElement, Element>) => void;
35
- onComparisonTypeChange: (measure: AlertMetric | undefined, relativeOperator: [IAlertRelativeOperator, IAlertRelativeArithmeticOperator] | undefined, comparisonType: AlertMetricComparatorType) => void;
35
+ onComparisonTypeChange: (measure: AlertMetric | undefined, relativeOperator: [IAlertRelativeOperator, IAlertRelativeArithmeticOperator] | undefined, comparisonType: AlertMetricComparatorType, granularity?: DateAttributeGranularity | undefined) => void;
36
36
  onDestinationChange: (destinationId: string) => void;
37
37
  onTriggerModeChange: (triggerMode: IAlertTriggerMode) => void;
38
38
  onTriggerIntervalChange: (triggerInterval: IAlertTriggerInterval, dirty?: boolean) => void;
39
39
  selectedMeasure: AlertMetric | undefined;
40
+ canChangeMeasure: boolean;
40
41
  supportedMeasures: AlertMetric[];
41
42
  canManageAttributes: boolean;
42
43
  selectedAttribute: AlertAttribute | undefined;
@@ -45,6 +46,7 @@ export declare function useEditAlert({ alertToEdit, notificationChannels, insigh
45
46
  catalogAttributes: import("@gooddata/sdk-model").ICatalogAttribute[];
46
47
  catalogDateDatasets: import("@gooddata/sdk-model").ICatalogDateDataset[];
47
48
  isResultLoading: boolean;
49
+ isInvalidConnectionToInsight: "" | boolean | undefined;
48
50
  selectedAiOperator: "AI.ANOMALY_DETECTION" | undefined;
49
51
  selectedSensitivity: IAlertAnomalyDetectionSensitivity | undefined;
50
52
  selectedGranularity: IAlertAnomalyDetectionGranularity | undefined;
@@ -47,6 +47,7 @@ export function useEditAlert({ alertToEdit, notificationChannels, insight, widge
47
47
  const commonDateFilterId = useDashboardSelector(selectAutomationCommonDateFilterId);
48
48
  const weekStart = useDashboardSelector(selectWeekStart);
49
49
  const timezone = useDashboardSelector(selectTimezone);
50
+ const isInvalidConnectionToInsight = alertToEdit?.metadata?.widget && !insight;
50
51
  const minimumRecurrenceMinutesEntitlement = useDashboardSelector(selectEntitlementMinimumRecurrenceMinutes);
51
52
  const allowHourlyRecurrence = parseInt(minimumRecurrenceMinutesEntitlement?.value ?? DEFAULT_MIN_RECURRENCE_MINUTES, 10) === 60;
52
53
  const widgetTabMap = useDashboardSelector(selectWidgetLocalIdToTabIdMap);
@@ -63,8 +64,8 @@ export function useEditAlert({ alertToEdit, notificationChannels, insight, widge
63
64
  const measureFormatMap = useMemo(() => {
64
65
  return getMeasureFormatsFromExecution(execResult?.executionResult);
65
66
  }, [execResult?.executionResult]);
66
- const supportedMeasures = useMemo(() => getSupportedInsightMeasuresByInsight(effectiveInsight, catalogDateDatasets, canManageComparison), [effectiveInsight, catalogDateDatasets, canManageComparison]);
67
- const supportedAttributes = useMemo(() => getSupportedInsightAttributesByInsight(insight, catalogDateDatasets), [insight, catalogDateDatasets]);
67
+ const supportedMeasures = useMemo(() => getSupportedInsightMeasuresByInsight(effectiveInsight, catalogDateDatasets, canManageComparison, alertToEdit), [effectiveInsight, catalogDateDatasets, canManageComparison, alertToEdit]);
68
+ const supportedAttributes = useMemo(() => getSupportedInsightAttributesByInsight(insight, catalogAttributes, catalogDateDatasets, alertToEdit), [insight, catalogDateDatasets, catalogAttributes, alertToEdit]);
68
69
  const { isResultLoading, getAttributeValues, getMetricValue } = useAttributeValuesFromExecResults(execResult);
69
70
  // Default values
70
71
  const defaultMeasure = supportedMeasures[0];
@@ -158,13 +159,13 @@ export function useEditAlert({ alertToEdit, notificationChannels, insight, widge
158
159
  setTriggerIntervalDirty(false);
159
160
  setEditedAutomation((alert) => transformAlertByAnomalyDetection(supportedMeasures, alert, measure, weekStart, timezone, enableAlertOncePerInterval));
160
161
  }, [supportedMeasures, weekStart, timezone, enableAlertOncePerInterval]);
161
- const onComparisonTypeChange = useCallback((measure, relativeOperator, comparisonType) => {
162
+ const onComparisonTypeChange = useCallback((measure, relativeOperator, comparisonType, granularity) => {
162
163
  if (!measure || !relativeOperator || !relativeOperator) {
163
164
  return;
164
165
  }
165
166
  const [relativeOperatorValue, arithmeticOperator] = relativeOperator;
166
167
  setEditedAutomation((alert) => alert
167
- ? transformAlertByRelativeOperator(supportedMeasures, alert, measure, relativeOperatorValue, arithmeticOperator, measureFormatMap, comparisonType)
168
+ ? transformAlertByRelativeOperator(supportedMeasures, alert, measure, relativeOperatorValue, arithmeticOperator, measureFormatMap, comparisonType, granularity)
168
169
  : undefined);
169
170
  }, [measureFormatMap, supportedMeasures]);
170
171
  const onSensitivityChange = useCallback((sensitivity) => {
@@ -283,7 +284,9 @@ export function useEditAlert({ alertToEdit, notificationChannels, insight, widge
283
284
  const hasValidThreshold = isAlertValueDefined(editedAutomation?.alert);
284
285
  const validationErrorMessage = isOriginalAutomationValid
285
286
  ? undefined
286
- : intl.formatMessage({ id: "insightAlert.config.invalidWidget" });
287
+ : isInvalidConnectionToInsight
288
+ ? intl.formatMessage({ id: "insightAlert.config.invalidWidget" })
289
+ : intl.formatMessage({ id: "insightAlert.config.unusedWidget" });
287
290
  const hasRecipients = (editedAutomation?.recipients?.length ?? 0) > 0;
288
291
  const hasValidExternalRecipients = allowExternalRecipients
289
292
  ? true
@@ -327,6 +330,7 @@ export function useEditAlert({ alertToEdit, notificationChannels, insight, widge
327
330
  onTriggerModeChange,
328
331
  onTriggerIntervalChange,
329
332
  selectedMeasure,
333
+ canChangeMeasure: !!insight,
330
334
  supportedMeasures,
331
335
  canManageAttributes,
332
336
  selectedAttribute,
@@ -335,6 +339,7 @@ export function useEditAlert({ alertToEdit, notificationChannels, insight, widge
335
339
  catalogAttributes,
336
340
  catalogDateDatasets,
337
341
  isResultLoading,
342
+ isInvalidConnectionToInsight,
338
343
  selectedAiOperator,
339
344
  selectedSensitivity,
340
345
  selectedGranularity,
@@ -212,7 +212,14 @@ export function getSelectedCatalogAttributeValue(attribute, getAttributeValue, s
212
212
  }
213
213
  const values = getAttributeValue(attribute);
214
214
  return (values.find((value) => value.name === selectedValue) ??
215
- values.find((value) => value.value === selectedValue));
215
+ values.find((value) => value.value === selectedValue) ??
216
+ (selectedValue
217
+ ? {
218
+ title: selectedValue,
219
+ value: selectedValue,
220
+ name: selectedValue,
221
+ }
222
+ : undefined));
216
223
  }
217
224
  /**
218
225
  * @internal
@@ -1,15 +1,18 @@
1
- import { type ICatalogDateDataset, type IInsight } from "@gooddata/sdk-model";
1
+ import { type IAutomationMetadataObject, type ICatalogAttribute, type ICatalogDateDataset, type IInsight } from "@gooddata/sdk-model";
2
2
  import { type AlertAttribute, type AlertMetric } from "../../types.js";
3
3
  /**
4
4
  * Get supported insight measures by insight
5
5
  * @param insight - insight to get supported measures for
6
6
  * @param dateDatasets - date datasets to filter out date attributes
7
7
  * @param canManageComparison - flag if user can manage comparison
8
+ * @param alert - alert to get supported measures for
8
9
  */
9
- export declare function getSupportedInsightMeasuresByInsight(insight: IInsight | null | undefined, dateDatasets?: ICatalogDateDataset[], canManageComparison?: boolean): AlertMetric[];
10
+ export declare function getSupportedInsightMeasuresByInsight(insight: IInsight | null | undefined, dateDatasets?: ICatalogDateDataset[], canManageComparison?: boolean, alert?: IAutomationMetadataObject): AlertMetric[];
10
11
  /**
11
12
  * Get supported insight attributes by insight
12
13
  * @param insight - insight to get supported attributes for
14
+ * @param attributes - attributes to filter out date attributes
13
15
  * @param dateDatasets - date datasets to filter out date attributes
16
+ * @param alert - alert metadata object
14
17
  */
15
- export declare function getSupportedInsightAttributesByInsight(insight: IInsight | null | undefined, dateDatasets?: ICatalogDateDataset[]): AlertAttribute[];
18
+ export declare function getSupportedInsightAttributesByInsight(insight: IInsight | null | undefined, attributes: ICatalogAttribute[], dateDatasets?: ICatalogDateDataset[], alert?: IAutomationMetadataObject): AlertAttribute[];