@bigbinary/neeto-rules-frontend 2.7.20 → 2.7.21

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 (87) hide show
  1. package/app/javascript/src/translations/ar.json +2 -1
  2. package/app/javascript/src/translations/bg.json +2 -1
  3. package/app/javascript/src/translations/ca.json +2 -1
  4. package/app/javascript/src/translations/cs.json +2 -1
  5. package/app/javascript/src/translations/da.json +2 -1
  6. package/app/javascript/src/translations/de.json +2 -1
  7. package/app/javascript/src/translations/es-MX.json +2 -1
  8. package/app/javascript/src/translations/es.json +2 -1
  9. package/app/javascript/src/translations/et.json +2 -1
  10. package/app/javascript/src/translations/fi.json +2 -1
  11. package/app/javascript/src/translations/fil.json +2 -1
  12. package/app/javascript/src/translations/fr.json +2 -1
  13. package/app/javascript/src/translations/he.json +2 -1
  14. package/app/javascript/src/translations/hi.json +2 -1
  15. package/app/javascript/src/translations/hr.json +2 -1
  16. package/app/javascript/src/translations/hu.json +2 -1
  17. package/app/javascript/src/translations/id.json +2 -1
  18. package/app/javascript/src/translations/it.json +2 -1
  19. package/app/javascript/src/translations/ja.json +2 -1
  20. package/app/javascript/src/translations/ko.json +2 -1
  21. package/app/javascript/src/translations/nl.json +2 -1
  22. package/app/javascript/src/translations/pl.json +2 -1
  23. package/app/javascript/src/translations/pt-BR.json +2 -1
  24. package/app/javascript/src/translations/pt.json +2 -1
  25. package/app/javascript/src/translations/ro.json +2 -1
  26. package/app/javascript/src/translations/ru.json +2 -1
  27. package/app/javascript/src/translations/sk.json +2 -1
  28. package/app/javascript/src/translations/sl.json +2 -1
  29. package/app/javascript/src/translations/sv.json +2 -1
  30. package/app/javascript/src/translations/th.json +2 -1
  31. package/app/javascript/src/translations/tr.json +2 -1
  32. package/app/javascript/src/translations/uk.json +2 -1
  33. package/app/javascript/src/translations/vi.json +2 -1
  34. package/app/javascript/src/translations/zh-CN.json +2 -1
  35. package/app/javascript/src/translations/zh-TW.json +2 -1
  36. package/dist/.ready +1 -1
  37. package/dist/NeetoRulesBuilder.js +30 -2
  38. package/dist/NeetoRulesBuilder.js.map +1 -1
  39. package/dist/NeetoRulesForm.js +2 -2
  40. package/dist/cjs/NeetoRulesBuilder.js +30 -2
  41. package/dist/cjs/NeetoRulesBuilder.js.map +1 -1
  42. package/dist/cjs/NeetoRulesForm.js +2 -2
  43. package/dist/cjs/index.js +2 -2
  44. package/dist/cjs/v2/NeetoRules.js +1 -1
  45. package/dist/cjs/v2/NeetoRulesBuilder.js +323 -141
  46. package/dist/cjs/v2/NeetoRulesBuilder.js.map +1 -1
  47. package/dist/cjs/v2/NeetoRulesForm.js +10 -8
  48. package/dist/cjs/v2/NeetoRulesForm.js.map +1 -1
  49. package/dist/cjs/v2/RulePreview.js +1 -1
  50. package/dist/cjs/v2/hooks.js +2 -2
  51. package/dist/cjs/v2/index.js +4 -4
  52. package/dist/{index-DMCgC01M.js → index-49edM85x.js} +2 -2
  53. package/dist/{index-DMCgC01M.js.map → index-49edM85x.js.map} +1 -1
  54. package/dist/{index-D_dEe2Gg.js → index-BZYwFjcS.js} +2 -2
  55. package/dist/{index-D_dEe2Gg.js.map → index-BZYwFjcS.js.map} +1 -1
  56. package/dist/{index-BXLCiV70.js → index-CcbW7H2S.js} +67 -21
  57. package/dist/index-CcbW7H2S.js.map +1 -0
  58. package/dist/{index-hvuc6OIP.js → index-Fukq1VSN.js} +69 -18
  59. package/dist/index-Fukq1VSN.js.map +1 -0
  60. package/dist/index.js +2 -2
  61. package/dist/keyboardShortcuts-DtnjcivY.js +96 -0
  62. package/dist/{keyboardShortcuts-lX0oq49M.js.map → keyboardShortcuts-DtnjcivY.js.map} +1 -1
  63. package/dist/keyboardShortcuts-_OrtgF0A.js +92 -0
  64. package/dist/{keyboardShortcuts-DXP5YgeG.js.map → keyboardShortcuts-_OrtgF0A.js.map} +1 -1
  65. package/dist/{useCustomDataStore-CU3KpyD4.js → useCustomDataStore-CEbhXugO.js} +2 -2
  66. package/dist/{useCustomDataStore-CU3KpyD4.js.map → useCustomDataStore-CEbhXugO.js.map} +1 -1
  67. package/dist/{useCustomDataStore-D9k8F5g-.js → useCustomDataStore-DPmWkEhs.js} +2 -2
  68. package/dist/{useCustomDataStore-D9k8F5g-.js.map → useCustomDataStore-DPmWkEhs.js.map} +1 -1
  69. package/dist/{useUtilityStore-BBb39TOj.js → useUtilityStore-BVCulMEC.js} +9 -2
  70. package/dist/useUtilityStore-BVCulMEC.js.map +1 -0
  71. package/dist/{useUtilityStore-DkxAexDw.js → useUtilityStore-gtPhFvpI.js} +9 -2
  72. package/dist/useUtilityStore-gtPhFvpI.js.map +1 -0
  73. package/dist/v2/NeetoRules.js +1 -1
  74. package/dist/v2/NeetoRulesBuilder.js +304 -141
  75. package/dist/v2/NeetoRulesBuilder.js.map +1 -1
  76. package/dist/v2/NeetoRulesForm.js +10 -8
  77. package/dist/v2/NeetoRulesForm.js.map +1 -1
  78. package/dist/v2/RulePreview.js +1 -1
  79. package/dist/v2/hooks.js +2 -2
  80. package/dist/v2/index.js +4 -4
  81. package/package.json +7 -7
  82. package/dist/index-BXLCiV70.js.map +0 -1
  83. package/dist/index-hvuc6OIP.js.map +0 -1
  84. package/dist/keyboardShortcuts-DXP5YgeG.js +0 -92
  85. package/dist/keyboardShortcuts-lX0oq49M.js +0 -96
  86. package/dist/useUtilityStore-BBb39TOj.js.map +0 -1
  87. package/dist/useUtilityStore-DkxAexDw.js.map +0 -1
@@ -2,7 +2,7 @@
2
2
 
3
3
  var _defineProperty = require('@babel/runtime/helpers/defineProperty');
4
4
  var _objectDestructuringEmpty = require('@babel/runtime/helpers/objectDestructuringEmpty');
5
- var keyboardShortcuts = require('../../keyboardShortcuts-lX0oq49M.js');
5
+ var keyboardShortcuts = require('../../keyboardShortcuts-DtnjcivY.js');
6
6
  var shallow = require('zustand/shallow');
7
7
  var react = require('react');
8
8
  var neetoAtoms = require('@bigbinary/neeto-atoms');
@@ -13,9 +13,9 @@ var neetoCist = require('@bigbinary/neeto-cist');
13
13
  var Container = require('@bigbinary/neeto-molecules/v2/Container');
14
14
  var Header = require('@bigbinary/neeto-molecules/v2/Header');
15
15
  var reactI18next = require('react-i18next');
16
- var index = require('../../index-hvuc6OIP.js');
17
- var useCustomDataStore = require('../../useCustomDataStore-D9k8F5g-.js');
18
- var useUtilityStore = require('../../useUtilityStore-DkxAexDw.js');
16
+ var index = require('../../index-Fukq1VSN.js');
17
+ var useCustomDataStore = require('../../useCustomDataStore-DPmWkEhs.js');
18
+ var useUtilityStore = require('../../useUtilityStore-gtPhFvpI.js');
19
19
  var _toConsumableArray = require('@babel/runtime/helpers/toConsumableArray');
20
20
  var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
21
21
  var Plus = require('@bigbinary/neeto-icons/Plus');
@@ -24,6 +24,7 @@ var Delete = require('@bigbinary/neeto-icons/Delete');
24
24
  var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
25
25
  var jsxRuntime = require('react/jsx-runtime');
26
26
  var i18next = require('i18next');
