@bigbinary/neeto-rules-frontend 2.7.19 → 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 (88) 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/en.json +2 -1
  8. package/app/javascript/src/translations/es-MX.json +2 -1
  9. package/app/javascript/src/translations/es.json +2 -1
  10. package/app/javascript/src/translations/et.json +2 -1
  11. package/app/javascript/src/translations/fi.json +2 -1
  12. package/app/javascript/src/translations/fil.json +2 -1
  13. package/app/javascript/src/translations/fr.json +2 -1
  14. package/app/javascript/src/translations/he.json +2 -1
  15. package/app/javascript/src/translations/hi.json +2 -1
  16. package/app/javascript/src/translations/hr.json +2 -1
  17. package/app/javascript/src/translations/hu.json +2 -1
  18. package/app/javascript/src/translations/id.json +2 -1
  19. package/app/javascript/src/translations/it.json +2 -1
  20. package/app/javascript/src/translations/ja.json +2 -1
  21. package/app/javascript/src/translations/ko.json +2 -1
  22. package/app/javascript/src/translations/nl.json +2 -1
  23. package/app/javascript/src/translations/pl.json +2 -1
  24. package/app/javascript/src/translations/pt-BR.json +2 -1
  25. package/app/javascript/src/translations/pt.json +2 -1
  26. package/app/javascript/src/translations/ro.json +2 -1
  27. package/app/javascript/src/translations/ru.json +2 -1
  28. package/app/javascript/src/translations/sk.json +2 -1
  29. package/app/javascript/src/translations/sl.json +2 -1
  30. package/app/javascript/src/translations/sv.json +2 -1
  31. package/app/javascript/src/translations/th.json +2 -1
  32. package/app/javascript/src/translations/tr.json +2 -1
  33. package/app/javascript/src/translations/uk.json +2 -1
  34. package/app/javascript/src/translations/vi.json +2 -1
  35. package/app/javascript/src/translations/zh-CN.json +2 -1
  36. package/app/javascript/src/translations/zh-TW.json +2 -1
  37. package/dist/.ready +1 -1
  38. package/dist/NeetoRulesBuilder.js +79 -17
  39. package/dist/NeetoRulesBuilder.js.map +1 -1
  40. package/dist/NeetoRulesForm.js +2 -2
  41. package/dist/cjs/NeetoRulesBuilder.js +78 -16
  42. package/dist/cjs/NeetoRulesBuilder.js.map +1 -1
  43. package/dist/cjs/NeetoRulesForm.js +2 -2
  44. package/dist/cjs/index.js +2 -2
  45. package/dist/cjs/v2/NeetoRules.js +1 -1
  46. package/dist/cjs/v2/NeetoRulesBuilder.js +370 -154
  47. package/dist/cjs/v2/NeetoRulesBuilder.js.map +1 -1
  48. package/dist/cjs/v2/NeetoRulesForm.js +10 -8
  49. package/dist/cjs/v2/NeetoRulesForm.js.map +1 -1
  50. package/dist/cjs/v2/RulePreview.js +1 -1
  51. package/dist/cjs/v2/hooks.js +2 -2
  52. package/dist/cjs/v2/index.js +4 -4
  53. package/dist/{index-DwGEK-_1.js → index-49edM85x.js} +2 -2
  54. package/dist/{index-DwGEK-_1.js.map → index-49edM85x.js.map} +1 -1
  55. package/dist/{index-ogJeZ752.js → index-BZYwFjcS.js} +2 -2
  56. package/dist/{index-ogJeZ752.js.map → index-BZYwFjcS.js.map} +1 -1
  57. package/dist/{index-k-eGkX-R.js → index-CcbW7H2S.js} +67 -21
  58. package/dist/index-CcbW7H2S.js.map +1 -0
  59. package/dist/{index-Vt6GY_o0.js → index-Fukq1VSN.js} +69 -18
  60. package/dist/index-Fukq1VSN.js.map +1 -0
  61. package/dist/index.js +2 -2
  62. package/dist/keyboardShortcuts-DtnjcivY.js +96 -0
  63. package/dist/{keyboardShortcuts-DNOhEY8h.js.map → keyboardShortcuts-DtnjcivY.js.map} +1 -1
  64. package/dist/keyboardShortcuts-_OrtgF0A.js +92 -0
  65. package/dist/{keyboardShortcuts-SAk712q8.js.map → keyboardShortcuts-_OrtgF0A.js.map} +1 -1
  66. package/dist/{useCustomDataStore-CU3KpyD4.js → useCustomDataStore-CEbhXugO.js} +2 -2
  67. package/dist/{useCustomDataStore-CU3KpyD4.js.map → useCustomDataStore-CEbhXugO.js.map} +1 -1
  68. package/dist/{useCustomDataStore-D9k8F5g-.js → useCustomDataStore-DPmWkEhs.js} +2 -2
  69. package/dist/{useCustomDataStore-D9k8F5g-.js.map → useCustomDataStore-DPmWkEhs.js.map} +1 -1
  70. package/dist/{useUtilityStore-BBb39TOj.js → useUtilityStore-BVCulMEC.js} +9 -2
  71. package/dist/useUtilityStore-BVCulMEC.js.map +1 -0
  72. package/dist/{useUtilityStore-DkxAexDw.js → useUtilityStore-gtPhFvpI.js} +9 -2
  73. package/dist/useUtilityStore-gtPhFvpI.js.map +1 -0
  74. package/dist/v2/NeetoRules.js +1 -1
  75. package/dist/v2/NeetoRulesBuilder.js +352 -155
  76. package/dist/v2/NeetoRulesBuilder.js.map +1 -1
  77. package/dist/v2/NeetoRulesForm.js +10 -8
  78. package/dist/v2/NeetoRulesForm.js.map +1 -1
  79. package/dist/v2/RulePreview.js +1 -1
  80. package/dist/v2/hooks.js +2 -2
  81. package/dist/v2/index.js +4 -4
  82. package/package.json +7 -7
  83. package/dist/index-Vt6GY_o0.js.map +0 -1
  84. package/dist/index-k-eGkX-R.js.map +0 -1
  85. package/dist/keyboardShortcuts-DNOhEY8h.js +0 -88
  86. package/dist/keyboardShortcuts-SAk712q8.js +0 -92
  87. package/dist/useUtilityStore-BBb39TOj.js.map +0 -1
  88. package/dist/useUtilityStore-DkxAexDw.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
2
2
  import _objectDestructuringEmpty from '@babel/runtime/helpers/objectDestructuringEmpty';
3
- import { f as flatten } from '../keyboardShortcuts-DNOhEY8h.js';
3
+ import { f as flatten, S as SHORTCUTS } from '../keyboardShortcuts-_OrtgF0A.js';
4
4
  import { useShallow } from 'zustand/shallow';
5
- import { useState, createElement, Fragment as Fragment$1, useCallback, useMemo, useRef, useEffect } from 'react';
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';
7
7
  import { Input as Input$1, Select, ActionBlock } from '@bigbinary/neeto-atoms/formik';
8
8
  import classNames from 'classnames';
