@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
@@ -1,6 +1,6 @@
1
1
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
2
2
  import _objectDestructuringEmpty from '@babel/runtime/helpers/objectDestructuringEmpty';
3
- import { f as flatten, S as SHORTCUTS } from '../keyboardShortcuts-DXP5YgeG.js';
3
+ import { f as flatten, S as SHORTCUTS } from '../keyboardShortcuts-_OrtgF0A.js';
4
4
  import { useShallow } from 'zustand/shallow';
5
5
  import { useState, useRef, useEffect, createElement, Fragment as Fragment$1, useCallback, useMemo } from 'react';
6
6
  import { Button, Input, Checkbox, Typography, RadioGroup, Textarea, DatePicker, Select as Select$1, Tooltip, MultiEmailInput, Spinner } from '@bigbinary/neeto-atoms';
@@ -11,9 +11,9 @@ import { isNotEmpty, toLabelAndValue, removeBy, isNot, noop } from '@bigbinary/n
11
11
  import Container from '@bigbinary/neeto-molecules/v2/Container';
12
12
  import Header from '@bigbinary/neeto-molecules/v2/Header';
13
13
  import { useTranslation } from 'react-i18next';
14
- import { E as EditorAction, m as Panel, p as ErrorCallout, q as SmsFields, r as PhoneNumberField, j as formatData, u as usePollRuleName, T as TouchErrors, l as ScrollToErrorField, n as getValidationSchema, o as DEFAULT_INITIAL_VALUE, k as buildPayload } from '../index-BXLCiV70.js';
15
- import { u as useCustomDataStore } from '../useCustomDataStore-CU3KpyD4.js';
16
- import { u as useUtilityStore } from '../useUtilityStore-BBb39TOj.js';
14
+ import { E as EditorAction, L as ListField, o as Panel, r as ErrorCallout, s as SmsFields, M as MIN_DELAY_VALUE, t as MAX_DELAY_VALUE, v as PhoneNumberField, l as formatData, u as usePollRuleName, T as TouchErrors, n as ScrollToErrorField, p as getValidationSchema, q as DEFAULT_INITIAL_VALUE, m as buildPayload } from '../index-CcbW7H2S.js';
15
+ import { u as useCustomDataStore } from '../useCustomDataStore-CEbhXugO.js';
16
+ import { u as useUtilityStore } from '../useUtilityStore-BVCulMEC.js';
17
17
  import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
18
18
  import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
19
19
  import Plus from '@bigbinary/neeto-icons/Plus';
@@ -22,6 +22,7 @@ import Delete from '@bigbinary/neeto-icons/Delete';
22
22
  import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
23
23
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
24
24
  import { t } from 'i18next';
25
+ import * as yup from 'yup';
25
26
  import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator';
26
27
  import useHotkeys from '@bigbinary/neeto-hotkeys';
27
28
  import Search from '@bigbinary/neeto-icons/Search';
@@ -35,7 +36,6 @@ import '@tanstack/react-query';
35
36
  import '@bigbinary/neeto-commons-frontend/react-utils';
36
37
  import 'axios';
37
38
  import '../index-9h4B7JZZ.js';
38
- import 'yup';
39
39
  import 'lucide-react';
40
40
  import '@bigbinary/neeto-commons-frontend/v2/react-utils';
41
41
  import '@monaco-editor/react';