27
+ var yup = require('yup');
27
28
  var _asyncToGenerator = require('@babel/runtime/helpers/asyncToGenerator');
28
29
  var useHotkeys = require('@bigbinary/neeto-hotkeys');
29
30
  var Search = require('@bigbinary/neeto-icons/Search');
@@ -37,7 +38,6 @@ require('@tanstack/react-query');
37
38
  require('@bigbinary/neeto-commons-frontend/react-utils');
38
39
  require('axios');
39
40
  require('../../index-DFwPrb81.js');
40
- require('yup');
41
41
  require('lucide-react');
42
42
  require('@bigbinary/neeto-commons-frontend/v2/react-utils');
43
43
  require('@monaco-editor/react');
@@ -54,6 +54,25 @@ require('zustand');
54
54
  require('@bigbinary/neeto-commons-frontend/initializers');
55
55
  require('@bigbinary/neeto-commons-frontend/utils');
56
56
 
57
+ function _interopNamespaceDefault(e) {
58
+ var n = Object.create(null);
59
+ if (e) {
60
+ Object.keys(e).forEach(function (k) {
61
+ if (k !== 'default') {
62
+ var d = Object.getOwnPropertyDescriptor(e, k);
63
+ Object.defineProperty(n, k, d.get ? d : {
64
+ enumerable: true,
65
+ get: function () { return e[k]; }
66
+ });
67
+ }
68
+ });
69
+ }
70
+ n.default = e;
71
+ return Object.freeze(n);
72
+ }
73
+
74
+ var yup__namespace = /*#__PURE__*/_interopNamespaceDefault(yup);
75
+
57
76
  var _TOKEN_TO_PANEL_TYPE;