@@ -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-k-eGkX-R.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,8 +22,9 @@ 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 { withT } from '@bigbinary/neeto-commons-frontend/v2/react-utils';
25
+ import * as yup from 'yup';
26
26
  import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator';
27
+ import useHotkeys from '@bigbinary/neeto-hotkeys';
27
28
  import Search from '@bigbinary/neeto-icons/Search';
28
29
  import { dayjs } from '@bigbinary/neeto-commons-frontend/v2/utils';
29
30
  import { D as DATE_FORMAT } from '../utils-DeqHhpwQ.js';
@@ -35,12 +36,11 @@ 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
+ import '@bigbinary/neeto-commons-frontend/v2/react-utils';
40
41
  import '@monaco-editor/react';
41
42
  import '@bigbinary/neeto-molecules/v2/DynamicVariables';
42
43
  import '@bigbinary/neeto-editor/v2/FormikEditor';
43
- import '@bigbinary/neeto-hotkeys';
44
44
  import 'react-select/creatable';
45
45
  import '@babel/runtime/helpers/toArray';
46
46
  import '@bigbinary/neeto-molecules/v2/PhoneNumber';
@@ -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() {
@@ -212,6 +232,21 @@ var TOKEN_STATE = {
212
232
  };
213
233
  var STATE_CLASS = _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, TOKEN_STATE.PLACEHOLDER, "mb-placeholder"), TOKEN_STATE.PLACEHOLDER_NEXT, "mb-placeholder mb-placeholder-next"), TOKEN_STATE.DISABLED, "mb-placeholder mb-placeholder-disabled"), TOKEN_STATE.FILLED, "mb-tok"), TOKEN_STATE.MUTED, "mb-tok mb-tok-muted");
214
234
 
235
+ // The flash is a run-once CSS animation, so a repeat submit while it is still
236
+ // applied would not replay it on its own.
237
+ var useReplayFlash = function useReplayFlash(flashSeq) {
238
+ var ref = useRef(null);
239
+ useEffect(function () {
240
+ var _ref$current, _ref$current$getAnima;
241
+ if (!flashSeq) return;
242
+ (_ref$current = ref.current) === null || _ref$current === void 0 || (_ref$current$getAnima = _ref$current.getAnimations) === null || _ref$current$getAnima === void 0 || _ref$current$getAnima.call(_ref$current).forEach(function (animation) {
243
+ animation.cancel();
244
+ animation.play();
245
+ });
246
+ }, [flashSeq]);
247
+ return ref;
248
+ };
249
+
215
250
  var _excluded = ["children", "state", "active", "invalid", "onClick", "className", "dataTestid"];
216
251
  function ownKeys$c(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; }