@@ -79,6 +79,22 @@ var TIME_UNITS = [{
79
79
  /* eslint-enable @bigbinary/neeto/neetocommons-tips */
80
80
 
81
81
  var LEAD_TIME_UNITS = TIME_UNITS;
82
+
83
+ // Mirrors the classic form's three-digit input cap; 999 days equals the
84
+ // backend's maximum lead time.
85
+ var MAX_LEAD_TIME_VALUE = 999;
86
+ var STEPPER_DELTA = {
87
+ INCREMENT: 1,
88
+ DECREMENT: -1
89
+ };
90
+ var STEPPER_MIN_VALUE = 1;
91
+ var DEFAULT_DELAY_VALUE = 10;
92
+
93
+ // Matches the token flash animation duration in sentence-builder.css.
94
+ var FLASH_DURATION_MS = 2200;
95
+
96
+ // Keeps an element out of the keyboard tab order (HTML tabindex sentinel).
97
+ var SKIP_TAB_INDEX = -1;
82
98
  var CONDITION_VERBS = {
83
99
  IS: "is",
84
100
  IS_NOT: "is_not",
@@ -176,7 +192,11 @@ var CONFIG_FOCUSABLE_SELECTOR = 'input:not([type="radio"]):not([type="checkbox"]
176
192
  var SMS_TYPES = ["sms", "smsToNumbers"];
177
193
  var RECIPIENT_FOCUSABLE_SELECTOR = 'input, textarea, [contenteditable="true"]';
178
194
  var SMS_TO_NUMBERS = "smsToNumbers";
179
- var SWAP_CHANNEL_ORDER = ["email", "sms", "slack", "webhook", "calendar_invite"];
195
+ var RECIPIENT_STORAGE_FIELDS = {
196
+ emails: "emails",
197
+ value: "value",
198
+ phoneNumbers: "phoneNumbers"
199
+ };
180
200
  var DEFAULT_DELAY_UNITS = TIME_UNITS;
181
201
 
182
202
  var useClearingHover = function useClearingHover() {
@@ -552,6 +572,25 @@ var partsToMinutes = function partsToMinutes(_ref19) {
552
572
  (Number(value) || 0) * (UNIT_MINUTES[unit] || 1)
553
573
  );
554
574
  };
575
+ var leadTimeExceedsMax = function leadTimeExceedsMax(minutes) {
576
+ return minutesToParts(minutes).value > MAX_LEAD_TIME_VALUE;
577
+ };
578
+ var leadTimeValidationSchema = function leadTimeValidationSchema(schema) {
579
+ var _schema$triggers2;
580
+ 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;
581
+ if (!fieldKey) return yup.object();
582
+ return yup.object().shape(_defineProperty({}, fieldKey, yup.object().shape({
583
+ // yup 0.32 can only concat schemas of the same type, and the host's
584
+ // generic field schema for the lead time key is a string schema.
585
+ value: yup.string().test("max-lead-time", function () {
586
+ return t("neetoRules.validations.maxLeadTime", {
587
+ max: MAX_LEAD_TIME_VALUE
588
+ });
589
+ }, function (minutes) {
590
+ return !leadTimeExceedsMax(minutes);
591
+ })
592
+ })));
593
+ };
555
594
  var leadTimeLabel = function leadTimeLabel(minutes) {
556
595
  var _minutesToParts = minutesToParts(minutes),
557
596
  value = _minutesToParts.value,
@@ -574,6 +613,9 @@ var normalizeSchema = function normalizeSchema() {
574
613
  var joinTypeFor = function joinTypeFor(mode) {
575
614
  return mode === CONDITION_MODE.OR ? JOIN_TYPE.or : JOIN_TYPE.and;
576
615
  };
616
+ var recipientStorageField = function recipientStorageField(field) {
617
+ return field === RECIPIENT_STORAGE_FIELDS.emails ? RECIPIENT_STORAGE_FIELDS.emails : RECIPIENT_STORAGE_FIELDS.value;
618
+ };
577
619
  var modeFromJoinType = function modeFromJoinType(joinType) {
578
620
  return joinType === JOIN_TYPE.or ? CONDITION_MODE.OR : CONDITION_MODE.AND;
579
621
  };
@@ -589,15 +631,15 @@ var asArray = function asArray(maybeArray) {
589
631
  return Array.isArray(maybeArray) ? maybeArray : [];
590
632
  };
591
633
  var fieldKeysFromSchema = function fieldKeysFromSchema() {
592
- var _schema$triggers2, _schema$actor2, _schema$conditions3, _schema$actions3, _schema$delay, _schema$triggers3, _schema$scope2;
634
+ var _schema$triggers3, _schema$actor2, _schema$conditions3, _schema$actions3, _schema$delay, _schema$triggers4, _schema$scope2;
593
635
  var schema = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
594
636
  return {
595
- triggers: ((_schema$triggers2 = schema.triggers) === null || _schema$triggers2 === void 0 ? void 0 : _schema$triggers2.fieldKey) || "events",
637
+ triggers: ((_schema$triggers3 = schema.triggers) === null || _schema$triggers3 === void 0 ? void 0 : _schema$triggers3.fieldKey) || "events",
596
638
  performer: ((_schema$actor2 = schema.actor) === null || _schema$actor2 === void 0 ? void 0 : _schema$actor2.fieldKey) || "performer",
597
639
  conditions: ((_schema$conditions3 = schema.conditions) === null || _schema$conditions3 === void 0 ? void 0 : _schema$conditions3.fieldKey) || "conditions",
598
640
  actions: ((_schema$actions3 = schema.actions) === null || _schema$actions3 === void 0 ? void 0 : _schema$actions3.fieldKey) || "actions",
599
641
  delay: ((_schema$delay = schema.delay) === null || _schema$delay === void 0 ? void 0 : _schema$delay.fieldKey) || "execution_delay",
600
- 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,
642
+ 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,
601
643
  scope: ((_schema$scope2 = schema.scope) === null || _schema$scope2 === void 0 ? void 0 : _schema$scope2.fieldKey) || "meetingIds",
602
644
  status: "status",
603
645
  name: "name"
@@ -750,6 +792,35 @@ var removeArrayItem = function removeArrayItem(_ref30) {
750
792
  }
751
793
  return result;
752
794
  };
795
+ var clearArrayItems = function clearArrayItems(_ref31) {
796
+ var values = _ref31.values,
797
+ fieldKey = _ref31.fieldKey;
798
+ var list = asArray(valueAt({
799
+ values: values,
800
+ key: fieldKey
801
+ }));
802
+ var removed = list.filter(function (item) {
803
+ return item === null || item === void 0 ? void 0 : item.id;
804
+ }).map(function (item) {
805
+ return _objectSpread$b(_objectSpread$b({}, item), {}, {
806
+ _destroy: true
807
+ });
808
+ });
809
+ var result = [{
810
+ path: "".concat(fieldKey, ".value"),
811
+ value: []
812
+ }];
813
+ if (removed.length) {
814
+ var _values$fieldKey2;
815
+ // eslint-disable-next-line security/detect-object-injection
816
+ var deleted = asArray(values === null || values === void 0 || (_values$fieldKey2 = values[fieldKey]) === null || _values$fieldKey2 === void 0 ? void 0 : _values$fieldKey2.deleted);
817
+ result.push({
818
+ path: "".concat(fieldKey, ".deleted"),
819
+ value: [].concat(_toConsumableArray(deleted), _toConsumableArray(removed))
820
+ });
821
+ }
822
+ return result;
823
+ };
753
824
 
754
825
  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; }
755
826
  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; }
@@ -1029,6 +1100,7 @@ var MetaBlock = function MetaBlock(_ref) {
1029
1100
  children: t("neetoRules.builder.automationName")
1030
1101
  }), /*#__PURE__*/jsxs("div", {
1031
1102
  ref: nameRef,
1103
+ "data-testid": "rule-name-edit",
1032
1104
  role: handleEditName ? "button" : undefined,
1033
1105
  tabIndex: handleEditName ? 0 : undefined,
1034
1106
  className: classNames("mb-meta-input-wrap", {
@@ -1037,6 +1109,7 @@ var MetaBlock = function MetaBlock(_ref) {
1037
1109
  onClick: handleEditName,
1038
1110
  children: [/*#__PURE__*/jsx("span", {
1039
1111
  className: "mb-meta-name-display",
1112
+ "data-testid": "rule-name",
1040
1113
  children: rule.name
1041
1114
  }), /*#__PURE__*/jsx("span", {
1042
1115
  "aria-hidden": true,
@@ -1058,8 +1131,10 @@ var MetaBlock = function MetaBlock(_ref) {
1058
1131
  })]
1059
1132
  }), /*#__PURE__*/jsxs("label", {
1060
1133
  className: "mb-meta-active",
1134
+ "data-testid": "rule-active-label",
1061
1135
  children: [/*#__PURE__*/jsx("input", {
1062
1136
  checked: rule.active,
1137
+ "data-testid": "rule-active-checkbox",
1063
1138
  readOnly: !handleToggleActive,
1064
1139
  type: "checkbox",
1065
1140
  onChange: handleToggleActive || undefined
@@ -1317,6 +1392,8 @@ var ActionRow = function ActionRow(_ref) {
1317
1392
  });
1318
1393
  var configLabel = mediumLabel || sentencify(option === null || option === void 0 ? void 0 : option.placeholder);
1319
1394
  var isConfigured = isNotEmpty(action === null || action === void 0 ? void 0 : action.metadata);
1395
+ // A host that shows no value label still needs the token to read as filled.
1396
+ var configState = configValue || isConfigured ? TOKEN_STATE.FILLED : TOKEN_STATE.MUTED;
1320
1397
  var _useClearingHover = useClearingHover(),
1321
1398
  clearing = _useClearingHover.clearing,
1322
1399
  clearingHoverProps = _useClearingHover.clearingHoverProps;
@@ -1355,7 +1432,7 @@ var ActionRow = function ActionRow(_ref) {
1355
1432
  children: verbLabel
1356
1433
  }), hasParamTail ? /*#__PURE__*/jsx(Token, {
1357
1434
  dataTestid: "action-config-value-".concat(index),
1358
- state: configValue ? TOKEN_STATE.FILLED : TOKEN_STATE.MUTED,
1435
+ state: configState,
1359
1436
  active: isActive({
1360
1437
  type: BUILDER_ANCHORS.ACTION_CONFIG,
1361
1438
  index: index
@@ -1403,7 +1480,7 @@ var ActionRow = function ActionRow(_ref) {
1403
1480
  index: index
1404
1481
  }),
1405
1482
  dataTestid: "action-config-value-".concat(index),
1406
- state: configValue ? TOKEN_STATE.FILLED : TOKEN_STATE.MUTED,
1483
+ state: configState,
1407
1484
  invalid: isInvalid({
1408
1485
  type: BUILDER_ANCHORS.ACTION_CONFIG,
1409
1486
  index: index
@@ -1626,6 +1703,10 @@ var WhenBlock = function WhenBlock(_ref) {
1626
1703
  type: BUILDER_ANCHORS.TRIGGER_LEAD_TIME,
1627
1704
  index: index
1628
1705
  }),
1706
+ invalid: isInvalid({
1707
+ type: BUILDER_ANCHORS.TRIGGER_LEAD_TIME,
1708
+ index: index
1709
+ }),
1629
1710
  onClick: handleToken && function () {
1630
1711
  return handleToken({
1631
1712
  type: BUILDER_ANCHORS.TRIGGER_LEAD_TIME,
@@ -1836,6 +1917,24 @@ var useRuleModel = function useRuleModel(schema) {
1836
1917
  fieldKey: fieldKeysFromSchema(schema).actions,
1837
1918
  index: index
1838
1919
  }));
1920
+ },
1921
+ clearTriggers: function clearTriggers() {
1922
+ return applyAll(clearArrayItems({
1923
+ values: values,
1924
+ fieldKey: fieldKeysFromSchema(schema).triggers
1925
+ }));
1926
+ },
1927
+ clearConditions: function clearConditions() {
1928
+ return applyAll(clearArrayItems({
1929
+ values: values,
1930
+ fieldKey: fieldKeysFromSchema(schema).conditions
1931
+ }));
1932
+ },
1933
+ clearActions: function clearActions() {
1934
+ return applyAll(clearArrayItems({
1935
+ values: values,
1936
+ fieldKey: fieldKeysFromSchema(schema).actions
1937
+ }));
1839
1938
  }
1840
1939
  };
1841
1940
  };
@@ -1982,9 +2081,9 @@ var nextTriggers = function nextTriggers(_ref10) {
1982
2081
  name: value
1983
2082
  }]);
1984
2083
  }
1985
- return [{
2084
+ return [_objectSpread$4(_objectSpread$4({}, rule.triggers[0]), {}, {
1986
2085
  name: value
1987
- }];
2086
+ })];
1988
2087
  };
1989
2088
  var nextConditionAt = function nextConditionAt(_ref11) {
1990
2089
  var _field$allowMatching;
@@ -2231,7 +2330,7 @@ var descriptorFor = function descriptorFor(_ref15) {
2231
2330
  case BUILDER_ANCHORS.ACTION_ADD:
2232
2331
  case BUILDER_ANCHORS.ACTION_CHANNEL:
2233
2332
  {
2234
- var _schema$actions11, _schema$actions12, _channelOf;
2333
+ var _schema$actions11, _channelOf;
2235
2334
  var isSwap = anchor === BUILDER_ANCHORS.ACTION_CHANNEL;
2236
2335
  var channelMetaFor = function channelMetaFor(value) {
2237
2336
  var _schema$actions10;
@@ -2240,13 +2339,10 @@ var descriptorFor = function descriptorFor(_ref15) {
2240
2339
  ((_schema$actions10 = schema.actions) === null || _schema$actions10 === void 0 || (_schema$actions10 = _schema$actions10.channelMeta) === null || _schema$actions10 === void 0 ? void 0 : _schema$actions10[value]) || {}
2241
2340
  );
2242
2341
  };
2243
- var rank = function rank(value) {
2244
- var idx = SWAP_CHANNEL_ORDER.indexOf(value);
2245
- return idx === -1 ? SWAP_CHANNEL_ORDER.length : idx;
2246
- };
2247
- var sourceOptions = isSwap ? _toConsumableArray(((_schema$actions11 = schema.actions) === null || _schema$actions11 === void 0 ? void 0 : _schema$actions11.options) || []).sort(function (optionA, optionB) {
2248
- return rank(optionValue(optionA)) - rank(optionValue(optionB));
2249
- }) : ((_schema$actions12 = schema.actions) === null || _schema$actions12 === void 0 ? void 0 : _schema$actions12.options) || [];
2342
+
2343
+ // Both panes list the host's actions in the order it declared them;
2344
+ // re-ranking only the swap pane made the two disagree.
2345
+ var sourceOptions = ((_schema$actions11 = schema.actions) === null || _schema$actions11 === void 0 ? void 0 : _schema$actions11.options) || [];
2250
2346
  return {
2251
2347
  title: isSwap ? t("neetoRules.builder.panes.actionSwapTitle") : t("neetoRules.builder.panes.actionAddTitle"),
2252
2348
  subtitle: isSwap ? t("neetoRules.builder.panes.actionSwapSubtitle") : t("neetoRules.builder.panes.actionAddSubtitle"),
@@ -2276,7 +2372,7 @@ var descriptorFor = function descriptorFor(_ref15) {
2276
2372
  }
2277
2373
  case BUILDER_ANCHORS.ACTION_RECIPIENT:
2278
2374
  {
2279
- var _schema$actions13;
2375
+ var _schema$actions12;
2280
2376
  var action = actionAt({
2281
2377
  rule: rule,
2282
2378
  index: index
@@ -2286,7 +2382,7 @@ var descriptorFor = function descriptorFor(_ref15) {
2286
2382
  action: action
2287
2383
  });
2288
2384
  // eslint-disable-next-line security/detect-object-injection
2289
- 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]) || {};
2385
+ 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]) || {};
2290
2386
  var option = findActionOption({
2291
2387
  schema: schema,
2292
2388
  name: channel
@@ -2402,9 +2498,9 @@ var commitFor = function commitFor(_ref16) {
2402
2498
  }]));
2403
2499
  return "stay";
2404
2500
  }
2405
- setTriggers([{
2501
+ setTriggers([_objectSpread$4(_objectSpread$4({}, rule.triggers[0]), {}, {
2406
2502
  name: value
2407
- }]);
2503
+ })]);
2408
2504
  var leadTime = (_schema$triggers5 = schema.triggers) === null || _schema$triggers5 === void 0 ? void 0 : _schema$triggers5.leadTime;
2409
2505
  if (leadTime !== null && leadTime !== void 0 && leadTime.fieldKey && (leadTime.appliesTo || []).includes(value) && !rule.leadTime) {
2410
2506
  var _leadTime$default;
@@ -2671,7 +2767,7 @@ var applyPreview = function applyPreview(_ref19) {
2671
2767
  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; }
2672
2768
  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; }
2673
2769
  var Canvas = function Canvas(_ref) {
2674
- var _schema$scope, _schema$scope2, _schema$conditions, _schema$triggers, _schema$delay, _schema$meta$showName, _schema$meta;
2770
+ var _schema$scope, _schema$conditions, _schema$triggers, _schema$delay, _schema$meta$showName, _schema$meta;
2675
2771
  var schema = _ref.schema;
2676
2772
  var _useTranslation = useTranslation(),
2677
2773
  t = _useTranslation.t;
@@ -2684,15 +2780,16 @@ var Canvas = function Canvas(_ref) {
2684
2780
  var _useRuleModel = useRuleModel(schema),
2685
2781
  rule = _useRuleModel.rule,
2686
2782
  fieldKeys = _useRuleModel.fieldKeys,
2687
- setTriggers = _useRuleModel.setTriggers,
2688
2783
  setPerformer = _useRuleModel.setPerformer,
2689
2784
  setConditions = _useRuleModel.setConditions,
2690
- setActions = _useRuleModel.setActions,
2691
2785
  setDelay = _useRuleModel.setDelay,
2692
2786
  removeCondition = _useRuleModel.removeCondition,
2693
2787
  removeAction = _useRuleModel.removeAction,
2694
2788
  setActive = _useRuleModel.setActive,
2695
- setScope = _useRuleModel.setScope;
2789
+ setScope = _useRuleModel.setScope,
2790
+ clearTriggers = _useRuleModel.clearTriggers,
2791
+ clearConditions = _useRuleModel.clearConditions,
2792
+ clearActions = _useRuleModel.clearActions;
2696
2793
  var _useUtilityStore = useUtilityStore(useShallow(function (store) {
2697
2794
  return {
2698
2795
  builderPreview: store["builderPreview"],
@@ -2737,7 +2834,7 @@ var Canvas = function Canvas(_ref) {
2737
2834
  setFlashSeq(submitCount);
2738
2835
  var timer = setTimeout(function () {
2739
2836
  return setFlashSeq(0);
2740
- }, 2200);
2837
+ }, FLASH_DURATION_MS);
2741
2838
  return function () {
2742
2839
  return clearTimeout(timer);
2743
2840
  };
@@ -2754,7 +2851,7 @@ var Canvas = function Canvas(_ref) {
2754
2851
  });
2755
2852
  };
2756
2853
  var matchesInvalid = function matchesInvalid(_ref4) {
2757
- 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;
2854
+ 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;
2758
2855
  var type = _ref4.type,
2759
2856
  index = _ref4.index;
2760
2857
  var conditionMeta = "".concat(fieldKeys.conditions, ".value.").concat(index, ".metadata");
@@ -2774,7 +2871,13 @@ var Canvas = function Canvas(_ref) {
2774
2871
  action: actionItem
2775
2872
  }));
2776
2873
  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));
2777
- var recipientFields = [].concat(_toConsumableArray(hasRecipientEditor || hasRecipientField ? ["emails"] : []), _toConsumableArray((flatOption === null || flatOption === void 0 ? void 0 : flatOption.type) === "smsToNumbers" ? ["phoneNumbers"] : []));
2874
+ var recipientConfig = [].concat(_toConsumableArray((((_schema$actions6 = schema.actions) === null || _schema$actions6 === void 0 ? void 0 : _schema$actions6.options) || []).flatMap(function (option) {
2875
+ return option.recipients || [];
2876
+ })), _toConsumableArray(((_schema$actions7 = schema.actions) === null || _schema$actions7 === void 0 ? void 0 : _schema$actions7.recipients) || [])).find(function (item) {
2877
+ var _item$value;
2878
+ return ((_item$value = item.value) !== null && _item$value !== void 0 ? _item$value : item.id) === actionItem.recipient;
2879
+ });
2880
+ 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] : []));
2778
2881
  switch (type) {
2779
2882
  case BUILDER_ANCHORS.TRIGGER_ACTOR:
2780
2883
  {
@@ -2784,6 +2887,10 @@ var Canvas = function Canvas(_ref) {
2784
2887
  prefix: "".concat(actorKey, ".value")
2785
2888
  });
2786
2889
  }
2890
+ case BUILDER_ANCHORS.TRIGGER_LEAD_TIME:
2891
+ return hasErrorAt({
2892
+ prefix: "".concat(fieldKeys.leadTime, ".value")
2893
+ });
2787
2894
  case BUILDER_ANCHORS.COND_FIELD:
2788
2895
  return hasErrorAt({
2789
2896
  prefix: "".concat(fieldKeys.conditions, ".value.").concat(index, ".field")
@@ -2804,7 +2911,9 @@ var Canvas = function Canvas(_ref) {
2804
2911
  exclude: recipientFields
2805
2912
  });
2806
2913
  case BUILDER_ANCHORS.ACTION_RECIPIENT:
2807
- return recipientFields.some(function (field) {
2914
+ return hasErrorAt({
2915
+ prefix: "".concat(fieldKeys.actions, ".value.").concat(index, ".name")
2916
+ }) || recipientFields.some(function (field) {
2808
2917
  return hasErrorAt({
2809
2918
  prefix: "".concat(actionMeta, ".").concat(field)
2810
2919
  });
@@ -2882,8 +2991,6 @@ var Canvas = function Canvas(_ref) {
2882
2991
  return (action === null || action === void 0 ? void 0 : action.channel) || (action === null || action === void 0 ? void 0 : action.name);
2883
2992
  });
2884
2993
  var isScopeEnabled = (schema === null || schema === void 0 || (_schema$scope = schema.scope) === null || _schema$scope === void 0 ? void 0 : _schema$scope.enabled) && isTriggerSet && hasAction;
2885
- var isScopeComplete = !(schema !== null && schema !== void 0 && (_schema$scope2 = schema.scope) !== null && _schema$scope2 !== void 0 && _schema$scope2.enabled) || isNotEmpty(rule.scope || []);
2886
- var isComplete = isRuleComplete(rule) && isScopeComplete;
2887
2994
  var handleRemoveScope = function handleRemoveScope(index) {
2888
2995
  closePaneIfTargeting({
2889
2996
  anchors: [BUILDER_ANCHORS.SCOPE_PHONE_FIELD],
@@ -2917,12 +3024,12 @@ var Canvas = function Canvas(_ref) {
2917
3024
  };
2918
3025
  var handleClearTrigger = function handleClearTrigger() {
2919
3026
  var _schema$actor2;
2920
- setTriggers([]);
3027
+ clearTriggers();
2921
3028
  // Hosts without an actor pane can never re-set it, and null fails its validation.
2922
3029
  if ((_schema$actor2 = schema.actor) !== null && _schema$actor2 !== void 0 && _schema$actor2.enabled) setPerformer(null);
2923
- setConditions([]);
3030
+ clearConditions();
2924
3031
  setDelay(null);
2925
- setActions([]);
3032
+ clearActions();
2926
3033
  setScope([]);
2927
3034
  closePane();
2928
3035
  };
@@ -2944,7 +3051,7 @@ var Canvas = function Canvas(_ref) {
2944
3051
  );
2945
3052
  };
2946
3053
  return /*#__PURE__*/jsx("div", {
2947
- className: "neeto-rules-builder",
3054
+ className: "neeto-rules-builder nrb-atoms",
2948
3055
  children: /*#__PURE__*/jsxs("div", {
2949
3056
  className: "mb-canvas neeto-rules-main-card",
2950
3057
  children: [/*#__PURE__*/jsx(WhenBlock, {
@@ -2986,6 +3093,7 @@ var Canvas = function Canvas(_ref) {
2986
3093
  }) : /*#__PURE__*/jsx("div", {
2987
3094
  className: "mb-add-row",
2988
3095
  children: /*#__PURE__*/jsx(Button, {
3096
+ "data-testid": "rule-delay-add",
2989
3097
  icon: Plus,
2990
3098
  iconPosition: "left",
2991
3099
  label: t("neetoRules.common.addDelay"),
@@ -3017,24 +3125,22 @@ var Canvas = function Canvas(_ref) {
3017
3125
  onRemoveScope: handleRemoveScope,
3018
3126
  onToken: openPane
3019
3127
  })]
3020
- }), isComplete && /*#__PURE__*/jsxs(Fragment$1, {
3021
- children: [/*#__PURE__*/jsx(Connector, {
3022
- active: true
3023
- }), /*#__PURE__*/jsx(MetaBlock, {
3024
- rule: rule,
3025
- isInvalid: isInvalid({
3128
+ }), /*#__PURE__*/jsx(Connector, {
3129
+ active: true
3130
+ }), /*#__PURE__*/jsx(MetaBlock, {
3131
+ rule: rule,
3132
+ isInvalid: isInvalid({
3133
+ type: BUILDER_ANCHORS.META_NAME
3134
+ }),
3135
+ 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,
3136
+ onEditName: function onEditName() {
3137
+ return openPane({
3026
3138
  type: BUILDER_ANCHORS.META_NAME
3027
- }),
3028
- 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,
3029
- onEditName: function onEditName() {
3030
- return openPane({
3031
- type: BUILDER_ANCHORS.META_NAME
3032
- });
3033
- },
3034
- onToggleActive: function onToggleActive(event) {
3035
- return setActive(event.target.checked);
3036
- }
3037
- })]
3139
+ });
3140
+ },
3141
+ onToggleActive: function onToggleActive(event) {
3142
+ return setActive(event.target.checked);
3143
+ }
3038
3144
  })]
3039
3145
  })
3040
3146
  });
@@ -3045,6 +3151,7 @@ var ACTION_CONFIG_TYPES = {
3045
3151
  multiSelect: "multiSelect",
3046
3152
  dropdown: "dropdown",
3047
3153
  note: "note",
3154
+ list: "list",
3048
3155
  textarea: "textarea",
3049
3156
  number: "number",
3050
3157
  text: "text",
@@ -3106,6 +3213,7 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3106
3213
  };
3107
3214
  var noMatches = /*#__PURE__*/jsx("div", {
3108
3215
  className: "mb-pane-empty",
3216
+ "data-testid": "pane-no-matches",
3109
3217
  children: t("neetoRules.builder.noMatches")
3110
3218
  });
3111
3219
  if (type === ACTION_CONFIG_TYPES.emailChannel) {
@@ -3114,7 +3222,7 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3114
3222
  var bodyPath = "".concat(name, ".body");
3115
3223
  return /*#__PURE__*/jsxs("div", {
3116
3224
  className: "flex flex-col gap-4",
3117
- children: [/*#__PURE__*/jsx(Input, {
3225
+ children: [!(option !== null && option !== void 0 && option.hideSubject) && /*#__PURE__*/jsx(Input, {
3118
3226
  required: true,
3119
3227
  error: fieldError(subjectPath),
3120
3228
  label: t("neetoRules.builder.config.subject"),
@@ -3147,7 +3255,7 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3147
3255
  setFieldValue(valuePath, current.includes(value) ? current.filter(isNot(value)) : [].concat(_toConsumableArray(current), [value]));
3148
3256
  };
3149
3257
  return /*#__PURE__*/jsxs("div", {
3150
- className: "flex flex-col gap-y-1",
3258
+ className: "mb-pane-list flex flex-col",
3151
3259
  children: [filtered.map(function (item) {
3152
3260
  return /*#__PURE__*/jsx(Checkbox, {
3153
3261
  checked: current.includes(item.value),
@@ -3189,6 +3297,14 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3189
3297
  })
3190
3298
  });
3191
3299
  }
3300
+ if (type === ACTION_CONFIG_TYPES.list) {
3301
+ return /*#__PURE__*/jsxs("div", {
3302
+ className: "flex flex-col gap-2",
3303
+ children: [/*#__PURE__*/jsx(ListField, {
3304
+ name: valuePath
3305
+ }), hintBlock]
3306
+ });
3307
+ }
3192
3308
  if (type === ACTION_CONFIG_TYPES.note) {
3193
3309
  var _action$metadata2;
3194
3310
  return /*#__PURE__*/jsxs("div", {
@@ -3434,7 +3550,7 @@ var ConfigPane = function ConfigPane(_ref) {
3434
3550
  while (1) switch (_context.n) {
3435
3551
  case 0:
3436
3552
  _context.n = 1;
3437
- return validate(["emails"]);
3553
+ return validate([RECIPIENT_STORAGE_FIELDS.emails]);
3438
3554
  case 1:
3439
3555
  if (_context.v) {
3440
3556
  _context.n = 2;
@@ -3463,7 +3579,6 @@ var ConfigPane = function ConfigPane(_ref) {
3463
3579
  };
3464
3580
  }();
3465
3581
  useHotkeys(SHORTCUTS["continue"].sequence, handleContinue, {
3466
- enabled: shouldAdvanceToRecipient,
3467
3582
  mode: "global"
3468
3583
  });
3469
3584
  return /*#__PURE__*/jsxs(Fragment, {
@@ -3471,6 +3586,7 @@ var ConfigPane = function ConfigPane(_ref) {
3471
3586
  children: /*#__PURE__*/jsxs("div", {
3472
3587
  className: "flex w-full flex-col gap-y-2",
3473
3588
  children: [/*#__PURE__*/jsx(Typography, {
3589
+ "data-testid": "pane-title",
3474
3590
  variant: "h5",
3475
3591
  children: title
3476
3592
  }), configSubtitle && /*#__PURE__*/jsx(Typography, {
@@ -3480,6 +3596,7 @@ var ConfigPane = function ConfigPane(_ref) {
3480
3596
  }), showSearch && /*#__PURE__*/jsx("div", {
3481
3597
  className: "mb-pane-search",
3482
3598
  children: /*#__PURE__*/jsx(Input, {
3599
+ dataTestid: "pane-search",
3483
3600
  placeholder: t("neetoRules.builder.searchPlaceholder"),
3484
3601
  prefix: /*#__PURE__*/jsx(Search, {}),
3485
3602
  size: "large",
@@ -3499,20 +3616,14 @@ var ConfigPane = function ConfigPane(_ref) {
3499
3616
  message: disabledReason,
3500
3617
  selectedField: flatOption
3501
3618
  })
3502
- }), isSms ? /*#__PURE__*/jsxs(Fragment, {
3503
- children: [/*#__PURE__*/jsx(SmsFields, {
3504
- action: action,
3505
- index: index,
3506
- name: name,
3507
- shouldAutoFocus: true,
3508
- selectedField: flatOption,
3509
- showPhoneNumberInput: false
3510
- }), /*#__PURE__*/jsx(Typography, {
3511
- className: "mt-2",
3512
- color: "muted",
3513
- variant: "body3",
3514
- children: t("neetoRules.builder.config.smsSegmentHint")
3515
- })]
3619
+ }), isSms ? /*#__PURE__*/jsx(SmsFields, {
3620
+ action: action,
3621
+ index: index,
3622
+ name: name,
3623
+ shouldAutoFocus: true,
3624
+ hint: t("neetoRules.builder.config.smsSegmentHint"),
3625
+ selectedField: flatOption,
3626
+ showPhoneNumberInput: false
3516
3627
  }) : (_hostRenderConfig = hostRenderConfig === null || hostRenderConfig === void 0 ? void 0 : hostRenderConfig({
3517
3628
  name: name,
3518
3629
  action: action,
@@ -3525,8 +3636,9 @@ var ConfigPane = function ConfigPane(_ref) {
3525
3636
  option: flatOption
3526
3637
  })]
3527
3638
  })
3528
- }), shouldAdvanceToRecipient && /*#__PURE__*/jsx(Panel.Footer, {
3639
+ }), /*#__PURE__*/jsx(Panel.Footer, {
3529
3640
  children: /*#__PURE__*/jsx(Button, {
3641
+ "data-testid": "continue-button",
3530
3642
  label: t("neetoRules.buttons.continue"),
3531
3643
  size: "lg",
3532
3644
  onClick: handleContinue
@@ -3543,7 +3655,9 @@ var DelayPane = function DelayPane(_ref) {
3543
3655
  var _useRuleModel = useRuleModel(schema),
3544
3656
  fieldKeys = _useRuleModel.fieldKeys;
3545
3657
  var _useFormikContext = useFormikContext(),
3546
- setFieldValue = _useFormikContext.setFieldValue;
3658
+ setFieldValue = _useFormikContext.setFieldValue,
3659
+ setFieldTouched = _useFormikContext.setFieldTouched,
3660
+ getFieldMeta = _useFormikContext.getFieldMeta;
3547
3661
  var bodyRef = useRef(null);
3548
3662
  var name = fieldKeys.delay;
3549
3663
  var _useField = useField("".concat(name, ".value")),
@@ -3552,12 +3666,18 @@ var DelayPane = function DelayPane(_ref) {
3552
3666
  var units = normalizeOptions((_schema$delay = schema.delay) === null || _schema$delay === void 0 ? void 0 : _schema$delay.units);
3553
3667
  var unitOptions = isNotEmpty(units) ? units : DEFAULT_DELAY_UNITS;
3554
3668
  usePaneValidation("".concat(name, ".value"));
3669
+ useEffect(function () {
3670
+ if ((value === null || value === void 0 ? void 0 : value.value) === undefined) return;
3671
+ if (getFieldMeta("".concat(name, ".value.value")).touched) return;
3672
+ setFieldTouched("".concat(name, ".value.value"), true, false);
3673
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3674
+ }, [value === null || value === void 0 ? void 0 : value.value]);
3555
3675
  useEffect(function () {
3556
3676
  if (isEmpty(value || {})) {
3557
3677
  var _unitOptions$0$value, _unitOptions$;
3558
3678
  setFieldValue("".concat(name, ".value"), {
3559
- value: 10,
3560
- 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"
3679
+ value: DEFAULT_DELAY_VALUE,
3680
+ 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
3561
3681
  });
3562
3682
  }
3563
3683
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -3575,6 +3695,7 @@ var DelayPane = function DelayPane(_ref) {
3575
3695
  children: /*#__PURE__*/jsxs("div", {
3576
3696
  className: "flex flex-col gap-y-1",
3577
3697
  children: [/*#__PURE__*/jsx(Typography, {
3698
+ "data-testid": "pane-title",
3578
3699
  variant: "h5",
3579
3700
  children: t("neetoRules.common.addDelay")
3580
3701
  }), /*#__PURE__*/jsx(Typography, {
@@ -3595,13 +3716,15 @@ var DelayPane = function DelayPane(_ref) {
3595
3716
  className: "flex items-start gap-x-2",
3596
3717
  children: [/*#__PURE__*/jsx(Input$1, {
3597
3718
  className: "w-44",
3598
- max: 999,
3599
- min: 1,
3719
+ dataTestid: "delay-value",
3720
+ max: MAX_DELAY_VALUE,
3721
+ min: MIN_DELAY_VALUE,
3600
3722
  name: "".concat(name, ".value.value"),
3601
3723
  placeholder: t("neetoRules.placeholders.enterDelay"),
3602
3724
  size: "large",
3603
3725
  type: "number"
3604
3726
  }), /*#__PURE__*/jsx(Select, {
3727
+ dataTestid: "delay-unit",
3605
3728
  defaultValue: value === null || value === void 0 ? void 0 : value.unit,
3606
3729
  name: "".concat(name, ".value.unit"),
3607
3730
  options: unitOptions,
@@ -3635,11 +3758,13 @@ var asText = function asText(value) {
3635
3758
  var NumberStepper = function NumberStepper(_ref) {
3636
3759
  var value = _ref.value,
3637
3760
  onChange = _ref.onChange,
3761
+ dataTestid = _ref.dataTestid,
3638
3762
  label = _ref.label,
3639
3763
  _ref$min = _ref.min,
3640
- min = _ref$min === void 0 ? 1 : _ref$min,
3764
+ min = _ref$min === void 0 ? STEPPER_MIN_VALUE : _ref$min,
3641
3765
  max = _ref.max,
3642
- required = _ref.required;
3766
+ required = _ref.required,
3767
+ error = _ref.error;
3643
3768
  var _useTranslation = useTranslation(),
3644
3769
  t = _useTranslation.t;
3645
3770
  var _useState = useState(null),
@@ -3664,15 +3789,41 @@ var NumberStepper = function NumberStepper(_ref) {
3664
3789
  max: max
3665
3790
  }));
3666
3791
  };
3667
- return /*#__PURE__*/jsxs("div", {
3792
+ return /*#__PURE__*/jsx("div", {
3668
3793
  className: "mb-stepper",
3669
- children: [/*#__PURE__*/jsx(Input, {
3794
+ children: /*#__PURE__*/jsx(Input, {
3795
+ dataTestid: dataTestid,
3796
+ error: error,
3670
3797
  label: label,
3671
3798
  min: min,
3672
3799
  required: required,
3673
3800
  autoFocus: true,
3674
3801
  type: "number",
3675
3802
  value: display,
3803
+ suffix: /*#__PURE__*/jsxs("div", {
3804
+ className: "mb-stepper-controls",
3805
+ children: [/*#__PURE__*/jsx("button", {
3806
+ "aria-label": t("neetoRules.builder.aria.increase"),
3807
+ className: "mb-stepper-btn",
3808
+ "data-testid": "stepper-increase",
3809
+ tabIndex: SKIP_TAB_INDEX,
3810
+ type: "button",
3811
+ onClick: function onClick() {
3812
+ return step(STEPPER_DELTA.INCREMENT);
3813
+ },
3814
+ children: /*#__PURE__*/jsx(Up, {})
3815
+ }), /*#__PURE__*/jsx("button", {
3816
+ "aria-label": t("neetoRules.builder.aria.decrease"),
3817
+ className: "mb-stepper-btn",
3818
+ "data-testid": "stepper-decrease",
3819
+ tabIndex: SKIP_TAB_INDEX,
3820
+ type: "button",
3821
+ onClick: function onClick() {
3822
+ return step(STEPPER_DELTA.DECREMENT);
3823
+ },
3824
+ children: /*#__PURE__*/jsx(Down, {})
3825
+ })]
3826
+ }),
3676
3827
  onBlur: function onBlur() {
3677
3828
  return setDraft(null);
3678
3829
  },
@@ -3682,28 +3833,7 @@ var NumberStepper = function NumberStepper(_ref) {
3682
3833
  onFocus: function onFocus(event) {
3683
3834
  return event.target.select();
3684
3835
  }
3685
- }), /*#__PURE__*/jsxs("div", {
3686
- className: "mb-stepper-controls",
3687
- children: [/*#__PURE__*/jsx("button", {
3688
- "aria-label": t("neetoRules.builder.aria.increase"),
3689
- className: "mb-stepper-btn",
3690
- tabIndex: -1,
3691
- type: "button",
3692
- onClick: function onClick() {
3693
- return step(1);
3694
- },
3695
- children: /*#__PURE__*/jsx(Up, {})
3696
- }), /*#__PURE__*/jsx("button", {
3697
- "aria-label": t("neetoRules.builder.aria.decrease"),
3698
- className: "mb-stepper-btn",
3699
- tabIndex: -1,
3700
- type: "button",
3701
- onClick: function onClick() {
3702
- return step(-1);
3703
- },
3704
- children: /*#__PURE__*/jsx(Down, {})
3705
- })]
3706
- })]
3836
+ })
3707
3837
  });
3708
3838
  };
3709
3839
 
@@ -3712,7 +3842,9 @@ var LeadTimePane = function LeadTimePane(_ref) {
3712
3842
  var _useTranslation = useTranslation(),
3713
3843
  t = _useTranslation.t;
3714
3844
  var _useFormikContext = useFormikContext(),
3715
- setFieldValue = _useFormikContext.setFieldValue;
3845
+ setFieldValue = _useFormikContext.setFieldValue,
3846
+ setFieldTouched = _useFormikContext.setFieldTouched,
3847
+ getFieldMeta = _useFormikContext.getFieldMeta;
3716
3848
  var _useRuleModel = useRuleModel(schema),
3717
3849
  rule = _useRuleModel.rule,
3718
3850
  fieldKeys = _useRuleModel.fieldKeys;
@@ -3720,6 +3852,11 @@ var LeadTimePane = function LeadTimePane(_ref) {
3720
3852
  amount = _minutesToParts.value,
3721
3853
  unit = _minutesToParts.unit;
3722
3854
  var path = "".concat(fieldKeys.leadTime, ".value");
3855
+ var _useField = useField(path),
3856
+ _useField2 = _slicedToArray(_useField, 2),
3857
+ _useField2$ = _useField2[1],
3858
+ error = _useField2$.error,
3859
+ touched = _useField2$.touched;
3723
3860
  var update = function update(_ref2) {
3724
3861
  var value = _ref2.value,
3725
3862
  nextUnit = _ref2.unit;
@@ -3728,11 +3865,19 @@ var LeadTimePane = function LeadTimePane(_ref) {
3728
3865
  unit: nextUnit
3729
3866
  }));
3730
3867
  };
3868
+ usePaneValidation(path);
3869
+ useEffect(function () {
3870
+ if (rule.leadTime === undefined) return;
3871
+ if (getFieldMeta(path).touched) return;
3872
+ setFieldTouched(path, true, false);
3873
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3874
+ }, [rule.leadTime]);
3731
3875
  return /*#__PURE__*/jsxs(Fragment, {
3732
3876
  children: [/*#__PURE__*/jsx(Panel.Header, {
3733
3877
  children: /*#__PURE__*/jsxs("div", {
3734
3878
  className: "flex flex-col gap-y-1",
3735
3879
  children: [/*#__PURE__*/jsx(Typography, {
3880
+ "data-testid": "pane-title",
3736
3881
  variant: "h5",
3737
3882
  children: t("neetoRules.builder.leadTimeTitle")
3738
3883
  }), /*#__PURE__*/jsx(Typography, {
@@ -3743,32 +3888,41 @@ var LeadTimePane = function LeadTimePane(_ref) {
3743
3888
  })
3744
3889
  }), /*#__PURE__*/jsx(Panel.Body, {
3745
3890
  children: /*#__PURE__*/jsxs("div", {
3746
- className: "flex items-end gap-x-3",
3747
- children: [/*#__PURE__*/jsx("div", {
3748
- className: "flex-1",
3749
- children: /*#__PURE__*/jsx(NumberStepper, {
3750
- label: t("neetoRules.builder.before"),
3751
- value: amount,
3752
- onChange: function onChange(value) {
3753
- return update({
3754
- value: value === "" ? 0 : value,
3755
- unit: unit
3756
- });
3757
- }
3758
- })
3759
- }), /*#__PURE__*/jsx("div", {
3760
- className: "w-32 shrink-0",
3761
- children: /*#__PURE__*/jsx(Select$1, {
3762
- isSearchable: false,
3763
- options: LEAD_TIME_UNITS,
3764
- value: unit,
3765
- onChange: function onChange(nextUnit) {
3766
- return update({
3767
- value: amount,
3768
- unit: nextUnit
3769
- });
3770
- }
3771
- })
3891
+ className: "flex flex-col gap-y-1.5",
3892
+ children: [/*#__PURE__*/jsx(Typography, {
3893
+ variant: "body2",
3894
+ weight: "medium",
3895
+ children: t("neetoRules.builder.before")
3896
+ }), /*#__PURE__*/jsxs("div", {
3897
+ className: "flex items-start gap-x-3",
3898
+ children: [/*#__PURE__*/jsx("div", {
3899
+ className: "flex-1",
3900
+ children: /*#__PURE__*/jsx(NumberStepper, {
3901
+ dataTestid: "lead-time",
3902
+ error: touched ? error : "",
3903
+ value: amount,
3904
+ onChange: function onChange(value) {
3905
+ return update({
3906
+ value: value === "" ? 0 : value,
3907
+ unit: unit
3908
+ });
3909
+ }
3910
+ })
3911
+ }), /*#__PURE__*/jsx("div", {
3912
+ className: "w-32 shrink-0",
3913
+ children: /*#__PURE__*/jsx(Select$1, {
3914
+ dataTestid: "lead-time-unit",
3915
+ isSearchable: false,
3916
+ options: LEAD_TIME_UNITS,
3917
+ value: unit,
3918
+ onChange: function onChange(nextUnit) {
3919
+ return update({
3920
+ value: amount,
3921
+ unit: nextUnit
3922
+ });
3923
+ }
3924
+ })
3925
+ })]
3772
3926
  })]
3773
3927
  })
3774
3928
  })]
@@ -3786,6 +3940,7 @@ var NamePane = function NamePane(_ref) {
3786
3940
  children: /*#__PURE__*/jsxs("div", {
3787
3941
  className: "flex flex-col gap-y-1",
3788
3942
  children: [/*#__PURE__*/jsx(Typography, {
3943
+ "data-testid": "pane-title",
3789
3944
  variant: "h5",
3790
3945
  children: t("neetoRules.builder.automationName")
3791
3946
  }), /*#__PURE__*/jsx(Typography, {
@@ -3963,12 +4118,13 @@ var OptionsPane = function OptionsPane(_ref) {
3963
4118
  if (isEmpty(filtered)) {
3964
4119
  return /*#__PURE__*/jsx("div", {
3965
4120
  className: "mb-pane-empty",
4121
+ "data-testid": "pane-no-matches",
3966
4122
  children: t("neetoRules.builder.noMatches")
3967
4123
  });
3968
4124
  }
3969
4125
  if (multi) {
3970
4126
  return /*#__PURE__*/jsx("div", {
3971
- className: "flex flex-col",
4127
+ className: "mb-pane-list flex flex-col",
3972
4128
  children: groups.map(function (_ref2) {
3973
4129
  var _ref3 = _slicedToArray(_ref2, 2),
3974
4130
  category = _ref3[0],
@@ -4020,6 +4176,7 @@ var OptionsPane = function OptionsPane(_ref) {
4020
4176
  children: /*#__PURE__*/jsxs("div", {
4021
4177
  className: "flex w-full flex-col gap-y-2",
4022
4178
  children: [/*#__PURE__*/jsx(Typography, {
4179
+ "data-testid": "pane-title",
4023
4180
  variant: "h5",
4024
4181
  children: title
4025
4182
  }), /*#__PURE__*/jsx(Typography, {
@@ -4030,6 +4187,7 @@ var OptionsPane = function OptionsPane(_ref) {
4030
4187
  className: "mb-pane-search",
4031
4188
  ref: searchRef,
4032
4189
  children: /*#__PURE__*/jsx(Input, {
4190
+ dataTestid: "pane-search",
4033
4191
  placeholder: t("neetoRules.builder.searchPlaceholder"),
4034
4192
  prefix: /*#__PURE__*/jsx(Search, {}),
4035
4193
  size: "large",
@@ -4044,6 +4202,7 @@ var OptionsPane = function OptionsPane(_ref) {
4044
4202
  children: [renderBody(), belowContent]
4045
4203
  }), cascade && /*#__PURE__*/jsx(Panel.Footer, {
4046
4204
  children: /*#__PURE__*/jsx(Button, {
4205
+ "data-testid": "continue-button",
4047
4206
  disabled: !canContinue,
4048
4207
  label: t("neetoRules.buttons.continue"),
4049
4208
  size: "lg",
@@ -4069,8 +4228,8 @@ var RecipientField = function RecipientField(_ref) {
4069
4228
  var fieldError = function fieldError(path) {
4070
4229
  return getIn(touched, path) && getIn(errors, path);
4071
4230
  };
4072
- if (field === "emails") {
4073
- var _path = "".concat(name, ".emails");
4231
+ if (field === RECIPIENT_STORAGE_FIELDS.emails) {
4232
+ var _path = "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.emails);
4074
4233
  var _current = getIn(values, _path) || [];
4075
4234
  return /*#__PURE__*/jsx(MultiEmailInput, {
4076
4235
  required: true,
@@ -4092,7 +4251,7 @@ var RecipientField = function RecipientField(_ref) {
4092
4251
  }
4093
4252
  });
4094
4253
  }
4095
- var path = "".concat(name, ".value");
4254
+ var path = "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.value);
4096
4255
  var current = getIn(values, path);
4097
4256
  var options = normalizeOptions(dropdownOptions);
4098
4257
  return /*#__PURE__*/jsx(Select$1, {
@@ -4136,7 +4295,7 @@ var RecipientPane = function RecipientPane(_ref) {
4136
4295
  });
4137
4296
  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));
4138
4297
  var isSmsToNumbers = (flatOption === null || flatOption === void 0 ? void 0 : flatOption.type) === SMS_TO_NUMBERS;
4139
- var recipientFieldPath = isSmsToNumbers ? "".concat(name, ".phoneNumbers") : "".concat(name, ".emails");
4298
+ var recipientFieldPath = isSmsToNumbers ? "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.phoneNumbers) : "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.emails);
4140
4299
  usePaneValidation(recipientFieldPath);
4141
4300
  useEffect(function () {
4142
4301
  var _bodyRef$current;
@@ -4167,7 +4326,7 @@ var RecipientPane = function RecipientPane(_ref) {
4167
4326
  action: action
4168
4327
  })) !== null && _schema$actions$rende !== void 0 ? _schema$actions$rende : builtInRecipientField;
4169
4328
  var phoneField = isSmsToNumbers ? /*#__PURE__*/jsx(PhoneNumberField, {
4170
- name: "".concat(name, ".phoneNumbers"),
4329
+ name: "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.phoneNumbers),
4171
4330
  phoneNumbers: (_action$metadata = action.metadata) === null || _action$metadata === void 0 ? void 0 : _action$metadata.phoneNumbers
4172
4331
  }) : null;
4173
4332
  var belowField = phoneField || recipientField;
@@ -4203,6 +4362,7 @@ var RecipientPane = function RecipientPane(_ref) {
4203
4362
  children: /*#__PURE__*/jsxs("div", {
4204
4363
  className: "flex w-full flex-col gap-y-2",
4205
4364
  children: [/*#__PURE__*/jsx(Typography, {
4365
+ "data-testid": "pane-title",
4206
4366
  variant: "h5",
4207
4367
  children: meta.recipientTitle || t("neetoRules.builder.panes.recipientTitle")
4208
4368
  }), /*#__PURE__*/jsx(Typography, {
@@ -4249,6 +4409,7 @@ var ConditionValueFields = function ConditionValueFields(_ref) {
4249
4409
  };
4250
4410
  var noMatches = /*#__PURE__*/jsx("div", {
4251
4411
  className: "mb-pane-empty",
4412
+ "data-testid": "pane-no-matches",
4252
4413
  children: t("neetoRules.builder.noMatches")
4253
4414
  });
4254
4415
  if (type === CONDITION_VALUE_TYPES.multiSelect && isNotEmpty(enumOptions)) {
@@ -4260,7 +4421,7 @@ var ConditionValueFields = function ConditionValueFields(_ref) {
4260
4421
  setFieldValue(valuesPath, current.includes(value) ? current.filter(isNot(value)) : [].concat(_toConsumableArray(current), [value]));
4261
4422
  };
4262
4423
  return /*#__PURE__*/jsxs("div", {
4263
- className: "flex flex-col gap-y-1",
4424
+ className: "mb-pane-list flex flex-col",
4264
4425
  children: [filtered.map(function (option) {
4265
4426
  return /*#__PURE__*/jsx(Checkbox, {
4266
4427
  checked: current.includes(option.value),
@@ -4404,6 +4565,7 @@ var ValuePane = function ValuePane(_ref) {
4404
4565
  children: /*#__PURE__*/jsxs("div", {
4405
4566
  className: "flex w-full flex-col gap-y-2",
4406
4567
  children: [/*#__PURE__*/jsx(Typography, {
4568
+ "data-testid": "pane-title",
4407
4569
  variant: "h5",
4408
4570
  children: t("neetoRules.builder.pickValue", {
4409
4571
  field: (field === null || field === void 0 ? void 0 : field.label) || t("neetoRules.builder.valueWord")
@@ -4418,6 +4580,7 @@ var ValuePane = function ValuePane(_ref) {
4418
4580
  className: "mb-pane-search",
4419
4581
  ref: searchRef,
4420
4582
  children: /*#__PURE__*/jsx(Input, {
4583
+ dataTestid: "pane-search",
4421
4584
  placeholder: t("neetoRules.builder.searchPlaceholder"),
4422
4585
  prefix: /*#__PURE__*/jsx(Search, {}),
4423
4586
  size: "large",
@@ -4564,7 +4727,7 @@ var Form = function Form(_ref) {
4564
4727
  data: data,
4565
4728
  customData: customData,
4566
4729
  isNameGenerationDisabled: isNameRequired
4567
- }),
4730
+ }).concat(leadTimeValidationSchema(schema)),
4568
4731
  onSubmit: submitForm,
4569
4732
  children: function children() {
4570
4733
  return /*#__PURE__*/jsxs("div", {