58
77
  var PARAM_KIND = {
59
78
  ENUM_SINGLE: "enum-single",
@@ -81,6 +100,22 @@ var TIME_UNITS = [{
81
100
  /* eslint-enable @bigbinary/neeto/neetocommons-tips */
82
101
 
83
102
  var LEAD_TIME_UNITS = TIME_UNITS;
103
+
104
+ // Mirrors the classic form's three-digit input cap; 999 days equals the
105
+ // backend's maximum lead time.
106
+ var MAX_LEAD_TIME_VALUE = 999;
107
+ var STEPPER_DELTA = {
108
+ INCREMENT: 1,
109
+ DECREMENT: -1
110
+ };
111
+ var STEPPER_MIN_VALUE = 1;
112
+ var DEFAULT_DELAY_VALUE = 10;
113
+
114
+ // Matches the token flash animation duration in sentence-builder.css.
115
+ var FLASH_DURATION_MS = 2200;
116
+
117
+ // Keeps an element out of the keyboard tab order (HTML tabindex sentinel).
118
+ var SKIP_TAB_INDEX = -1;
84
119
  var CONDITION_VERBS = {
85
120
  IS: "is",
86
121
  IS_NOT: "is_not",
@@ -178,7 +213,11 @@ var CONFIG_FOCUSABLE_SELECTOR = 'input:not([type="radio"]):not([type="checkbox"]
178
213
  var SMS_TYPES = ["sms", "smsToNumbers"];
179
214
  var RECIPIENT_FOCUSABLE_SELECTOR = 'input, textarea, [contenteditable="true"]';
180
215
  var SMS_TO_NUMBERS = "smsToNumbers";
181
- var SWAP_CHANNEL_ORDER = ["email", "sms", "slack", "webhook", "calendar_invite"];
216
+ var RECIPIENT_STORAGE_FIELDS = {
217
+ emails: "emails",
218
+ value: "value",
219
+ phoneNumbers: "phoneNumbers"
220
+ };
182
221
  var DEFAULT_DELAY_UNITS = TIME_UNITS;
183
222
 
184
223
  var useClearingHover = function useClearingHover() {
@@ -554,6 +593,25 @@ var partsToMinutes = function partsToMinutes(_ref19) {
554
593
  (Number(value) || 0) * (UNIT_MINUTES[unit] || 1)
555
594
  );
556
595
  };
596
+ var leadTimeExceedsMax = function leadTimeExceedsMax(minutes) {
597
+ return minutesToParts(minutes).value > MAX_LEAD_TIME_VALUE;
598
+ };
599
+ var leadTimeValidationSchema = function leadTimeValidationSchema(schema) {
600
+ var _schema$triggers2;
601
+ var fieldKey = schema === null || schema === void 0 || (_schema$triggers2 = schema.triggers) === null || _schema$triggers2 === void 0 || (_schema$triggers2 = _schema$triggers2.leadTime) === null || _schema$triggers2 === void 0 ? void 0 : _schema$triggers2.fieldKey;
602
+ if (!fieldKey) return yup__namespace.object();
603
+ return yup__namespace.object().shape(_defineProperty({}, fieldKey, yup__namespace.object().shape({
604
+ // yup 0.32 can only concat schemas of the same type, and the host's
605
+ // generic field schema for the lead time key is a string schema.
606
+ value: yup__namespace.string().test("max-lead-time", function () {
607
+ return i18next.t("neetoRules.validations.maxLeadTime", {
608
+ max: MAX_LEAD_TIME_VALUE
609
+ });
610
+ }, function (minutes) {
611
+ return !leadTimeExceedsMax(minutes);
612
+ })
613
+ })));
614
+ };
557
615
  var leadTimeLabel = function leadTimeLabel(minutes) {
558
616
  var _minutesToParts = minutesToParts(minutes),
559
617
  value = _minutesToParts.value,
@@ -576,6 +634,9 @@ var normalizeSchema = function normalizeSchema() {
576
634
  var joinTypeFor = function joinTypeFor(mode) {
577
635
  return mode === CONDITION_MODE.OR ? JOIN_TYPE.or : JOIN_TYPE.and;
578
636
  };
637
+ var recipientStorageField = function recipientStorageField(field) {
638
+ return field === RECIPIENT_STORAGE_FIELDS.emails ? RECIPIENT_STORAGE_FIELDS.emails : RECIPIENT_STORAGE_FIELDS.value;
639
+ };
579
640
  var modeFromJoinType = function modeFromJoinType(joinType) {
580
641
  return joinType === JOIN_TYPE.or ? CONDITION_MODE.OR : CONDITION_MODE.AND;
581
642
  };
@@ -591,15 +652,15 @@ var asArray = function asArray(maybeArray) {
591
652
  return Array.isArray(maybeArray) ? maybeArray : [];
592
653
  };
593
654
  var fieldKeysFromSchema = function fieldKeysFromSchema() {
594
- var _schema$triggers2, _schema$actor2, _schema$conditions3, _schema$actions3, _schema$delay, _schema$triggers3, _schema$scope2;
655
+ var _schema$triggers3, _schema$actor2, _schema$conditions3, _schema$actions3, _schema$delay, _schema$triggers4, _schema$scope2;
595
656
  var schema = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
596
657
  return {
597
- triggers: ((_schema$triggers2 = schema.triggers) === null || _schema$triggers2 === void 0 ? void 0 : _schema$triggers2.fieldKey) || "events",
658
+ triggers: ((_schema$triggers3 = schema.triggers) === null || _schema$triggers3 === void 0 ? void 0 : _schema$triggers3.fieldKey) || "events",
598
659
  performer: ((_schema$actor2 = schema.actor) === null || _schema$actor2 === void 0 ? void 0 : _schema$actor2.fieldKey) || "performer",
599
660
  conditions: ((_schema$conditions3 = schema.conditions) === null || _schema$conditions3 === void 0 ? void 0 : _schema$conditions3.fieldKey) || "conditions",
600
661
  actions: ((_schema$actions3 = schema.actions) === null || _schema$actions3 === void 0 ? void 0 : _schema$actions3.fieldKey) || "actions",
601
662
  delay: ((_schema$delay = schema.delay) === null || _schema$delay === void 0 ? void 0 : _schema$delay.fieldKey) || "execution_delay",
602
- leadTime: (_schema$triggers3 = schema.triggers) === null || _schema$triggers3 === void 0 || (_schema$triggers3 = _schema$triggers3.leadTime) === null || _schema$triggers3 === void 0 ? void 0 : _schema$triggers3.fieldKey,
663
+ leadTime: (_schema$triggers4 = schema.triggers) === null || _schema$triggers4 === void 0 || (_schema$triggers4 = _schema$triggers4.leadTime) === null || _schema$triggers4 === void 0 ? void 0 : _schema$triggers4.fieldKey,
603
664
  scope: ((_schema$scope2 = schema.scope) === null || _schema$scope2 === void 0 ? void 0 : _schema$scope2.fieldKey) || "meetingIds",
604
665
  status: "status",
605
666
  name: "name"
@@ -752,6 +813,35 @@ var removeArrayItem = function removeArrayItem(_ref30) {
752
813
  }
753
814
  return result;
754
815
  };
816
+ var clearArrayItems = function clearArrayItems(_ref31) {
817
+ var values = _ref31.values,
818
+ fieldKey = _ref31.fieldKey;
819
+ var list = asArray(valueAt({
820
+ values: values,
821
+ key: fieldKey
822
+ }));
823
+ var removed = list.filter(function (item) {
824
+ return item === null || item === void 0 ? void 0 : item.id;
825
+ }).map(function (item) {
826
+ return _objectSpread$b(_objectSpread$b({}, item), {}, {
827
+ _destroy: true
828
+ });
829
+ });
830
+ var result = [{
831
+ path: "".concat(fieldKey, ".value"),
832
+ value: []
833
+ }];
834
+ if (removed.length) {
835
+ var _values$fieldKey2;
836
+ // eslint-disable-next-line security/detect-object-injection
837
+ var deleted = asArray(values === null || values === void 0 || (_values$fieldKey2 = values[fieldKey]) === null || _values$fieldKey2 === void 0 ? void 0 : _values$fieldKey2.deleted);
838
+ result.push({
839
+ path: "".concat(fieldKey, ".deleted"),
840
+ value: [].concat(_toConsumableArray(deleted), _toConsumableArray(removed))
841
+ });
842
+ }
843
+ return result;
844
+ };
755
845
 
756
846
  function ownKeys$a(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
757
847
  function _objectSpread$a(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$a(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$a(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -1031,6 +1121,7 @@ var MetaBlock = function MetaBlock(_ref) {
1031
1121
  children: t("neetoRules.builder.automationName")
1032
1122
  }), /*#__PURE__*/jsxRuntime.jsxs("div", {
1033
1123
  ref: nameRef,
1124
+ "data-testid": "rule-name-edit",
1034
1125
  role: handleEditName ? "button" : undefined,
1035
1126
  tabIndex: handleEditName ? 0 : undefined,
1036
1127
  className: classNames("mb-meta-input-wrap", {
@@ -1039,6 +1130,7 @@ var MetaBlock = function MetaBlock(_ref) {
1039
1130
  onClick: handleEditName,
1040
1131
  children: [/*#__PURE__*/jsxRuntime.jsx("span", {
1041
1132
  className: "mb-meta-name-display",
1133
+ "data-testid": "rule-name",
1042
1134
  children: rule.name
1043
1135
  }), /*#__PURE__*/jsxRuntime.jsx("span", {
1044
1136
  "aria-hidden": true,
@@ -1060,8 +1152,10 @@ var MetaBlock = function MetaBlock(_ref) {
1060
1152
  })]
1061
1153
  }), /*#__PURE__*/jsxRuntime.jsxs("label", {
1062
1154
  className: "mb-meta-active",
1155
+ "data-testid": "rule-active-label",
1063
1156
  children: [/*#__PURE__*/jsxRuntime.jsx("input", {
1064
1157
  checked: rule.active,
1158
+ "data-testid": "rule-active-checkbox",
1065
1159
  readOnly: !handleToggleActive,
1066
1160
  type: "checkbox",
1067
1161
  onChange: handleToggleActive || undefined
@@ -1319,6 +1413,8 @@ var ActionRow = function ActionRow(_ref) {
1319
1413
  });
1320
1414
  var configLabel = mediumLabel || sentencify(option === null || option === void 0 ? void 0 : option.placeholder);
1321
1415
  var isConfigured = neetoCist.isNotEmpty(action === null || action === void 0 ? void 0 : action.metadata);
1416
+ // A host that shows no value label still needs the token to read as filled.
1417
+ var configState = configValue || isConfigured ? TOKEN_STATE.FILLED : TOKEN_STATE.MUTED;
1322
1418
  var _useClearingHover = useClearingHover(),
1323
1419
  clearing = _useClearingHover.clearing,
1324
1420
  clearingHoverProps = _useClearingHover.clearingHoverProps;
@@ -1357,7 +1453,7 @@ var ActionRow = function ActionRow(_ref) {
1357
1453
  children: verbLabel
1358
1454
  }), hasParamTail ? /*#__PURE__*/jsxRuntime.jsx(Token, {
1359
1455
  dataTestid: "action-config-value-".concat(index),
1360
- state: configValue ? TOKEN_STATE.FILLED : TOKEN_STATE.MUTED,
1456
+ state: configState,
1361
1457
  active: isActive({
1362
1458
  type: BUILDER_ANCHORS.ACTION_CONFIG,
1363
1459
  index: index
@@ -1405,7 +1501,7 @@ var ActionRow = function ActionRow(_ref) {
1405
1501
  index: index
1406
1502
  }),
1407
1503
  dataTestid: "action-config-value-".concat(index),
1408
- state: configValue ? TOKEN_STATE.FILLED : TOKEN_STATE.MUTED,
1504
+ state: configState,
1409
1505
  invalid: isInvalid({
1410
1506
  type: BUILDER_ANCHORS.ACTION_CONFIG,
1411
1507
  index: index
@@ -1628,6 +1724,10 @@ var WhenBlock = function WhenBlock(_ref) {
1628
1724
  type: BUILDER_ANCHORS.TRIGGER_LEAD_TIME,
1629
1725
  index: index
1630
1726
  }),
1727
+ invalid: isInvalid({
1728
+ type: BUILDER_ANCHORS.TRIGGER_LEAD_TIME,
1729
+ index: index
1730
+ }),
1631
1731
  onClick: handleToken && function () {
1632
1732
  return handleToken({
1633
1733
  type: BUILDER_ANCHORS.TRIGGER_LEAD_TIME,
@@ -1838,6 +1938,24 @@ var useRuleModel = function useRuleModel(schema) {
1838
1938
  fieldKey: fieldKeysFromSchema(schema).actions,
1839
1939
  index: index
1840
1940
  }));
1941
+ },
1942
+ clearTriggers: function clearTriggers() {
1943
+ return applyAll(clearArrayItems({
1944
+ values: values,
1945
+ fieldKey: fieldKeysFromSchema(schema).triggers
1946
+ }));
1947
+ },
1948
+ clearConditions: function clearConditions() {
1949
+ return applyAll(clearArrayItems({
1950
+ values: values,
1951
+ fieldKey: fieldKeysFromSchema(schema).conditions
1952
+ }));
1953
+ },
1954
+ clearActions: function clearActions() {
1955
+ return applyAll(clearArrayItems({
1956
+ values: values,
1957
+ fieldKey: fieldKeysFromSchema(schema).actions
1958
+ }));
1841
1959
  }
1842
1960
  };
1843
1961
  };
@@ -1984,9 +2102,9 @@ var nextTriggers = function nextTriggers(_ref10) {
1984
2102
  name: value
1985
2103
  }]);
1986
2104
  }
1987
- return [{
2105
+ return [_objectSpread$4(_objectSpread$4({}, rule.triggers[0]), {}, {
1988
2106
  name: value
1989
- }];
2107
+ })];
1990
2108
  };
1991
2109
  var nextConditionAt = function nextConditionAt(_ref11) {
1992
2110
  var _field$allowMatching;
@@ -2233,7 +2351,7 @@ var descriptorFor = function descriptorFor(_ref15) {
2233
2351
  case BUILDER_ANCHORS.ACTION_ADD:
2234
2352
  case BUILDER_ANCHORS.ACTION_CHANNEL:
2235
2353
  {
2236
- var _schema$actions11, _schema$actions12, _channelOf;
2354
+ var _schema$actions11, _channelOf;
2237
2355
  var isSwap = anchor === BUILDER_ANCHORS.ACTION_CHANNEL;
2238
2356
  var channelMetaFor = function channelMetaFor(value) {
2239
2357
  var _schema$actions10;
@@ -2242,13 +2360,10 @@ var descriptorFor = function descriptorFor(_ref15) {
2242
2360
  ((_schema$actions10 = schema.actions) === null || _schema$actions10 === void 0 || (_schema$actions10 = _schema$actions10.channelMeta) === null || _schema$actions10 === void 0 ? void 0 : _schema$actions10[value]) || {}
2243
2361
  );
2244
2362
  };
2245
- var rank = function rank(value) {
2246
- var idx = SWAP_CHANNEL_ORDER.indexOf(value);
2247
- return idx === -1 ? SWAP_CHANNEL_ORDER.length : idx;
2248
- };
2249
- var sourceOptions = isSwap ? _toConsumableArray(((_schema$actions11 = schema.actions) === null || _schema$actions11 === void 0 ? void 0 : _schema$actions11.options) || []).sort(function (optionA, optionB) {
2250
- return rank(optionValue(optionA)) - rank(optionValue(optionB));
2251
- }) : ((_schema$actions12 = schema.actions) === null || _schema$actions12 === void 0 ? void 0 : _schema$actions12.options) || [];
2363
+
2364
+ // Both panes list the host's actions in the order it declared them;
2365
+ // re-ranking only the swap pane made the two disagree.
2366
+ var sourceOptions = ((_schema$actions11 = schema.actions) === null || _schema$actions11 === void 0 ? void 0 : _schema$actions11.options) || [];
2252
2367
  return {
2253
2368
  title: isSwap ? i18next.t("neetoRules.builder.panes.actionSwapTitle") : i18next.t("neetoRules.builder.panes.actionAddTitle"),
2254
2369
  subtitle: isSwap ? i18next.t("neetoRules.builder.panes.actionSwapSubtitle") : i18next.t("neetoRules.builder.panes.actionAddSubtitle"),
@@ -2278,7 +2393,7 @@ var descriptorFor = function descriptorFor(_ref15) {
2278
2393
  }
2279
2394
  case BUILDER_ANCHORS.ACTION_RECIPIENT:
2280
2395
  {
2281
- var _schema$actions13;
2396
+ var _schema$actions12;
2282
2397
  var action = actionAt({
2283
2398
  rule: rule,
2284
2399
  index: index
@@ -2288,7 +2403,7 @@ var descriptorFor = function descriptorFor(_ref15) {
2288
2403
  action: action
2289
2404
  });
2290
2405
  // eslint-disable-next-line security/detect-object-injection
2291
- var meta = ((_schema$actions13 = schema.actions) === null || _schema$actions13 === void 0 || (_schema$actions13 = _schema$actions13.channelMeta) === null || _schema$actions13 === void 0 ? void 0 : _schema$actions13[channel]) || {};
2406
+ var meta = ((_schema$actions12 = schema.actions) === null || _schema$actions12 === void 0 || (_schema$actions12 = _schema$actions12.channelMeta) === null || _schema$actions12 === void 0 ? void 0 : _schema$actions12[channel]) || {};
2292
2407
  var option = findActionOption({
2293
2408
  schema: schema,
2294
2409
  name: channel
@@ -2404,9 +2519,9 @@ var commitFor = function commitFor(_ref16) {
2404
2519
  }]));
2405
2520
  return "stay";
2406
2521
  }
2407
- setTriggers([{
2522
+ setTriggers([_objectSpread$4(_objectSpread$4({}, rule.triggers[0]), {}, {
2408
2523
  name: value
2409
- }]);
2524
+ })]);
2410
2525
  var leadTime = (_schema$triggers5 = schema.triggers) === null || _schema$triggers5 === void 0 ? void 0 : _schema$triggers5.leadTime;
2411
2526
  if (leadTime !== null && leadTime !== void 0 && leadTime.fieldKey && (leadTime.appliesTo || []).includes(value) && !rule.leadTime) {
2412
2527
  var _leadTime$default;
@@ -2673,7 +2788,7 @@ var applyPreview = function applyPreview(_ref19) {
2673
2788
  function ownKeys$3(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2674
2789
  function _objectSpread$3(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$3(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$3(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
2675
2790
  var Canvas = function Canvas(_ref) {
2676
- var _schema$scope, _schema$scope2, _schema$conditions, _schema$triggers, _schema$delay, _schema$meta$showName, _schema$meta;
2791
+ var _schema$scope, _schema$conditions, _schema$triggers, _schema$delay, _schema$meta$showName, _schema$meta;
2677
2792
  var schema = _ref.schema;
2678
2793
  var _useTranslation = reactI18next.useTranslation(),
2679
2794
  t = _useTranslation.t;
@@ -2686,15 +2801,16 @@ var Canvas = function Canvas(_ref) {
2686
2801
  var _useRuleModel = useRuleModel(schema),
2687
2802
  rule = _useRuleModel.rule,
2688
2803
  fieldKeys = _useRuleModel.fieldKeys,
2689
- setTriggers = _useRuleModel.setTriggers,
2690
2804
  setPerformer = _useRuleModel.setPerformer,
2691
2805
  setConditions = _useRuleModel.setConditions,
2692
- setActions = _useRuleModel.setActions,
2693
2806
  setDelay = _useRuleModel.setDelay,
2694
2807
  removeCondition = _useRuleModel.removeCondition,
2695
2808
  removeAction = _useRuleModel.removeAction,
2696
2809
  setActive = _useRuleModel.setActive,
2697
- setScope = _useRuleModel.setScope;
2810
+ setScope = _useRuleModel.setScope,
2811
+ clearTriggers = _useRuleModel.clearTriggers,
2812
+ clearConditions = _useRuleModel.clearConditions,
2813
+ clearActions = _useRuleModel.clearActions;
2698
2814
  var _useUtilityStore = useUtilityStore.useUtilityStore(shallow.useShallow(function (store) {
2699
2815
  return {
2700
2816
  builderPreview: store["builderPreview"],
@@ -2739,7 +2855,7 @@ var Canvas = function Canvas(_ref) {
2739
2855
  setFlashSeq(submitCount);
2740
2856
  var timer = setTimeout(function () {
2741
2857
  return setFlashSeq(0);
2742
- }, 2200);
2858
+ }, FLASH_DURATION_MS);
2743
2859
  return function () {
2744
2860
  return clearTimeout(timer);
2745
2861
  };
@@ -2756,7 +2872,7 @@ var Canvas = function Canvas(_ref) {
2756
2872
  });
2757
2873
  };
2758
2874
  var matchesInvalid = function matchesInvalid(_ref4) {
2759
- var _schema$actions, _schema$actions$actio, _schema$actions2, _schema$actions2$rend, _schema$actions3, _schema$actions3$rend, _schema$actions4, _schema$actions4$opti, _schema$actions5, _schema$actions5$opti;
2875
+ var _schema$actions, _schema$actions$actio, _schema$actions2, _schema$actions2$rend, _schema$actions3, _schema$actions3$rend, _schema$actions4, _schema$actions4$opti, _schema$actions5, _schema$actions5$opti, _schema$actions6, _schema$actions7;
2760
2876
  var type = _ref4.type,
2761
2877
  index = _ref4.index;
2762
2878
  var conditionMeta = "".concat(fieldKeys.conditions, ".value.").concat(index, ".metadata");
@@ -2776,7 +2892,13 @@ var Canvas = function Canvas(_ref) {
2776
2892
  action: actionItem
2777
2893
  }));
2778
2894
  var flatOption = ((_schema$actions4 = schema.actions) === null || _schema$actions4 === void 0 || (_schema$actions4$opti = _schema$actions4.optionForName) === null || _schema$actions4$opti === void 0 ? void 0 : _schema$actions4$opti.call(_schema$actions4, actionItem.name)) || ((_schema$actions5 = schema.actions) === null || _schema$actions5 === void 0 || (_schema$actions5$opti = _schema$actions5.optionForChannel) === null || _schema$actions5$opti === void 0 ? void 0 : _schema$actions5$opti.call(_schema$actions5, channel));
2779
- var recipientFields = [].concat(_toConsumableArray(hasRecipientEditor || hasRecipientField ? ["emails"] : []), _toConsumableArray((flatOption === null || flatOption === void 0 ? void 0 : flatOption.type) === "smsToNumbers" ? ["phoneNumbers"] : []));
2895
+ var recipientConfig = [].concat(_toConsumableArray((((_schema$actions6 = schema.actions) === null || _schema$actions6 === void 0 ? void 0 : _schema$actions6.options) || []).flatMap(function (option) {
2896
+ return option.recipients || [];
2897
+ })), _toConsumableArray(((_schema$actions7 = schema.actions) === null || _schema$actions7 === void 0 ? void 0 : _schema$actions7.recipients) || [])).find(function (item) {
2898
+ var _item$value;
2899
+ return ((_item$value = item.value) !== null && _item$value !== void 0 ? _item$value : item.id) === actionItem.recipient;
2900
+ });
2901
+ var recipientFields = [].concat(_toConsumableArray(hasRecipientEditor || hasRecipientField ? [RECIPIENT_STORAGE_FIELDS.emails] : []), _toConsumableArray(recipientConfig !== null && recipientConfig !== void 0 && recipientConfig.field ? [recipientStorageField(recipientConfig.field)] : []), _toConsumableArray((flatOption === null || flatOption === void 0 ? void 0 : flatOption.type) === SMS_TO_NUMBERS ? [RECIPIENT_STORAGE_FIELDS.phoneNumbers] : []));
2780
2902
  switch (type) {
2781
2903
  case BUILDER_ANCHORS.TRIGGER_ACTOR:
2782
2904
  {
@@ -2786,6 +2908,10 @@ var Canvas = function Canvas(_ref) {
2786
2908
  prefix: "".concat(actorKey, ".value")
2787
2909
  });
2788
2910
  }
2911
+ case BUILDER_ANCHORS.TRIGGER_LEAD_TIME:
2912
+ return hasErrorAt({
2913
+ prefix: "".concat(fieldKeys.leadTime, ".value")
2914
+ });
2789
2915
  case BUILDER_ANCHORS.COND_FIELD:
2790
2916
  return hasErrorAt({
2791
2917
  prefix: "".concat(fieldKeys.conditions, ".value.").concat(index, ".field")
@@ -2806,7 +2932,9 @@ var Canvas = function Canvas(_ref) {
2806
2932
  exclude: recipientFields
2807
2933
  });
2808
2934
  case BUILDER_ANCHORS.ACTION_RECIPIENT:
2809
- return recipientFields.some(function (field) {
2935
+ return hasErrorAt({
2936
+ prefix: "".concat(fieldKeys.actions, ".value.").concat(index, ".name")
2937
+ }) || recipientFields.some(function (field) {
2810
2938
  return hasErrorAt({
2811
2939
  prefix: "".concat(actionMeta, ".").concat(field)
2812
2940
  });
@@ -2884,8 +3012,6 @@ var Canvas = function Canvas(_ref) {
2884
3012
  return (action === null || action === void 0 ? void 0 : action.channel) || (action === null || action === void 0 ? void 0 : action.name);
2885
3013
  });
2886
3014
  var isScopeEnabled = (schema === null || schema === void 0 || (_schema$scope = schema.scope) === null || _schema$scope === void 0 ? void 0 : _schema$scope.enabled) && isTriggerSet && hasAction;
2887
- var isScopeComplete = !(schema !== null && schema !== void 0 && (_schema$scope2 = schema.scope) !== null && _schema$scope2 !== void 0 && _schema$scope2.enabled) || neetoCist.isNotEmpty(rule.scope || []);
2888
- var isComplete = isRuleComplete(rule) && isScopeComplete;
2889
3015
  var handleRemoveScope = function handleRemoveScope(index) {
2890
3016
  closePaneIfTargeting({
2891
3017
  anchors: [BUILDER_ANCHORS.SCOPE_PHONE_FIELD],
@@ -2919,12 +3045,12 @@ var Canvas = function Canvas(_ref) {
2919
3045
  };
2920
3046
  var handleClearTrigger = function handleClearTrigger() {
2921
3047
  var _schema$actor2;
2922
- setTriggers([]);
3048
+ clearTriggers();
2923
3049
  // Hosts without an actor pane can never re-set it, and null fails its validation.
2924
3050
  if ((_schema$actor2 = schema.actor) !== null && _schema$actor2 !== void 0 && _schema$actor2.enabled) setPerformer(null);
2925
- setConditions([]);
3051
+ clearConditions();
2926
3052
  setDelay(null);
2927
- setActions([]);
3053
+ clearActions();
2928
3054
  setScope([]);
2929
3055
  closePane();
2930
3056
  };
@@ -2946,7 +3072,7 @@ var Canvas = function Canvas(_ref) {
2946
3072
  );
2947
3073
  };
2948
3074
  return /*#__PURE__*/jsxRuntime.jsx("div", {
2949
- className: "neeto-rules-builder",
3075
+ className: "neeto-rules-builder nrb-atoms",
2950
3076
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
2951
3077
  className: "mb-canvas neeto-rules-main-card",
2952
3078
  children: [/*#__PURE__*/jsxRuntime.jsx(WhenBlock, {
@@ -2988,6 +3114,7 @@ var Canvas = function Canvas(_ref) {
2988
3114
  }) : /*#__PURE__*/jsxRuntime.jsx("div", {
2989
3115
  className: "mb-add-row",
2990
3116
  children: /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Button, {
3117
+ "data-testid": "rule-delay-add",
2991
3118
  icon: Plus,
2992
3119
  iconPosition: "left",
2993
3120
  label: t("neetoRules.common.addDelay"),
@@ -3019,24 +3146,22 @@ var Canvas = function Canvas(_ref) {
3019
3146
  onRemoveScope: handleRemoveScope,
3020
3147
  onToken: openPane
3021
3148
  })]
3022
- }), isComplete && /*#__PURE__*/jsxRuntime.jsxs(react.Fragment, {
3023
- children: [/*#__PURE__*/jsxRuntime.jsx(Connector, {
3024
- active: true
3025
- }), /*#__PURE__*/jsxRuntime.jsx(MetaBlock, {
3026
- rule: rule,
3027
- isInvalid: isInvalid({
3149
+ }), /*#__PURE__*/jsxRuntime.jsx(Connector, {
3150
+ active: true
3151
+ }), /*#__PURE__*/jsxRuntime.jsx(MetaBlock, {
3152
+ rule: rule,
3153
+ isInvalid: isInvalid({
3154
+ type: BUILDER_ANCHORS.META_NAME
3155
+ }),
3156
+ showName: (_schema$meta$showName = schema === null || schema === void 0 || (_schema$meta = schema.meta) === null || _schema$meta === void 0 ? void 0 : _schema$meta.showName) !== null && _schema$meta$showName !== void 0 ? _schema$meta$showName : true,
3157
+ onEditName: function onEditName() {
3158
+ return openPane({
3028
3159
  type: BUILDER_ANCHORS.META_NAME
3029
- }),
3030
- showName: (_schema$meta$showName = schema === null || schema === void 0 || (_schema$meta = schema.meta) === null || _schema$meta === void 0 ? void 0 : _schema$meta.showName) !== null && _schema$meta$showName !== void 0 ? _schema$meta$showName : true,
3031
- onEditName: function onEditName() {
3032
- return openPane({
3033
- type: BUILDER_ANCHORS.META_NAME
3034
- });
3035
- },
3036
- onToggleActive: function onToggleActive(event) {
3037
- return setActive(event.target.checked);
3038
- }
3039
- })]
3160
+ });
3161
+ },
3162
+ onToggleActive: function onToggleActive(event) {
3163
+ return setActive(event.target.checked);
3164
+ }
3040
3165
  })]
3041
3166
  })
3042
3167
  });
@@ -3047,6 +3172,7 @@ var ACTION_CONFIG_TYPES = {
3047
3172
  multiSelect: "multiSelect",
3048
3173
  dropdown: "dropdown",
3049
3174
  note: "note",
3175
+ list: "list",
3050
3176
  textarea: "textarea",
3051
3177
  number: "number",
3052
3178
  text: "text",
@@ -3108,6 +3234,7 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3108
3234
  };
3109
3235
  var noMatches = /*#__PURE__*/jsxRuntime.jsx("div", {
3110
3236
  className: "mb-pane-empty",
3237
+ "data-testid": "pane-no-matches",
3111
3238
  children: t("neetoRules.builder.noMatches")
3112
3239
  });
3113
3240
  if (type === ACTION_CONFIG_TYPES.emailChannel) {
@@ -3116,7 +3243,7 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3116
3243
  var bodyPath = "".concat(name, ".body");
3117
3244
  return /*#__PURE__*/jsxRuntime.jsxs("div", {
3118
3245
  className: "flex flex-col gap-4",
3119
- children: [/*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Input, {
3246
+ children: [!(option !== null && option !== void 0 && option.hideSubject) && /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Input, {
3120
3247
  required: true,
3121
3248
  error: fieldError(subjectPath),
3122
3249
  label: t("neetoRules.builder.config.subject"),
@@ -3149,7 +3276,7 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3149
3276
  setFieldValue(valuePath, current.includes(value) ? current.filter(neetoCist.isNot(value)) : [].concat(_toConsumableArray(current), [value]));
3150
3277
  };
3151
3278
  return /*#__PURE__*/jsxRuntime.jsxs("div", {
3152
- className: "flex flex-col gap-y-1",
3279
+ className: "mb-pane-list flex flex-col",
3153
3280
  children: [filtered.map(function (item) {
3154
3281
  return /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Checkbox, {
3155
3282
  checked: current.includes(item.value),
@@ -3191,6 +3318,14 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3191
3318
  })
3192
3319
  });
3193
3320
  }
3321
+ if (type === ACTION_CONFIG_TYPES.list) {
3322
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
3323
+ className: "flex flex-col gap-2",
3324
+ children: [/*#__PURE__*/jsxRuntime.jsx(index.ListField, {
3325
+ name: valuePath
3326
+ }), hintBlock]
3327
+ });
3328
+ }
3194
3329
  if (type === ACTION_CONFIG_TYPES.note) {
3195
3330
  var _action$metadata2;
3196
3331
  return /*#__PURE__*/jsxRuntime.jsxs("div", {
@@ -3436,7 +3571,7 @@ var ConfigPane = function ConfigPane(_ref) {
3436
3571
  while (1) switch (_context.n) {
3437
3572
  case 0:
3438
3573
  _context.n = 1;
3439
- return validate(["emails"]);
3574
+ return validate([RECIPIENT_STORAGE_FIELDS.emails]);
3440
3575
  case 1:
3441
3576
  if (_context.v) {
3442
3577
  _context.n = 2;
@@ -3465,7 +3600,6 @@ var ConfigPane = function ConfigPane(_ref) {
3465
3600
  };
3466
3601
  }();
3467
3602
  useHotkeys(keyboardShortcuts.SHORTCUTS["continue"].sequence, handleContinue, {
3468
- enabled: shouldAdvanceToRecipient,
3469
3603
  mode: "global"
3470
3604
  });
3471
3605
  return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
@@ -3473,6 +3607,7 @@ var ConfigPane = function ConfigPane(_ref) {
3473
3607
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
3474
3608
  className: "flex w-full flex-col gap-y-2",
3475
3609
  children: [/*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
3610
+ "data-testid": "pane-title",
3476
3611
  variant: "h5",
3477
3612
  children: title
3478
3613
  }), configSubtitle && /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
@@ -3482,6 +3617,7 @@ var ConfigPane = function ConfigPane(_ref) {
3482
3617
  }), showSearch && /*#__PURE__*/jsxRuntime.jsx("div", {
3483
3618
  className: "mb-pane-search",
3484
3619
  children: /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Input, {
3620
+ dataTestid: "pane-search",
3485
3621
  placeholder: t("neetoRules.builder.searchPlaceholder"),
3486
3622
  prefix: /*#__PURE__*/jsxRuntime.jsx(Search, {}),
3487
3623
  size: "large",
@@ -3501,20 +3637,14 @@ var ConfigPane = function ConfigPane(_ref) {
3501
3637
  message: disabledReason,
3502
3638
  selectedField: flatOption
3503
3639
  })
3504
- }), isSms ? /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
3505
- children: [/*#__PURE__*/jsxRuntime.jsx(index.SmsFields, {
3506
- action: action,
3507
- index: index$1,
3508
- name: name,
3509
- shouldAutoFocus: true,
3510
- selectedField: flatOption,
3511
- showPhoneNumberInput: false
3512
- }), /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
3513
- className: "mt-2",
3514
- color: "muted",
3515
- variant: "body3",
3516
- children: t("neetoRules.builder.config.smsSegmentHint")
3517
- })]
3640
+ }), isSms ? /*#__PURE__*/jsxRuntime.jsx(index.SmsFields, {
3641
+ action: action,
3642
+ index: index$1,
3643
+ name: name,
3644
+ shouldAutoFocus: true,
3645
+ hint: t("neetoRules.builder.config.smsSegmentHint"),
3646
+ selectedField: flatOption,
3647
+ showPhoneNumberInput: false
3518
3648
  }) : (_hostRenderConfig = hostRenderConfig === null || hostRenderConfig === void 0 ? void 0 : hostRenderConfig({
3519
3649
  name: name,
3520
3650
  action: action,
@@ -3527,8 +3657,9 @@ var ConfigPane = function ConfigPane(_ref) {
3527
3657
  option: flatOption
3528
3658
  })]
3529
3659
  })
3530
- }), shouldAdvanceToRecipient && /*#__PURE__*/jsxRuntime.jsx(index.Panel.Footer, {
3660
+ }), /*#__PURE__*/jsxRuntime.jsx(index.Panel.Footer, {
3531
3661
  children: /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Button, {
3662
+ "data-testid": "continue-button",
3532
3663
  label: t("neetoRules.buttons.continue"),
3533
3664
  size: "lg",
3534
3665
  onClick: handleContinue
@@ -3545,7 +3676,9 @@ var DelayPane = function DelayPane(_ref) {
3545
3676
  var _useRuleModel = useRuleModel(schema),
3546
3677
  fieldKeys = _useRuleModel.fieldKeys;
3547
3678
  var _useFormikContext = formik.useFormikContext(),
3548
- setFieldValue = _useFormikContext.setFieldValue;
3679
+ setFieldValue = _useFormikContext.setFieldValue,
3680
+ setFieldTouched = _useFormikContext.setFieldTouched,
3681
+ getFieldMeta = _useFormikContext.getFieldMeta;
3549
3682
  var bodyRef = react.useRef(null);
3550
3683
  var name = fieldKeys.delay;
3551
3684
  var _useField = formik.useField("".concat(name, ".value")),
@@ -3554,12 +3687,18 @@ var DelayPane = function DelayPane(_ref) {
3554
3687
  var units = normalizeOptions((_schema$delay = schema.delay) === null || _schema$delay === void 0 ? void 0 : _schema$delay.units);
3555
3688
  var unitOptions = neetoCist.isNotEmpty(units) ? units : DEFAULT_DELAY_UNITS;
3556
3689
  usePaneValidation("".concat(name, ".value"));
3690
+ react.useEffect(function () {
3691
+ if ((value === null || value === void 0 ? void 0 : value.value) === undefined) return;
3692
+ if (getFieldMeta("".concat(name, ".value.value")).touched) return;
3693
+ setFieldTouched("".concat(name, ".value.value"), true, false);
3694
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3695
+ }, [value === null || value === void 0 ? void 0 : value.value]);
3557
3696
  react.useEffect(function () {
3558
3697
  if (ramda.isEmpty(value || {})) {
3559
3698
  var _unitOptions$0$value, _unitOptions$;
3560
3699
  setFieldValue("".concat(name, ".value"), {
3561
- value: 10,
3562
- unit: (_unitOptions$0$value = (_unitOptions$ = unitOptions[0]) === null || _unitOptions$ === void 0 ? void 0 : _unitOptions$.value) !== null && _unitOptions$0$value !== void 0 ? _unitOptions$0$value : "minutes"
3700
+ value: DEFAULT_DELAY_VALUE,
3701
+ unit: (_unitOptions$0$value = (_unitOptions$ = unitOptions[0]) === null || _unitOptions$ === void 0 ? void 0 : _unitOptions$.value) !== null && _unitOptions$0$value !== void 0 ? _unitOptions$0$value : DEFAULT_DELAY_UNITS[0].value
3563
3702
  });
3564
3703
  }
3565
3704
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -3577,6 +3716,7 @@ var DelayPane = function DelayPane(_ref) {
3577
3716
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
3578
3717
  className: "flex flex-col gap-y-1",
3579
3718
  children: [/*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
3719
+ "data-testid": "pane-title",
3580
3720
  variant: "h5",
3581
3721
  children: t("neetoRules.common.addDelay")
3582
3722
  }), /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
@@ -3597,13 +3737,15 @@ var DelayPane = function DelayPane(_ref) {
3597
3737
  className: "flex items-start gap-x-2",
3598
3738
  children: [/*#__PURE__*/jsxRuntime.jsx(formik$1.Input, {
3599
3739
  className: "w-44",
3600
- max: 999,
3601
- min: 1,
3740
+ dataTestid: "delay-value",
3741
+ max: index.MAX_DELAY_VALUE,
3742
+ min: index.MIN_DELAY_VALUE,
3602
3743
  name: "".concat(name, ".value.value"),
3603
3744
  placeholder: t("neetoRules.placeholders.enterDelay"),
3604
3745
  size: "large",
3605
3746
  type: "number"
3606
3747
  }), /*#__PURE__*/jsxRuntime.jsx(formik$1.Select, {
3748
+ dataTestid: "delay-unit",
3607
3749
  defaultValue: value === null || value === void 0 ? void 0 : value.unit,
3608
3750
  name: "".concat(name, ".value.unit"),
3609
3751
  options: unitOptions,
@@ -3637,11 +3779,13 @@ var asText = function asText(value) {
3637
3779
  var NumberStepper = function NumberStepper(_ref) {
3638
3780
  var value = _ref.value,
3639
3781
  onChange = _ref.onChange,
3782
+ dataTestid = _ref.dataTestid,
3640
3783
  label = _ref.label,
3641
3784
  _ref$min = _ref.min,
3642
- min = _ref$min === void 0 ? 1 : _ref$min,
3785
+ min = _ref$min === void 0 ? STEPPER_MIN_VALUE : _ref$min,
3643
3786
  max = _ref.max,
3644
- required = _ref.required;
3787
+ required = _ref.required,
3788
+ error = _ref.error;
3645
3789
  var _useTranslation = reactI18next.useTranslation(),
3646
3790
  t = _useTranslation.t;
3647
3791
  var _useState = react.useState(null),
@@ -3666,15 +3810,41 @@ var NumberStepper = function NumberStepper(_ref) {
3666
3810
  max: max
3667
3811
  }));
3668
3812
  };
3669
- return /*#__PURE__*/jsxRuntime.jsxs("div", {
3813
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
3670
3814
  className: "mb-stepper",
3671
- children: [/*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Input, {
3815
+ children: /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Input, {
3816
+ dataTestid: dataTestid,
3817
+ error: error,
3672
3818
  label: label,
3673
3819
  min: min,
3674
3820
  required: required,
3675
3821
  autoFocus: true,
3676
3822
  type: "number",
3677
3823
  value: display,
3824
+ suffix: /*#__PURE__*/jsxRuntime.jsxs("div", {
3825
+ className: "mb-stepper-controls",
3826
+ children: [/*#__PURE__*/jsxRuntime.jsx("button", {
3827
+ "aria-label": t("neetoRules.builder.aria.increase"),
3828
+ className: "mb-stepper-btn",
3829
+ "data-testid": "stepper-increase",
3830
+ tabIndex: SKIP_TAB_INDEX,
3831
+ type: "button",
3832
+ onClick: function onClick() {
3833
+ return step(STEPPER_DELTA.INCREMENT);
3834
+ },
3835
+ children: /*#__PURE__*/jsxRuntime.jsx(Up, {})
3836
+ }), /*#__PURE__*/jsxRuntime.jsx("button", {
3837
+ "aria-label": t("neetoRules.builder.aria.decrease"),
3838
+ className: "mb-stepper-btn",
3839
+ "data-testid": "stepper-decrease",
3840
+ tabIndex: SKIP_TAB_INDEX,
3841
+ type: "button",
3842
+ onClick: function onClick() {
3843
+ return step(STEPPER_DELTA.DECREMENT);
3844
+ },
3845
+ children: /*#__PURE__*/jsxRuntime.jsx(Down, {})
3846
+ })]
3847
+ }),
3678
3848
  onBlur: function onBlur() {
3679
3849
  return setDraft(null);
3680
3850
  },
@@ -3684,28 +3854,7 @@ var NumberStepper = function NumberStepper(_ref) {
3684
3854
  onFocus: function onFocus(event) {
3685
3855
  return event.target.select();
3686
3856
  }
3687
- }), /*#__PURE__*/jsxRuntime.jsxs("div", {
3688
- className: "mb-stepper-controls",
3689
- children: [/*#__PURE__*/jsxRuntime.jsx("button", {
3690
- "aria-label": t("neetoRules.builder.aria.increase"),
3691
- className: "mb-stepper-btn",
3692
- tabIndex: -1,
3693
- type: "button",
3694
- onClick: function onClick() {
3695
- return step(1);
3696
- },
3697
- children: /*#__PURE__*/jsxRuntime.jsx(Up, {})
3698
- }), /*#__PURE__*/jsxRuntime.jsx("button", {
3699
- "aria-label": t("neetoRules.builder.aria.decrease"),
3700
- className: "mb-stepper-btn",
3701
- tabIndex: -1,
3702
- type: "button",
3703
- onClick: function onClick() {
3704
- return step(-1);
3705
- },
3706
- children: /*#__PURE__*/jsxRuntime.jsx(Down, {})
3707
- })]
3708
- })]
3857
+ })
3709
3858
  });
3710
3859
  };
3711
3860
 
@@ -3714,7 +3863,9 @@ var LeadTimePane = function LeadTimePane(_ref) {
3714
3863
  var _useTranslation = reactI18next.useTranslation(),
3715
3864
  t = _useTranslation.t;
3716
3865
  var _useFormikContext = formik.useFormikContext(),
3717
- setFieldValue = _useFormikContext.setFieldValue;
3866
+ setFieldValue = _useFormikContext.setFieldValue,
3867
+ setFieldTouched = _useFormikContext.setFieldTouched,
3868
+ getFieldMeta = _useFormikContext.getFieldMeta;
3718
3869
  var _useRuleModel = useRuleModel(schema),
3719
3870
  rule = _useRuleModel.rule,
3720
3871
  fieldKeys = _useRuleModel.fieldKeys;
@@ -3722,6 +3873,11 @@ var LeadTimePane = function LeadTimePane(_ref) {
3722
3873
  amount = _minutesToParts.value,
3723
3874
  unit = _minutesToParts.unit;
3724
3875
  var path = "".concat(fieldKeys.leadTime, ".value");
3876
+ var _useField = formik.useField(path),
3877
+ _useField2 = _slicedToArray(_useField, 2),
3878
+ _useField2$ = _useField2[1],
3879
+ error = _useField2$.error,
3880
+ touched = _useField2$.touched;
3725
3881
  var update = function update(_ref2) {
3726
3882
  var value = _ref2.value,
3727
3883
  nextUnit = _ref2.unit;
@@ -3730,11 +3886,19 @@ var LeadTimePane = function LeadTimePane(_ref) {
3730
3886
  unit: nextUnit
3731
3887
  }));
3732
3888
  };
3889
+ usePaneValidation(path);
3890
+ react.useEffect(function () {
3891
+ if (rule.leadTime === undefined) return;
3892
+ if (getFieldMeta(path).touched) return;
3893
+ setFieldTouched(path, true, false);
3894
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3895
+ }, [rule.leadTime]);
3733
3896
  return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
3734
3897
  children: [/*#__PURE__*/jsxRuntime.jsx(index.Panel.Header, {
3735
3898
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
3736
3899
  className: "flex flex-col gap-y-1",
3737
3900
  children: [/*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
3901
+ "data-testid": "pane-title",
3738
3902
  variant: "h5",
3739
3903
  children: t("neetoRules.builder.leadTimeTitle")
3740
3904
  }), /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
@@ -3745,32 +3909,41 @@ var LeadTimePane = function LeadTimePane(_ref) {
3745
3909
  })
3746
3910
  }), /*#__PURE__*/jsxRuntime.jsx(index.Panel.Body, {
3747
3911
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
3748
- className: "flex items-end gap-x-3",
3749
- children: [/*#__PURE__*/jsxRuntime.jsx("div", {
3750
- className: "flex-1",
3751
- children: /*#__PURE__*/jsxRuntime.jsx(NumberStepper, {
3752
- label: t("neetoRules.builder.before"),
3753
- value: amount,
3754
- onChange: function onChange(value) {
3755
- return update({
3756
- value: value === "" ? 0 : value,
3757
- unit: unit
3758
- });
3759
- }
3760
- })
3761
- }), /*#__PURE__*/jsxRuntime.jsx("div", {
3762
- className: "w-32 shrink-0",
3763
- children: /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Select, {
3764
- isSearchable: false,
3765
- options: LEAD_TIME_UNITS,
3766
- value: unit,
3767
- onChange: function onChange(nextUnit) {
3768
- return update({
3769
- value: amount,
3770
- unit: nextUnit
3771
- });
3772
- }
3773
- })
3912
+ className: "flex flex-col gap-y-1.5",
3913
+ children: [/*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
3914
+ variant: "body2",
3915
+ weight: "medium",
3916
+ children: t("neetoRules.builder.before")
3917
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
3918
+ className: "flex items-start gap-x-3",
3919
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
3920
+ className: "flex-1",
3921
+ children: /*#__PURE__*/jsxRuntime.jsx(NumberStepper, {
3922
+ dataTestid: "lead-time",
3923
+ error: touched ? error : "",
3924
+ value: amount,
3925
+ onChange: function onChange(value) {
3926
+ return update({
3927
+ value: value === "" ? 0 : value,
3928
+ unit: unit
3929
+ });
3930
+ }
3931
+ })
3932
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
3933
+ className: "w-32 shrink-0",
3934
+ children: /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Select, {
3935
+ dataTestid: "lead-time-unit",
3936
+ isSearchable: false,
3937
+ options: LEAD_TIME_UNITS,
3938
+ value: unit,
3939
+ onChange: function onChange(nextUnit) {
3940
+ return update({
3941
+ value: amount,
3942
+ unit: nextUnit
3943
+ });
3944
+ }
3945
+ })
3946
+ })]
3774
3947
  })]
3775
3948
  })
3776
3949
  })]
@@ -3788,6 +3961,7 @@ var NamePane = function NamePane(_ref) {
3788
3961
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
3789
3962
  className: "flex flex-col gap-y-1",
3790
3963
  children: [/*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
3964
+ "data-testid": "pane-title",
3791
3965
  variant: "h5",
3792
3966
  children: t("neetoRules.builder.automationName")
3793
3967
  }), /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
@@ -3965,12 +4139,13 @@ var OptionsPane = function OptionsPane(_ref) {
3965
4139
  if (ramda.isEmpty(filtered)) {
3966
4140
  return /*#__PURE__*/jsxRuntime.jsx("div", {
3967
4141
  className: "mb-pane-empty",
4142
+ "data-testid": "pane-no-matches",
3968
4143
  children: t("neetoRules.builder.noMatches")
3969
4144
  });
3970
4145
  }
3971
4146
  if (multi) {
3972
4147
  return /*#__PURE__*/jsxRuntime.jsx("div", {
3973
- className: "flex flex-col",
4148
+ className: "mb-pane-list flex flex-col",
3974
4149
  children: groups.map(function (_ref2) {
3975
4150
  var _ref3 = _slicedToArray(_ref2, 2),
3976
4151
  category = _ref3[0],
@@ -4022,6 +4197,7 @@ var OptionsPane = function OptionsPane(_ref) {
4022
4197
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
4023
4198
  className: "flex w-full flex-col gap-y-2",
4024
4199
  children: [/*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
4200
+ "data-testid": "pane-title",
4025
4201
  variant: "h5",
4026
4202
  children: title
4027
4203
  }), /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
@@ -4032,6 +4208,7 @@ var OptionsPane = function OptionsPane(_ref) {
4032
4208
  className: "mb-pane-search",
4033
4209
  ref: searchRef,
4034
4210
  children: /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Input, {
4211
+ dataTestid: "pane-search",
4035
4212
  placeholder: t("neetoRules.builder.searchPlaceholder"),
4036
4213
  prefix: /*#__PURE__*/jsxRuntime.jsx(Search, {}),
4037
4214
  size: "large",
@@ -4046,6 +4223,7 @@ var OptionsPane = function OptionsPane(_ref) {
4046
4223
  children: [renderBody(), belowContent]
4047
4224
  }), cascade && /*#__PURE__*/jsxRuntime.jsx(index.Panel.Footer, {
4048
4225
  children: /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Button, {
4226
+ "data-testid": "continue-button",
4049
4227
  disabled: !canContinue,
4050
4228
  label: t("neetoRules.buttons.continue"),
4051
4229
  size: "lg",
@@ -4071,8 +4249,8 @@ var RecipientField = function RecipientField(_ref) {
4071
4249
  var fieldError = function fieldError(path) {
4072
4250
  return formik.getIn(touched, path) && formik.getIn(errors, path);
4073
4251
  };
4074
- if (field === "emails") {
4075
- var _path = "".concat(name, ".emails");
4252
+ if (field === RECIPIENT_STORAGE_FIELDS.emails) {
4253
+ var _path = "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.emails);
4076
4254
  var _current = formik.getIn(values, _path) || [];
4077
4255
  return /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.MultiEmailInput, {
4078
4256
  required: true,
@@ -4094,7 +4272,7 @@ var RecipientField = function RecipientField(_ref) {
4094
4272
  }
4095
4273
  });
4096
4274
  }
4097
- var path = "".concat(name, ".value");
4275
+ var path = "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.value);
4098
4276
  var current = formik.getIn(values, path);
4099
4277
  var options = normalizeOptions(dropdownOptions);
4100
4278
  return /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Select, {
@@ -4138,7 +4316,7 @@ var RecipientPane = function RecipientPane(_ref) {
4138
4316
  });
4139
4317
  var flatOption = ((_schema$actions4 = schema.actions) === null || _schema$actions4 === void 0 || (_schema$actions4$opti = _schema$actions4.optionForName) === null || _schema$actions4$opti === void 0 ? void 0 : _schema$actions4$opti.call(_schema$actions4, action.name)) || ((_schema$actions5 = schema.actions) === null || _schema$actions5 === void 0 || (_schema$actions5$opti = _schema$actions5.optionForChannel) === null || _schema$actions5$opti === void 0 ? void 0 : _schema$actions5$opti.call(_schema$actions5, channel));
4140
4318
  var isSmsToNumbers = (flatOption === null || flatOption === void 0 ? void 0 : flatOption.type) === SMS_TO_NUMBERS;
4141
- var recipientFieldPath = isSmsToNumbers ? "".concat(name, ".phoneNumbers") : "".concat(name, ".emails");
4319
+ var recipientFieldPath = isSmsToNumbers ? "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.phoneNumbers) : "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.emails);
4142
4320
  usePaneValidation(recipientFieldPath);
4143
4321
  react.useEffect(function () {
4144
4322
  var _bodyRef$current;
@@ -4169,7 +4347,7 @@ var RecipientPane = function RecipientPane(_ref) {
4169
4347
  action: action
4170
4348
  })) !== null && _schema$actions$rende !== void 0 ? _schema$actions$rende : builtInRecipientField;
4171
4349
  var phoneField = isSmsToNumbers ? /*#__PURE__*/jsxRuntime.jsx(index.PhoneNumberField, {
4172
- name: "".concat(name, ".phoneNumbers"),
4350
+ name: "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.phoneNumbers),
4173
4351
  phoneNumbers: (_action$metadata = action.metadata) === null || _action$metadata === void 0 ? void 0 : _action$metadata.phoneNumbers
4174
4352
  }) : null;
4175
4353
  var belowField = phoneField || recipientField;
@@ -4205,6 +4383,7 @@ var RecipientPane = function RecipientPane(_ref) {
4205
4383
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
4206
4384
  className: "flex w-full flex-col gap-y-2",
4207
4385
  children: [/*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
4386
+ "data-testid": "pane-title",
4208
4387
  variant: "h5",
4209
4388
  children: meta.recipientTitle || t("neetoRules.builder.panes.recipientTitle")
4210
4389
  }), /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
@@ -4251,6 +4430,7 @@ var ConditionValueFields = function ConditionValueFields(_ref) {
4251
4430
  };
4252
4431
  var noMatches = /*#__PURE__*/jsxRuntime.jsx("div", {
4253
4432
  className: "mb-pane-empty",
4433
+ "data-testid": "pane-no-matches",
4254
4434
  children: t("neetoRules.builder.noMatches")
4255
4435
  });
4256
4436
  if (type === CONDITION_VALUE_TYPES.multiSelect && neetoCist.isNotEmpty(enumOptions)) {
@@ -4262,7 +4442,7 @@ var ConditionValueFields = function ConditionValueFields(_ref) {
4262
4442
  setFieldValue(valuesPath, current.includes(value) ? current.filter(neetoCist.isNot(value)) : [].concat(_toConsumableArray(current), [value]));
4263
4443
  };
4264
4444
  return /*#__PURE__*/jsxRuntime.jsxs("div", {
4265
- className: "flex flex-col gap-y-1",
4445
+ className: "mb-pane-list flex flex-col",
4266
4446
  children: [filtered.map(function (option) {
4267
4447
  return /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Checkbox, {
4268
4448
  checked: current.includes(option.value),
@@ -4406,6 +4586,7 @@ var ValuePane = function ValuePane(_ref) {
4406
4586
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
4407
4587
  className: "flex w-full flex-col gap-y-2",
4408
4588
  children: [/*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Typography, {
4589
+ "data-testid": "pane-title",
4409
4590
  variant: "h5",
4410
4591
  children: t("neetoRules.builder.pickValue", {
4411
4592
  field: (field === null || field === void 0 ? void 0 : field.label) || t("neetoRules.builder.valueWord")
@@ -4420,6 +4601,7 @@ var ValuePane = function ValuePane(_ref) {
4420
4601
  className: "mb-pane-search",
4421
4602
  ref: searchRef,
4422
4603
  children: /*#__PURE__*/jsxRuntime.jsx(neetoAtoms.Input, {
4604
+ dataTestid: "pane-search",
4423
4605
  placeholder: t("neetoRules.builder.searchPlaceholder"),
4424
4606
  prefix: /*#__PURE__*/jsxRuntime.jsx(Search, {}),
4425
4607
  size: "large",
@@ -4566,7 +4748,7 @@ var Form = function Form(_ref) {
4566
4748
  data: data,
4567
4749
  customData: customData,
4568
4750
  isNameGenerationDisabled: isNameRequired
4569
- }),
4751
+ }).concat(leadTimeValidationSchema(schema)),
4570
4752
  onSubmit: submitForm,
4571
4753
  children: function children() {
4572
4754
  return /*#__PURE__*/jsxRuntime.jsxs("div", {