217
252
  function _objectSpread$c(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$c(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$c(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -228,8 +263,10 @@ var Token = function Token(_ref) {
228
263
  dataTestid = _ref.dataTestid,
229
264
  rest = _objectWithoutProperties(_ref, _excluded);
230
265
  var isInteractive = typeof onClick === "function" && state !== TOKEN_STATE.DISABLED;
266
+ var tokenRef = useReplayFlash(invalid);
231
267
  return /*#__PURE__*/jsx("span", _objectSpread$c(_objectSpread$c({
232
268
  "data-testid": dataTestid,
269
+ ref: tokenRef,
233
270
  role: isInteractive ? "button" : undefined,
234
271
  tabIndex: isInteractive ? 0 : undefined,
235
272
  className: classNames(
@@ -535,6 +572,25 @@ var partsToMinutes = function partsToMinutes(_ref19) {
535
572
  (Number(value) || 0) * (UNIT_MINUTES[unit] || 1)
536
573
  );
537
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
+ };
538
594
  var leadTimeLabel = function leadTimeLabel(minutes) {
539
595
  var _minutesToParts = minutesToParts(minutes),
540
596
  value = _minutesToParts.value,
@@ -557,6 +613,9 @@ var normalizeSchema = function normalizeSchema() {
557
613
  var joinTypeFor = function joinTypeFor(mode) {
558
614
  return mode === CONDITION_MODE.OR ? JOIN_TYPE.or : JOIN_TYPE.and;
559
615
  };
616
+ var recipientStorageField = function recipientStorageField(field) {
617
+ return field === RECIPIENT_STORAGE_FIELDS.emails ? RECIPIENT_STORAGE_FIELDS.emails : RECIPIENT_STORAGE_FIELDS.value;
618
+ };
560
619
  var modeFromJoinType = function modeFromJoinType(joinType) {
561
620
  return joinType === JOIN_TYPE.or ? CONDITION_MODE.OR : CONDITION_MODE.AND;
562
621
  };
@@ -572,15 +631,15 @@ var asArray = function asArray(maybeArray) {
572
631
  return Array.isArray(maybeArray) ? maybeArray : [];
573
632
  };
574
633
  var fieldKeysFromSchema = function fieldKeysFromSchema() {
575
- 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;
576
635
  var schema = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
577
636
  return {
578
- 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",
579
638
  performer: ((_schema$actor2 = schema.actor) === null || _schema$actor2 === void 0 ? void 0 : _schema$actor2.fieldKey) || "performer",
580
639
  conditions: ((_schema$conditions3 = schema.conditions) === null || _schema$conditions3 === void 0 ? void 0 : _schema$conditions3.fieldKey) || "conditions",
581
640
  actions: ((_schema$actions3 = schema.actions) === null || _schema$actions3 === void 0 ? void 0 : _schema$actions3.fieldKey) || "actions",
582
641
  delay: ((_schema$delay = schema.delay) === null || _schema$delay === void 0 ? void 0 : _schema$delay.fieldKey) || "execution_delay",
583
- 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,
584
643
  scope: ((_schema$scope2 = schema.scope) === null || _schema$scope2 === void 0 ? void 0 : _schema$scope2.fieldKey) || "meetingIds",
585
644
  status: "status",
586
645
  name: "name"
@@ -733,6 +792,35 @@ var removeArrayItem = function removeArrayItem(_ref30) {
733
792
  }
734
793
  return result;
735
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
+ };
736
824
 
737
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; }
738
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; }
@@ -992,15 +1080,17 @@ var DelayRow = function DelayRow(_ref) {
992
1080
  });
993
1081
  };
994
1082
 
995
- var MetaBlock = withT(function (_ref) {
996
- var t = _ref.t,
997
- rule = _ref.rule,
1083
+ var MetaBlock = function MetaBlock(_ref) {
1084
+ var rule = _ref.rule,
998
1085
  _ref$showName = _ref.showName,
999
1086
  showName = _ref$showName === void 0 ? true : _ref$showName,
1000
1087
  _ref$isInvalid = _ref.isInvalid,
1001
1088
  isInvalid = _ref$isInvalid === void 0 ? false : _ref$isInvalid,
1002
1089
  handleEditName = _ref.onEditName,
1003
1090
  handleToggleActive = _ref.onToggleActive;
1091
+ var _useTranslation = useTranslation(),
1092
+ t = _useTranslation.t;
1093
+ var nameRef = useReplayFlash(isInvalid);
1004
1094
  return /*#__PURE__*/jsxs("div", {
1005
1095
  className: "mb-meta",
1006
1096
  children: [showName && /*#__PURE__*/jsxs("div", {
@@ -1009,6 +1099,8 @@ var MetaBlock = withT(function (_ref) {
1009
1099
  className: "mb-meta-label",
1010
1100
  children: t("neetoRules.builder.automationName")
1011
1101
  }), /*#__PURE__*/jsxs("div", {
1102
+ ref: nameRef,
1103
+ "data-testid": "rule-name-edit",
1012
1104
  role: handleEditName ? "button" : undefined,
1013
1105
  tabIndex: handleEditName ? 0 : undefined,
1014
1106
  className: classNames("mb-meta-input-wrap", {
@@ -1017,6 +1109,7 @@ var MetaBlock = withT(function (_ref) {
1017
1109
  onClick: handleEditName,
1018
1110
  children: [/*#__PURE__*/jsx("span", {
1019
1111
  className: "mb-meta-name-display",
1112
+ "data-testid": "rule-name",
1020
1113
  children: rule.name
1021
1114
  }), /*#__PURE__*/jsx("span", {
1022
1115
  "aria-hidden": true,
@@ -1038,8 +1131,10 @@ var MetaBlock = withT(function (_ref) {
1038
1131
  })]
1039
1132
  }), /*#__PURE__*/jsxs("label", {
1040
1133
  className: "mb-meta-active",
1134
+ "data-testid": "rule-active-label",
1041
1135
  children: [/*#__PURE__*/jsx("input", {
1042
1136
  checked: rule.active,
1137
+ "data-testid": "rule-active-checkbox",
1043
1138
  readOnly: !handleToggleActive,
1044
1139
  type: "checkbox",
1045
1140
  onChange: handleToggleActive || undefined
@@ -1058,7 +1153,7 @@ var MetaBlock = withT(function (_ref) {
1058
1153
  }), t("neetoRules.builder.activateThisAutomation")]
1059
1154
  })]
1060
1155
  });
1061
- });
1156
+ };
1062
1157
 
1063
1158
  function ownKeys$8(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; }
1064
1159
  function _objectSpread$8(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$8(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$8(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -1297,6 +1392,8 @@ var ActionRow = function ActionRow(_ref) {
1297
1392
  });
1298
1393
  var configLabel = mediumLabel || sentencify(option === null || option === void 0 ? void 0 : option.placeholder);
1299
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;
1300
1397
  var _useClearingHover = useClearingHover(),
1301
1398
  clearing = _useClearingHover.clearing,
1302
1399
  clearingHoverProps = _useClearingHover.clearingHoverProps;
@@ -1335,7 +1432,7 @@ var ActionRow = function ActionRow(_ref) {
1335
1432
  children: verbLabel
1336
1433
  }), hasParamTail ? /*#__PURE__*/jsx(Token, {
1337
1434
  dataTestid: "action-config-value-".concat(index),
1338
- state: configValue ? TOKEN_STATE.FILLED : TOKEN_STATE.MUTED,
1435
+ state: configState,
1339
1436
  active: isActive({
1340
1437
  type: BUILDER_ANCHORS.ACTION_CONFIG,
1341
1438
  index: index
@@ -1383,7 +1480,7 @@ var ActionRow = function ActionRow(_ref) {
1383
1480
  index: index
1384
1481
  }),
1385
1482
  dataTestid: "action-config-value-".concat(index),
1386
- state: configValue ? TOKEN_STATE.FILLED : TOKEN_STATE.MUTED,
1483
+ state: configState,
1387
1484
  invalid: isInvalid({
1388
1485
  type: BUILDER_ANCHORS.ACTION_CONFIG,
1389
1486
  index: index
@@ -1606,6 +1703,10 @@ var WhenBlock = function WhenBlock(_ref) {
1606
1703
  type: BUILDER_ANCHORS.TRIGGER_LEAD_TIME,
1607
1704
  index: index
1608
1705
  }),
1706
+ invalid: isInvalid({
1707
+ type: BUILDER_ANCHORS.TRIGGER_LEAD_TIME,
1708
+ index: index
1709
+ }),
1609
1710
  onClick: handleToken && function () {
1610
1711
  return handleToken({
1611
1712
  type: BUILDER_ANCHORS.TRIGGER_LEAD_TIME,
@@ -1816,6 +1917,24 @@ var useRuleModel = function useRuleModel(schema) {
1816
1917
  fieldKey: fieldKeysFromSchema(schema).actions,
1817
1918
  index: index
1818
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
+ }));
1819
1938
  }
1820
1939
  };
1821
1940
  };
@@ -1962,9 +2081,9 @@ var nextTriggers = function nextTriggers(_ref10) {
1962
2081
  name: value
1963
2082
  }]);
1964
2083
  }
1965
- return [{
2084
+ return [_objectSpread$4(_objectSpread$4({}, rule.triggers[0]), {}, {
1966
2085
  name: value
1967
- }];
2086
+ })];
1968
2087
  };
1969
2088
  var nextConditionAt = function nextConditionAt(_ref11) {
1970
2089
  var _field$allowMatching;
@@ -2211,7 +2330,7 @@ var descriptorFor = function descriptorFor(_ref15) {
2211
2330
  case BUILDER_ANCHORS.ACTION_ADD:
2212
2331
  case BUILDER_ANCHORS.ACTION_CHANNEL:
2213
2332
  {
2214
- var _schema$actions11, _schema$actions12, _channelOf;
2333
+ var _schema$actions11, _channelOf;
2215
2334
  var isSwap = anchor === BUILDER_ANCHORS.ACTION_CHANNEL;
2216
2335
  var channelMetaFor = function channelMetaFor(value) {
2217
2336
  var _schema$actions10;
@@ -2220,13 +2339,10 @@ var descriptorFor = function descriptorFor(_ref15) {
2220
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]) || {}
2221
2340
  );
2222
2341
  };
2223
- var rank = function rank(value) {
2224
- var idx = SWAP_CHANNEL_ORDER.indexOf(value);
2225
- return idx === -1 ? SWAP_CHANNEL_ORDER.length : idx;
2226
- };
2227
- var sourceOptions = isSwap ? _toConsumableArray(((_schema$actions11 = schema.actions) === null || _schema$actions11 === void 0 ? void 0 : _schema$actions11.options) || []).sort(function (optionA, optionB) {
2228
- return rank(optionValue(optionA)) - rank(optionValue(optionB));
2229
- }) : ((_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) || [];
2230
2346
  return {
2231
2347
  title: isSwap ? t("neetoRules.builder.panes.actionSwapTitle") : t("neetoRules.builder.panes.actionAddTitle"),
2232
2348
  subtitle: isSwap ? t("neetoRules.builder.panes.actionSwapSubtitle") : t("neetoRules.builder.panes.actionAddSubtitle"),
@@ -2256,7 +2372,7 @@ var descriptorFor = function descriptorFor(_ref15) {
2256
2372
  }
2257
2373
  case BUILDER_ANCHORS.ACTION_RECIPIENT:
2258
2374
  {
2259
- var _schema$actions13;
2375
+ var _schema$actions12;
2260
2376
  var action = actionAt({
2261
2377
  rule: rule,
2262
2378
  index: index
@@ -2266,7 +2382,7 @@ var descriptorFor = function descriptorFor(_ref15) {
2266
2382
  action: action
2267
2383
  });
2268
2384
  // eslint-disable-next-line security/detect-object-injection
2269
- 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]) || {};
2270
2386
  var option = findActionOption({
2271
2387
  schema: schema,
2272
2388
  name: channel
@@ -2382,9 +2498,9 @@ var commitFor = function commitFor(_ref16) {
2382
2498
  }]));
2383
2499
  return "stay";
2384
2500
  }
2385
- setTriggers([{
2501
+ setTriggers([_objectSpread$4(_objectSpread$4({}, rule.triggers[0]), {}, {
2386
2502
  name: value
2387
- }]);
2503
+ })]);
2388
2504
  var leadTime = (_schema$triggers5 = schema.triggers) === null || _schema$triggers5 === void 0 ? void 0 : _schema$triggers5.leadTime;
2389
2505
  if (leadTime !== null && leadTime !== void 0 && leadTime.fieldKey && (leadTime.appliesTo || []).includes(value) && !rule.leadTime) {
2390
2506
  var _leadTime$default;
@@ -2651,7 +2767,7 @@ var applyPreview = function applyPreview(_ref19) {
2651
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; }
2652
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; }
2653
2769
  var Canvas = function Canvas(_ref) {
2654
- 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;
2655
2771
  var schema = _ref.schema;
2656
2772
  var _useTranslation = useTranslation(),
2657
2773
  t = _useTranslation.t;
@@ -2664,15 +2780,16 @@ var Canvas = function Canvas(_ref) {
2664
2780
  var _useRuleModel = useRuleModel(schema),
2665
2781
  rule = _useRuleModel.rule,
2666
2782
  fieldKeys = _useRuleModel.fieldKeys,
2667
- setTriggers = _useRuleModel.setTriggers,
2668
2783
  setPerformer = _useRuleModel.setPerformer,
2669
2784
  setConditions = _useRuleModel.setConditions,
2670
- setActions = _useRuleModel.setActions,
2671
2785
  setDelay = _useRuleModel.setDelay,
2672
2786
  removeCondition = _useRuleModel.removeCondition,
2673
2787
  removeAction = _useRuleModel.removeAction,
2674
2788
  setActive = _useRuleModel.setActive,
2675
- setScope = _useRuleModel.setScope;
2789
+ setScope = _useRuleModel.setScope,
2790
+ clearTriggers = _useRuleModel.clearTriggers,
2791
+ clearConditions = _useRuleModel.clearConditions,
2792
+ clearActions = _useRuleModel.clearActions;
2676
2793
  var _useUtilityStore = useUtilityStore(useShallow(function (store) {
2677
2794
  return {
2678
2795
  builderPreview: store["builderPreview"],
@@ -2697,10 +2814,10 @@ var Canvas = function Canvas(_ref) {
2697
2814
  var flatErrors = useMemo(function () {
2698
2815
  return flatten(errors || {});
2699
2816
  }, [errors]);
2700
- var _useState = useState(false),
2817
+ var _useState = useState(0),
2701
2818
  _useState2 = _slicedToArray(_useState, 2),
2702
- flashing = _useState2[0],
2703
- setFlashing = _useState2[1];
2819
+ flashSeq = _useState2[0],
2820
+ setFlashSeq = _useState2[1];
2704
2821
  var lastSubmitCount = useRef(submitCount);
2705
2822
  useEffect(function () {
2706
2823
  if (submitCount <= lastSubmitCount.current) return undefined;
@@ -2714,10 +2831,10 @@ var Canvas = function Canvas(_ref) {
2714
2831
  }))) {
2715
2832
  return undefined;
2716
2833
  }
2717
- setFlashing(true);
2834
+ setFlashSeq(submitCount);
2718
2835
  var timer = setTimeout(function () {
2719
- return setFlashing(false);
2720
- }, 2200);
2836
+ return setFlashSeq(0);
2837
+ }, FLASH_DURATION_MS);
2721
2838
  return function () {
2722
2839
  return clearTimeout(timer);
2723
2840
  };
@@ -2733,11 +2850,10 @@ var Canvas = function Canvas(_ref) {
2733
2850
  });
2734
2851
  });
2735
2852
  };
2736
- var isInvalid = function isInvalid(_ref4) {
2737
- 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;
2853
+ var matchesInvalid = function matchesInvalid(_ref4) {
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;
2738
2855
  var type = _ref4.type,
2739
2856
  index = _ref4.index;
2740
- if (!flashing) return false;
2741
2857
  var conditionMeta = "".concat(fieldKeys.conditions, ".value.").concat(index, ".metadata");
2742
2858
  var actionMeta = "".concat(fieldKeys.actions, ".value.").concat(index, ".metadata");
2743
2859
  // eslint-disable-next-line security/detect-object-injection
@@ -2755,7 +2871,13 @@ var Canvas = function Canvas(_ref) {
2755
2871
  action: actionItem
2756
2872
  }));
2757
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));
2758
- 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] : []));
2759
2881
  switch (type) {
2760
2882
  case BUILDER_ANCHORS.TRIGGER_ACTOR:
2761
2883
  {
@@ -2765,6 +2887,10 @@ var Canvas = function Canvas(_ref) {
2765
2887
  prefix: "".concat(actorKey, ".value")
2766
2888
  });
2767
2889
  }
2890
+ case BUILDER_ANCHORS.TRIGGER_LEAD_TIME:
2891
+ return hasErrorAt({
2892
+ prefix: "".concat(fieldKeys.leadTime, ".value")
2893
+ });
2768
2894
  case BUILDER_ANCHORS.COND_FIELD:
2769
2895
  return hasErrorAt({
2770
2896
  prefix: "".concat(fieldKeys.conditions, ".value.").concat(index, ".field")
@@ -2785,7 +2911,9 @@ var Canvas = function Canvas(_ref) {
2785
2911
  exclude: recipientFields
2786
2912
  });
2787
2913
  case BUILDER_ANCHORS.ACTION_RECIPIENT:
2788
- return recipientFields.some(function (field) {
2914
+ return hasErrorAt({
2915
+ prefix: "".concat(fieldKeys.actions, ".value.").concat(index, ".name")
2916
+ }) || recipientFields.some(function (field) {
2789
2917
  return hasErrorAt({
2790
2918
  prefix: "".concat(actionMeta, ".").concat(field)
2791
2919
  });
@@ -2816,6 +2944,12 @@ var Canvas = function Canvas(_ref) {
2816
2944
  return false;
2817
2945
  }
2818
2946
  };
2947
+
2948
+ // Returns the submit generation, not a boolean, so consumers can replay the
2949
+ // flash animation when the same token is still invalid on a repeat submit.
2950
+ var isInvalid = function isInvalid(args) {
2951
+ return flashSeq && matchesInvalid(args) ? flashSeq : 0;
2952
+ };
2819
2953
  var closePaneIfTargeting = function closePaneIfTargeting(_ref5) {
2820
2954
  var anchors = _ref5.anchors,
2821
2955
  index = _ref5.index;
@@ -2857,8 +2991,6 @@ var Canvas = function Canvas(_ref) {
2857
2991
  return (action === null || action === void 0 ? void 0 : action.channel) || (action === null || action === void 0 ? void 0 : action.name);
2858
2992
  });
2859
2993
  var isScopeEnabled = (schema === null || schema === void 0 || (_schema$scope = schema.scope) === null || _schema$scope === void 0 ? void 0 : _schema$scope.enabled) && isTriggerSet && hasAction;
2860
- var isScopeComplete = !(schema !== null && schema !== void 0 && (_schema$scope2 = schema.scope) !== null && _schema$scope2 !== void 0 && _schema$scope2.enabled) || isNotEmpty(rule.scope || []);
2861
- var isComplete = isRuleComplete(rule) && isScopeComplete;
2862
2994
  var handleRemoveScope = function handleRemoveScope(index) {
2863
2995
  closePaneIfTargeting({
2864
2996
  anchors: [BUILDER_ANCHORS.SCOPE_PHONE_FIELD],
@@ -2892,12 +3024,12 @@ var Canvas = function Canvas(_ref) {
2892
3024
  };
2893
3025
  var handleClearTrigger = function handleClearTrigger() {
2894
3026
  var _schema$actor2;
2895
- setTriggers([]);
3027
+ clearTriggers();
2896
3028
  // Hosts without an actor pane can never re-set it, and null fails its validation.
2897
3029
  if ((_schema$actor2 = schema.actor) !== null && _schema$actor2 !== void 0 && _schema$actor2.enabled) setPerformer(null);
2898
- setConditions([]);
3030
+ clearConditions();
2899
3031
  setDelay(null);
2900
- setActions([]);
3032
+ clearActions();
2901
3033
  setScope([]);
2902
3034
  closePane();
2903
3035
  };
@@ -2919,7 +3051,7 @@ var Canvas = function Canvas(_ref) {
2919
3051
  );
2920
3052
  };
2921
3053
  return /*#__PURE__*/jsx("div", {
2922
- className: "neeto-rules-builder",
3054
+ className: "neeto-rules-builder nrb-atoms",
2923
3055
  children: /*#__PURE__*/jsxs("div", {
2924
3056
  className: "mb-canvas neeto-rules-main-card",
2925
3057
  children: [/*#__PURE__*/jsx(WhenBlock, {
@@ -2961,6 +3093,7 @@ var Canvas = function Canvas(_ref) {
2961
3093
  }) : /*#__PURE__*/jsx("div", {
2962
3094
  className: "mb-add-row",
2963
3095
  children: /*#__PURE__*/jsx(Button, {
3096
+ "data-testid": "rule-delay-add",
2964
3097
  icon: Plus,
2965
3098
  iconPosition: "left",
2966
3099
  label: t("neetoRules.common.addDelay"),
@@ -2992,24 +3125,22 @@ var Canvas = function Canvas(_ref) {
2992
3125
  onRemoveScope: handleRemoveScope,
2993
3126
  onToken: openPane
2994
3127
  })]
2995
- }), isComplete && /*#__PURE__*/jsxs(Fragment$1, {
2996
- children: [/*#__PURE__*/jsx(Connector, {
2997
- active: true
2998
- }), /*#__PURE__*/jsx(MetaBlock, {
2999
- rule: rule,
3000
- 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({
3001
3138
  type: BUILDER_ANCHORS.META_NAME
3002
- }),
3003
- 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,
3004
- onEditName: function onEditName() {
3005
- return openPane({
3006
- type: BUILDER_ANCHORS.META_NAME
3007
- });
3008
- },
3009
- onToggleActive: function onToggleActive(event) {
3010
- return setActive(event.target.checked);
3011
- }
3012
- })]
3139
+ });
3140
+ },
3141
+ onToggleActive: function onToggleActive(event) {
3142
+ return setActive(event.target.checked);
3143
+ }
3013
3144
  })]
3014
3145
  })
3015
3146
  });
@@ -3020,6 +3151,7 @@ var ACTION_CONFIG_TYPES = {
3020
3151
  multiSelect: "multiSelect",
3021
3152
  dropdown: "dropdown",
3022
3153
  note: "note",
3154
+ list: "list",
3023
3155
  textarea: "textarea",
3024
3156
  number: "number",
3025
3157
  text: "text",
@@ -3081,6 +3213,7 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3081
3213
  };
3082
3214
  var noMatches = /*#__PURE__*/jsx("div", {
3083
3215
  className: "mb-pane-empty",
3216
+ "data-testid": "pane-no-matches",
3084
3217
  children: t("neetoRules.builder.noMatches")
3085
3218
  });
3086
3219
  if (type === ACTION_CONFIG_TYPES.emailChannel) {
@@ -3089,7 +3222,7 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3089
3222
  var bodyPath = "".concat(name, ".body");
3090
3223
  return /*#__PURE__*/jsxs("div", {
3091
3224
  className: "flex flex-col gap-4",
3092
- children: [/*#__PURE__*/jsx(Input, {
3225
+ children: [!(option !== null && option !== void 0 && option.hideSubject) && /*#__PURE__*/jsx(Input, {
3093
3226
  required: true,
3094
3227
  error: fieldError(subjectPath),
3095
3228
  label: t("neetoRules.builder.config.subject"),
@@ -3122,7 +3255,7 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3122
3255
  setFieldValue(valuePath, current.includes(value) ? current.filter(isNot(value)) : [].concat(_toConsumableArray(current), [value]));
3123
3256
  };
3124
3257
  return /*#__PURE__*/jsxs("div", {
3125
- className: "flex flex-col gap-y-1",
3258
+ className: "mb-pane-list flex flex-col",
3126
3259
  children: [filtered.map(function (item) {
3127
3260
  return /*#__PURE__*/jsx(Checkbox, {
3128
3261
  checked: current.includes(item.value),
@@ -3164,6 +3297,14 @@ var ActionConfigFields = function ActionConfigFields(_ref) {
3164
3297
  })
3165
3298
  });
3166
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
+ }
3167
3308
  if (type === ACTION_CONFIG_TYPES.note) {
3168
3309
  var _action$metadata2;
3169
3310
  return /*#__PURE__*/jsxs("div", {
@@ -3409,7 +3550,7 @@ var ConfigPane = function ConfigPane(_ref) {
3409
3550
  while (1) switch (_context.n) {
3410
3551
  case 0:
3411
3552
  _context.n = 1;
3412
- return validate(["emails"]);
3553
+ return validate([RECIPIENT_STORAGE_FIELDS.emails]);
3413
3554
  case 1:
3414
3555
  if (_context.v) {
3415
3556
  _context.n = 2;
@@ -3437,11 +3578,15 @@ var ConfigPane = function ConfigPane(_ref) {
3437
3578
  return _ref2.apply(this, arguments);
3438
3579
  };
3439
3580
  }();
3581
+ useHotkeys(SHORTCUTS["continue"].sequence, handleContinue, {
3582
+ mode: "global"
3583
+ });
3440
3584
  return /*#__PURE__*/jsxs(Fragment, {
3441
3585
  children: [/*#__PURE__*/jsx(Panel.Header, {
3442
3586
  children: /*#__PURE__*/jsxs("div", {
3443
3587
  className: "flex w-full flex-col gap-y-2",
3444
3588
  children: [/*#__PURE__*/jsx(Typography, {
3589
+ "data-testid": "pane-title",
3445
3590
  variant: "h5",
3446
3591
  children: title
3447
3592
  }), configSubtitle && /*#__PURE__*/jsx(Typography, {
@@ -3451,6 +3596,7 @@ var ConfigPane = function ConfigPane(_ref) {
3451
3596
  }), showSearch && /*#__PURE__*/jsx("div", {
3452
3597
  className: "mb-pane-search",
3453
3598
  children: /*#__PURE__*/jsx(Input, {
3599
+ dataTestid: "pane-search",
3454
3600
  placeholder: t("neetoRules.builder.searchPlaceholder"),
3455
3601
  prefix: /*#__PURE__*/jsx(Search, {}),
3456
3602
  size: "large",
@@ -3470,20 +3616,14 @@ var ConfigPane = function ConfigPane(_ref) {
3470
3616
  message: disabledReason,
3471
3617
  selectedField: flatOption
3472
3618
  })
3473
- }), isSms ? /*#__PURE__*/jsxs(Fragment, {
3474
- children: [/*#__PURE__*/jsx(SmsFields, {
3475
- action: action,
3476
- index: index,
3477
- name: name,
3478
- shouldAutoFocus: true,
3479
- selectedField: flatOption,
3480
- showPhoneNumberInput: false
3481
- }), /*#__PURE__*/jsx(Typography, {
3482
- className: "mt-2",
3483
- color: "muted",
3484
- variant: "body3",
3485
- children: t("neetoRules.builder.config.smsSegmentHint")
3486
- })]
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
3487
3627
  }) : (_hostRenderConfig = hostRenderConfig === null || hostRenderConfig === void 0 ? void 0 : hostRenderConfig({
3488
3628
  name: name,
3489
3629
  action: action,
@@ -3496,8 +3636,9 @@ var ConfigPane = function ConfigPane(_ref) {
3496
3636
  option: flatOption
3497
3637
  })]
3498
3638
  })
3499
- }), shouldAdvanceToRecipient && /*#__PURE__*/jsx(Panel.Footer, {
3639
+ }), /*#__PURE__*/jsx(Panel.Footer, {
3500
3640
  children: /*#__PURE__*/jsx(Button, {
3641
+ "data-testid": "continue-button",
3501
3642
  label: t("neetoRules.buttons.continue"),
3502
3643
  size: "lg",
3503
3644
  onClick: handleContinue
@@ -3514,7 +3655,9 @@ var DelayPane = function DelayPane(_ref) {
3514
3655
  var _useRuleModel = useRuleModel(schema),
3515
3656
  fieldKeys = _useRuleModel.fieldKeys;
3516
3657
  var _useFormikContext = useFormikContext(),
3517
- setFieldValue = _useFormikContext.setFieldValue;
3658
+ setFieldValue = _useFormikContext.setFieldValue,
3659
+ setFieldTouched = _useFormikContext.setFieldTouched,
3660
+ getFieldMeta = _useFormikContext.getFieldMeta;
3518
3661
  var bodyRef = useRef(null);
3519
3662
  var name = fieldKeys.delay;
3520
3663
  var _useField = useField("".concat(name, ".value")),
@@ -3523,12 +3666,18 @@ var DelayPane = function DelayPane(_ref) {
3523
3666
  var units = normalizeOptions((_schema$delay = schema.delay) === null || _schema$delay === void 0 ? void 0 : _schema$delay.units);
3524
3667
  var unitOptions = isNotEmpty(units) ? units : DEFAULT_DELAY_UNITS;
3525
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]);
3526
3675
  useEffect(function () {
3527
3676
  if (isEmpty(value || {})) {
3528
3677
  var _unitOptions$0$value, _unitOptions$;
3529
3678
  setFieldValue("".concat(name, ".value"), {
3530
- value: 10,
3531
- 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
3532
3681
  });
3533
3682
  }
3534
3683
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -3546,6 +3695,7 @@ var DelayPane = function DelayPane(_ref) {
3546
3695
  children: /*#__PURE__*/jsxs("div", {
3547
3696
  className: "flex flex-col gap-y-1",
3548
3697
  children: [/*#__PURE__*/jsx(Typography, {
3698
+ "data-testid": "pane-title",
3549
3699
  variant: "h5",
3550
3700
  children: t("neetoRules.common.addDelay")
3551
3701
  }), /*#__PURE__*/jsx(Typography, {
@@ -3566,13 +3716,15 @@ var DelayPane = function DelayPane(_ref) {
3566
3716
  className: "flex items-start gap-x-2",
3567
3717
  children: [/*#__PURE__*/jsx(Input$1, {
3568
3718
  className: "w-44",
3569
- max: 999,
3570
- min: 1,
3719
+ dataTestid: "delay-value",
3720
+ max: MAX_DELAY_VALUE,
3721
+ min: MIN_DELAY_VALUE,
3571
3722
  name: "".concat(name, ".value.value"),
3572
3723
  placeholder: t("neetoRules.placeholders.enterDelay"),
3573
3724
  size: "large",
3574
3725
  type: "number"
3575
3726
  }), /*#__PURE__*/jsx(Select, {
3727
+ dataTestid: "delay-unit",
3576
3728
  defaultValue: value === null || value === void 0 ? void 0 : value.unit,
3577
3729
  name: "".concat(name, ".value.unit"),
3578
3730
  options: unitOptions,
@@ -3606,11 +3758,13 @@ var asText = function asText(value) {
3606
3758
  var NumberStepper = function NumberStepper(_ref) {
3607
3759
  var value = _ref.value,
3608
3760
  onChange = _ref.onChange,
3761
+ dataTestid = _ref.dataTestid,
3609
3762
  label = _ref.label,
3610
3763
  _ref$min = _ref.min,
3611
- min = _ref$min === void 0 ? 1 : _ref$min,
3764
+ min = _ref$min === void 0 ? STEPPER_MIN_VALUE : _ref$min,
3612
3765
  max = _ref.max,
3613
- required = _ref.required;
3766
+ required = _ref.required,
3767
+ error = _ref.error;
3614
3768
  var _useTranslation = useTranslation(),
3615
3769
  t = _useTranslation.t;
3616
3770
  var _useState = useState(null),
@@ -3635,15 +3789,41 @@ var NumberStepper = function NumberStepper(_ref) {
3635
3789
  max: max
3636
3790
  }));
3637
3791
  };
3638
- return /*#__PURE__*/jsxs("div", {
3792
+ return /*#__PURE__*/jsx("div", {
3639
3793
  className: "mb-stepper",
3640
- children: [/*#__PURE__*/jsx(Input, {
3794
+ children: /*#__PURE__*/jsx(Input, {
3795
+ dataTestid: dataTestid,
3796
+ error: error,
3641
3797
  label: label,
3642
3798
  min: min,
3643
3799
  required: required,
3644
3800
  autoFocus: true,
3645
3801
  type: "number",
3646
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
+ }),
3647
3827
  onBlur: function onBlur() {
3648
3828
  return setDraft(null);
3649
3829
  },
@@ -3653,28 +3833,7 @@ var NumberStepper = function NumberStepper(_ref) {
3653
3833
  onFocus: function onFocus(event) {
3654
3834
  return event.target.select();
3655
3835
  }
3656
- }), /*#__PURE__*/jsxs("div", {
3657
- className: "mb-stepper-controls",
3658
- children: [/*#__PURE__*/jsx("button", {
3659
- "aria-label": t("neetoRules.builder.aria.increase"),
3660
- className: "mb-stepper-btn",
3661
- tabIndex: -1,
3662
- type: "button",
3663
- onClick: function onClick() {
3664
- return step(1);
3665
- },
3666
- children: /*#__PURE__*/jsx(Up, {})
3667
- }), /*#__PURE__*/jsx("button", {
3668
- "aria-label": t("neetoRules.builder.aria.decrease"),
3669
- className: "mb-stepper-btn",
3670
- tabIndex: -1,
3671
- type: "button",
3672
- onClick: function onClick() {
3673
- return step(-1);
3674
- },
3675
- children: /*#__PURE__*/jsx(Down, {})
3676
- })]
3677
- })]
3836
+ })
3678
3837
  });
3679
3838
  };
3680
3839
 
@@ -3683,7 +3842,9 @@ var LeadTimePane = function LeadTimePane(_ref) {
3683
3842
  var _useTranslation = useTranslation(),
3684
3843
  t = _useTranslation.t;
3685
3844
  var _useFormikContext = useFormikContext(),
3686
- setFieldValue = _useFormikContext.setFieldValue;
3845
+ setFieldValue = _useFormikContext.setFieldValue,
3846
+ setFieldTouched = _useFormikContext.setFieldTouched,
3847
+ getFieldMeta = _useFormikContext.getFieldMeta;
3687
3848
  var _useRuleModel = useRuleModel(schema),
3688
3849
  rule = _useRuleModel.rule,
3689
3850
  fieldKeys = _useRuleModel.fieldKeys;
@@ -3691,6 +3852,11 @@ var LeadTimePane = function LeadTimePane(_ref) {
3691
3852
  amount = _minutesToParts.value,
3692
3853
  unit = _minutesToParts.unit;
3693
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;
3694
3860
  var update = function update(_ref2) {
3695
3861
  var value = _ref2.value,
3696
3862
  nextUnit = _ref2.unit;
@@ -3699,11 +3865,19 @@ var LeadTimePane = function LeadTimePane(_ref) {
3699
3865
  unit: nextUnit
3700
3866
  }));
3701
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]);
3702
3875
  return /*#__PURE__*/jsxs(Fragment, {
3703
3876
  children: [/*#__PURE__*/jsx(Panel.Header, {
3704
3877
  children: /*#__PURE__*/jsxs("div", {
3705
3878
  className: "flex flex-col gap-y-1",
3706
3879
  children: [/*#__PURE__*/jsx(Typography, {
3880
+ "data-testid": "pane-title",
3707
3881
  variant: "h5",
3708
3882
  children: t("neetoRules.builder.leadTimeTitle")
3709
3883
  }), /*#__PURE__*/jsx(Typography, {
@@ -3714,32 +3888,41 @@ var LeadTimePane = function LeadTimePane(_ref) {
3714
3888
  })
3715
3889
  }), /*#__PURE__*/jsx(Panel.Body, {
3716
3890
  children: /*#__PURE__*/jsxs("div", {
3717
- className: "flex items-end gap-x-3",
3718
- children: [/*#__PURE__*/jsx("div", {
3719
- className: "flex-1",
3720
- children: /*#__PURE__*/jsx(NumberStepper, {
3721
- label: t("neetoRules.builder.before"),
3722
- value: amount,
3723
- onChange: function onChange(value) {
3724
- return update({
3725
- value: value === "" ? 0 : value,
3726
- unit: unit
3727
- });
3728
- }
3729
- })
3730
- }), /*#__PURE__*/jsx("div", {
3731
- className: "w-32 shrink-0",
3732
- children: /*#__PURE__*/jsx(Select$1, {
3733
- isSearchable: false,
3734
- options: LEAD_TIME_UNITS,
3735
- value: unit,
3736
- onChange: function onChange(nextUnit) {
3737
- return update({
3738
- value: amount,
3739
- unit: nextUnit
3740
- });
3741
- }
3742
- })
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
+ })]
3743
3926
  })]
3744
3927
  })
3745
3928
  })]
@@ -3757,6 +3940,7 @@ var NamePane = function NamePane(_ref) {
3757
3940
  children: /*#__PURE__*/jsxs("div", {
3758
3941
  className: "flex flex-col gap-y-1",
3759
3942
  children: [/*#__PURE__*/jsx(Typography, {
3943
+ "data-testid": "pane-title",
3760
3944
  variant: "h5",
3761
3945
  children: t("neetoRules.builder.automationName")
3762
3946
  }), /*#__PURE__*/jsx(Typography, {
@@ -3898,6 +4082,11 @@ var OptionsPane = function OptionsPane(_ref) {
3898
4082
  data: target.data
3899
4083
  });else closePane();
3900
4084
  };
4085
+ var canContinue = cascade && selectedValues.some(Boolean);
4086
+ useHotkeys(SHORTCUTS["continue"].sequence, handleContinue, {
4087
+ enabled: canContinue,
4088
+ mode: "global"
4089
+ });
3901
4090
  var optionLabel = function optionLabel(option) {
3902
4091
  var Icon = option.icon;
3903
4092
  if (!Icon && !option.disabledReason) return option.label;
@@ -3929,12 +4118,13 @@ var OptionsPane = function OptionsPane(_ref) {
3929
4118
  if (isEmpty(filtered)) {
3930
4119
  return /*#__PURE__*/jsx("div", {
3931
4120
  className: "mb-pane-empty",
4121
+ "data-testid": "pane-no-matches",
3932
4122
  children: t("neetoRules.builder.noMatches")
3933
4123
  });
3934
4124
  }
3935
4125
  if (multi) {
3936
4126
  return /*#__PURE__*/jsx("div", {
3937
- className: "flex flex-col",
4127
+ className: "mb-pane-list flex flex-col",
3938
4128
  children: groups.map(function (_ref2) {
3939
4129
  var _ref3 = _slicedToArray(_ref2, 2),
3940
4130
  category = _ref3[0],
@@ -3986,6 +4176,7 @@ var OptionsPane = function OptionsPane(_ref) {
3986
4176
  children: /*#__PURE__*/jsxs("div", {
3987
4177
  className: "flex w-full flex-col gap-y-2",
3988
4178
  children: [/*#__PURE__*/jsx(Typography, {
4179
+ "data-testid": "pane-title",
3989
4180
  variant: "h5",
3990
4181
  children: title
3991
4182
  }), /*#__PURE__*/jsx(Typography, {
@@ -3996,6 +4187,7 @@ var OptionsPane = function OptionsPane(_ref) {
3996
4187
  className: "mb-pane-search",
3997
4188
  ref: searchRef,
3998
4189
  children: /*#__PURE__*/jsx(Input, {
4190
+ dataTestid: "pane-search",
3999
4191
  placeholder: t("neetoRules.builder.searchPlaceholder"),
4000
4192
  prefix: /*#__PURE__*/jsx(Search, {}),
4001
4193
  size: "large",
@@ -4010,7 +4202,8 @@ var OptionsPane = function OptionsPane(_ref) {
4010
4202
  children: [renderBody(), belowContent]
4011
4203
  }), cascade && /*#__PURE__*/jsx(Panel.Footer, {
4012
4204
  children: /*#__PURE__*/jsx(Button, {
4013
- disabled: !selectedValues.some(Boolean),
4205
+ "data-testid": "continue-button",
4206
+ disabled: !canContinue,
4014
4207
  label: t("neetoRules.buttons.continue"),
4015
4208
  size: "lg",
4016
4209
  onClick: handleContinue
@@ -4035,8 +4228,8 @@ var RecipientField = function RecipientField(_ref) {
4035
4228
  var fieldError = function fieldError(path) {
4036
4229
  return getIn(touched, path) && getIn(errors, path);
4037
4230
  };
4038
- if (field === "emails") {
4039
- var _path = "".concat(name, ".emails");
4231
+ if (field === RECIPIENT_STORAGE_FIELDS.emails) {
4232
+ var _path = "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.emails);
4040
4233
  var _current = getIn(values, _path) || [];
4041
4234
  return /*#__PURE__*/jsx(MultiEmailInput, {
4042
4235
  required: true,
@@ -4058,7 +4251,7 @@ var RecipientField = function RecipientField(_ref) {
4058
4251
  }
4059
4252
  });
4060
4253
  }
4061
- var path = "".concat(name, ".value");
4254
+ var path = "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.value);
4062
4255
  var current = getIn(values, path);
4063
4256
  var options = normalizeOptions(dropdownOptions);
4064
4257
  return /*#__PURE__*/jsx(Select$1, {
@@ -4102,7 +4295,7 @@ var RecipientPane = function RecipientPane(_ref) {
4102
4295
  });
4103
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));
4104
4297
  var isSmsToNumbers = (flatOption === null || flatOption === void 0 ? void 0 : flatOption.type) === SMS_TO_NUMBERS;
4105
- 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);
4106
4299
  usePaneValidation(recipientFieldPath);
4107
4300
  useEffect(function () {
4108
4301
  var _bodyRef$current;
@@ -4133,7 +4326,7 @@ var RecipientPane = function RecipientPane(_ref) {
4133
4326
  action: action
4134
4327
  })) !== null && _schema$actions$rende !== void 0 ? _schema$actions$rende : builtInRecipientField;
4135
4328
  var phoneField = isSmsToNumbers ? /*#__PURE__*/jsx(PhoneNumberField, {
4136
- name: "".concat(name, ".phoneNumbers"),
4329
+ name: "".concat(name, ".").concat(RECIPIENT_STORAGE_FIELDS.phoneNumbers),
4137
4330
  phoneNumbers: (_action$metadata = action.metadata) === null || _action$metadata === void 0 ? void 0 : _action$metadata.phoneNumbers
4138
4331
  }) : null;
4139
4332
  var belowField = phoneField || recipientField;
@@ -4169,6 +4362,7 @@ var RecipientPane = function RecipientPane(_ref) {
4169
4362
  children: /*#__PURE__*/jsxs("div", {
4170
4363
  className: "flex w-full flex-col gap-y-2",
4171
4364
  children: [/*#__PURE__*/jsx(Typography, {
4365
+ "data-testid": "pane-title",
4172
4366
  variant: "h5",
4173
4367
  children: meta.recipientTitle || t("neetoRules.builder.panes.recipientTitle")
4174
4368
  }), /*#__PURE__*/jsx(Typography, {
@@ -4215,6 +4409,7 @@ var ConditionValueFields = function ConditionValueFields(_ref) {
4215
4409
  };
4216
4410
  var noMatches = /*#__PURE__*/jsx("div", {
4217
4411
  className: "mb-pane-empty",
4412
+ "data-testid": "pane-no-matches",
4218
4413
  children: t("neetoRules.builder.noMatches")
4219
4414
  });
4220
4415
  if (type === CONDITION_VALUE_TYPES.multiSelect && isNotEmpty(enumOptions)) {
@@ -4226,7 +4421,7 @@ var ConditionValueFields = function ConditionValueFields(_ref) {
4226
4421
  setFieldValue(valuesPath, current.includes(value) ? current.filter(isNot(value)) : [].concat(_toConsumableArray(current), [value]));
4227
4422
  };
4228
4423
  return /*#__PURE__*/jsxs("div", {
4229
- className: "flex flex-col gap-y-1",
4424
+ className: "mb-pane-list flex flex-col",
4230
4425
  children: [filtered.map(function (option) {
4231
4426
  return /*#__PURE__*/jsx(Checkbox, {
4232
4427
  checked: current.includes(option.value),
@@ -4370,6 +4565,7 @@ var ValuePane = function ValuePane(_ref) {
4370
4565
  children: /*#__PURE__*/jsxs("div", {
4371
4566
  className: "flex w-full flex-col gap-y-2",
4372
4567
  children: [/*#__PURE__*/jsx(Typography, {
4568
+ "data-testid": "pane-title",
4373
4569
  variant: "h5",
4374
4570
  children: t("neetoRules.builder.pickValue", {
4375
4571
  field: (field === null || field === void 0 ? void 0 : field.label) || t("neetoRules.builder.valueWord")
@@ -4384,6 +4580,7 @@ var ValuePane = function ValuePane(_ref) {
4384
4580
  className: "mb-pane-search",
4385
4581
  ref: searchRef,
4386
4582
  children: /*#__PURE__*/jsx(Input, {
4583
+ dataTestid: "pane-search",
4387
4584
  placeholder: t("neetoRules.builder.searchPlaceholder"),
4388
4585
  prefix: /*#__PURE__*/jsx(Search, {}),
4389
4586
  size: "large",
@@ -4530,7 +4727,7 @@ var Form = function Form(_ref) {
4530
4727
  data: data,
4531
4728
  customData: customData,
4532
4729
  isNameGenerationDisabled: isNameRequired
4533
- }),
4730
+ }).concat(leadTimeValidationSchema(schema)),
4534
4731
  onSubmit: submitForm,
4535
4732
  children: function children() {
4536
4733
  return /*#__PURE__*/jsxs("div", {