@aglyn/plugins-workflows 1.0.0-beta.222 → 1.0.0-beta.224

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 (40) hide show
  1. package/package.json +12 -12
  2. package/src/lib/components/automation-step-fields.component.d.ts +16 -2
  3. package/src/lib/components/automation-step-fields.component.js +53 -2
  4. package/src/lib/components/automation-step-fields.component.js.map +1 -1
  5. package/src/lib/components/automation-trigger-conditions.component.d.ts +23 -2
  6. package/src/lib/components/automation-trigger-conditions.component.js +89 -10
  7. package/src/lib/components/automation-trigger-conditions.component.js.map +1 -1
  8. package/src/lib/components/host-actions-card.component.js +64 -30
  9. package/src/lib/components/host-actions-card.component.js.map +1 -1
  10. package/src/lib/components/host-workflows-card.component.js +31 -10
  11. package/src/lib/components/host-workflows-card.component.js.map +1 -1
  12. package/src/lib/components/org-automation-editor.component.d.ts +1 -1
  13. package/src/lib/components/org-automation-editor.component.js +20 -11
  14. package/src/lib/components/org-automation-editor.component.js.map +1 -1
  15. package/src/lib/engine/email-merge.d.ts +69 -0
  16. package/src/lib/engine/email-merge.js +98 -0
  17. package/src/lib/engine/email-merge.js.map +1 -0
  18. package/src/lib/engine/run-event-actions.d.ts +30 -0
  19. package/src/lib/engine/run-event-actions.js +158 -54
  20. package/src/lib/engine/run-event-actions.js.map +1 -1
  21. package/src/lib/engine/run-event-workflows.js +44 -8
  22. package/src/lib/engine/run-event-workflows.js.map +1 -1
  23. package/src/lib/engine/run-org-automations.js +3 -1
  24. package/src/lib/engine/run-org-automations.js.map +1 -1
  25. package/src/lib/engine/triggered-docs.d.ts +39 -0
  26. package/src/lib/engine/triggered-docs.js +47 -0
  27. package/src/lib/engine/triggered-docs.js.map +1 -0
  28. package/src/lib/model/host-actions.d.ts +53 -0
  29. package/src/lib/model/host-actions.js +50 -2
  30. package/src/lib/model/host-actions.js.map +1 -1
  31. package/src/lib/model/org-automations.d.ts +6 -4
  32. package/src/lib/model/org-automations.js +8 -5
  33. package/src/lib/model/org-automations.js.map +1 -1
  34. package/src/lib/server-automation-drafts.js +2 -1
  35. package/src/lib/server-automation-drafts.js.map +1 -1
  36. package/src/lib/testing/stand-in-crm-steps.js +1 -1
  37. package/src/lib/testing/stand-in-crm-steps.js.map +1 -1
  38. package/src/lib/testing/stand-in-recipes.d.ts +3 -2
  39. package/src/lib/testing/stand-in-recipes.js +17 -12
  40. package/src/lib/testing/stand-in-recipes.js.map +1 -1
@@ -19,6 +19,22 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
19
19
  import { ACTION_MAX_CONDITIONS, normalizeTriggerConditions } from "@aglyn/aglyn";
20
20
  import { Button, IconButton, MenuItem, Stack, TextField, Typography } from "@mui/material";
21
21
  import { placeholderState } from "./automation-step-fields.component.js";
22
+ /**
23
+ * ONE TRIGGER-CONDITION EDITOR FOR EVERY AUTOMATION THAT HAS A TRIGGER.
24
+ *
25
+ * The structured payload conditions (AGL-557), chainable with AND/OR
26
+ * (AGL-565): the no-code sibling of the filter expression — "only when
27
+ * `subscribe` is not empty". A site action and an org automation (AGL-3302)
28
+ * take the same clauses and are judged by the same evaluator, so they are
29
+ * edited by the same rows.
30
+ */ /**
31
+ * "Form is …" (AGL-3458): a row that picks ONE FORM by id. Stored as the
32
+ * plain clause `formId equals <id>` — the evaluator has no form operator and
33
+ * needs none — and read back into this row, so a condition on which form it
34
+ * was is chosen from the site's forms rather than typed, and survives the
35
+ * form being renamed where `formName equals …` silently stops matching.
36
+ */ export const FORM_IS_OP = 'formIs';
37
+ /** The payload key a "Form is" row compares. */ export const FORM_ID_FIELD = 'formId';
22
38
  export const EMPTY_CONDITION_ROW = {
23
39
  op: '',
24
40
  field: '',
@@ -30,11 +46,16 @@ export const EMPTY_CONDITION_ROW = {
30
46
  * row.
31
47
  */ export function conditionRowsFromTrigger(trigger) {
32
48
  const rows = normalizeTriggerConditions(trigger).map((condition)=>{
33
- var _condition_op, _condition_field, _condition_value;
34
- return {
49
+ var _condition_value, _condition_op, _condition_field, _condition_value1;
50
+ var _condition_field1;
51
+ return ((_condition_field1 = condition.field) == null ? void 0 : _condition_field1.trim()) === FORM_ID_FIELD && condition.op === 'equals' ? {
52
+ op: FORM_IS_OP,
53
+ field: FORM_ID_FIELD,
54
+ value: (_condition_value = condition.value) != null ? _condition_value : ''
55
+ } : {
35
56
  op: (_condition_op = condition.op) != null ? _condition_op : '',
36
57
  field: (_condition_field = condition.field) != null ? _condition_field : '',
37
- value: (_condition_value = condition.value) != null ? _condition_value : ''
58
+ value: (_condition_value1 = condition.value) != null ? _condition_value1 : ''
38
59
  };
39
60
  });
40
61
  return rows.length ? rows : [
@@ -47,7 +68,11 @@ export const EMPTY_CONDITION_ROW = {
47
68
  */ export function conditionsFromRows(rows, combinator) {
48
69
  if (!rows.some((row)=>row.op)) return {};
49
70
  return {
50
- conditions: rows.filter((row)=>row.op).map((row)=>_extends({
71
+ conditions: rows.filter((row)=>row.op).map((row)=>row.op === FORM_IS_OP ? {
72
+ field: FORM_ID_FIELD,
73
+ op: 'equals',
74
+ value: row.value.trim()
75
+ } : _extends({
51
76
  field: row.field.trim(),
52
77
  op: row.op
53
78
  }, row.op !== 'notEmpty' ? {
@@ -56,7 +81,8 @@ export const EMPTY_CONDITION_ROW = {
56
81
  combinator
57
82
  };
58
83
  }
59
- /** The condition rows, "Add condition", and the AND/OR choice once there are two. */ export function TriggerConditionRows({ rows, combinator, onRowsChange, onCombinatorChange }) {
84
+ /** The condition rows, "Add condition", and the AND/OR choice once there are two. */ export function TriggerConditionRows({ rows, combinator, onRowsChange, onCombinatorChange, formOptions }) {
85
+ const offerForms = Boolean(formOptions) || rows.some((row)=>row.op === FORM_IS_OP);
60
86
  return /*#__PURE__*/ _jsxs(_Fragment, {
61
87
  children: [
62
88
  rows.map((row, index)=>/*#__PURE__*/ _jsxs(Stack, {
@@ -75,9 +101,25 @@ export const EMPTY_CONDITION_ROW = {
75
101
  select: true,
76
102
  label: index === 0 ? 'Only run when' : 'Condition',
77
103
  value: row.op,
78
- onChange: (event)=>onRowsChange((previous)=>previous.map((previousRow, index2)=>index2 === index ? _extends({}, previousRow, {
79
- op: event.target.value
80
- }) : previousRow)),
104
+ onChange: (event)=>onRowsChange((previous)=>previous.map((previousRow, index2)=>{
105
+ if (index2 !== index) return previousRow;
106
+ const op = event.target.value;
107
+ // Into and out of "Form is", the field is the form's id or
108
+ // nothing — never a leftover the author did not type.
109
+ if (op === FORM_IS_OP) return {
110
+ op,
111
+ field: FORM_ID_FIELD,
112
+ value: ''
113
+ };
114
+ if (previousRow.op === FORM_IS_OP) return {
115
+ op,
116
+ field: '',
117
+ value: ''
118
+ };
119
+ return _extends({}, previousRow, {
120
+ op
121
+ });
122
+ })),
81
123
  size: "small",
82
124
  sx: {
83
125
  minWidth: 180
@@ -98,10 +140,47 @@ export const EMPTY_CONDITION_ROW = {
98
140
  /*#__PURE__*/ _jsx(MenuItem, {
99
141
  value: "contains",
100
142
  children: 'A field contains…'
101
- })
143
+ }),
144
+ offerForms ? /*#__PURE__*/ _jsx(MenuItem, {
145
+ value: FORM_IS_OP,
146
+ children: 'Form is…'
147
+ }) : null
102
148
  ]
103
149
  }),
104
- row.op ? /*#__PURE__*/ _jsx(TextField, {
150
+ row.op === FORM_IS_OP ? formOptions ? /*#__PURE__*/ _jsxs(TextField, {
151
+ select: true,
152
+ label: "Form",
153
+ value: row.value,
154
+ error: !row.value,
155
+ helperText: row.value ? undefined : 'Pick the form',
156
+ onChange: (event)=>onRowsChange((previous)=>previous.map((previousRow, index2)=>index2 === index ? _extends({}, previousRow, {
157
+ value: event.target.value
158
+ }) : previousRow)),
159
+ size: "small",
160
+ sx: {
161
+ flex: 1
162
+ },
163
+ children: [
164
+ row.value && !formOptions.some((form)=>form.id === row.value) ? /*#__PURE__*/ _jsx(MenuItem, {
165
+ value: row.value,
166
+ children: `A form that is gone (${row.value})`
167
+ }) : null,
168
+ formOptions.map((form)=>/*#__PURE__*/ _jsx(MenuItem, {
169
+ value: form.id,
170
+ children: form.name
171
+ }, form.id))
172
+ ]
173
+ }) : /*#__PURE__*/ _jsx(TextField, {
174
+ label: "Form id",
175
+ value: row.value,
176
+ onChange: (event)=>onRowsChange((previous)=>previous.map((previousRow, index2)=>index2 === index ? _extends({}, previousRow, {
177
+ value: event.target.value
178
+ }) : previousRow)),
179
+ size: "small",
180
+ sx: {
181
+ flex: 1
182
+ }
183
+ }) : row.op ? /*#__PURE__*/ _jsx(TextField, {
105
184
  label: "Field",
106
185
  placeholder: "subscribe",
107
186
  value: row.field,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/automation-trigger-conditions.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n ACTION_MAX_CONDITIONS,\n normalizeTriggerConditions,\n type TriggerCombinator,\n type TriggerConditionOp,\n} from '@aglyn/aglyn'\nimport {\n Button,\n IconButton,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { placeholderState } from './automation-step-fields.component'\nimport type { HostActionTrigger, HostActionTriggerCondition } from '../model/host-actions'\n\n/**\n * ONE TRIGGER-CONDITION EDITOR FOR EVERY AUTOMATION THAT HAS A TRIGGER.\n *\n * The structured payload conditions (AGL-557), chainable with AND/OR\n * (AGL-565): the no-code sibling of the filter expression — \"only when\n * `subscribe` is not empty\". A site action and an org automation (AGL-3302)\n * take the same clauses and are judged by the same evaluator, so they are\n * edited by the same rows.\n */\n\n/** One editable condition row; a lone row with an empty op means \"always run\". */\nexport interface ConditionRowDraft {\n op: '' | TriggerConditionOp\n field: string\n value: string\n}\n\nexport const EMPTY_CONDITION_ROW: ConditionRowDraft = {\n op: '',\n field: '',\n value: '',\n}\n\n/**\n * A stored trigger's clauses as editor rows. A legacy single-condition trigger\n * normalizes to one row, and a trigger with none opens on the lone \"always\"\n * row.\n */\nexport function conditionRowsFromTrigger(\n trigger:\n | Pick<HostActionTrigger, 'condition' | 'conditions'>\n | null\n | undefined,\n): ConditionRowDraft[] {\n const rows = normalizeTriggerConditions(trigger).map(\n (condition): ConditionRowDraft => ({\n op: condition.op ?? '',\n field: condition.field ?? '',\n value: condition.value ?? '',\n }),\n )\n return rows.length ? rows : [EMPTY_CONDITION_ROW]\n}\n\n/**\n * The rows as the stored clause list, or nothing when every row says\n * \"always\". Blank fields are kept, so the validator names what is missing.\n */\nexport function conditionsFromRows(\n rows: readonly ConditionRowDraft[],\n combinator: TriggerCombinator,\n): { conditions?: HostActionTriggerCondition[]; combinator?: TriggerCombinator } {\n if (!rows.some((row) => row.op)) return {}\n return {\n conditions: rows\n .filter((row) => row.op)\n .map((row) => ({\n field: row.field.trim(),\n op: row.op as TriggerConditionOp,\n ...(row.op !== 'notEmpty' ? { value: row.value.trim() } : {}),\n })),\n combinator,\n }\n}\n\nexport interface TriggerConditionRowsProps {\n rows: readonly ConditionRowDraft[]\n combinator: TriggerCombinator\n /** Replaces the rows, from the rows as they stand. */\n onRowsChange: (\n update: (previous: ConditionRowDraft[]) => ConditionRowDraft[],\n ) => void\n onCombinatorChange: (combinator: TriggerCombinator) => void\n}\n\n/** The condition rows, \"Add condition\", and the AND/OR choice once there are two. */\nexport function TriggerConditionRows({\n rows,\n combinator,\n onRowsChange,\n onCombinatorChange,\n}: TriggerConditionRowsProps) {\n return (\n <>\n {rows.map((row, index) => (\n <Stack\n key={index}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n {index > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {combinator === 'or' ? 'or' : 'and'}\n </Typography>\n ) : null}\n <TextField\n select\n label={index === 0 ? 'Only run when' : 'Condition'}\n value={row.op}\n onChange={(event) =>\n onRowsChange((previous) =>\n previous.map((previousRow, index2) =>\n index2 === index\n ? {\n ...previousRow,\n op: event.target.value as ConditionRowDraft['op'],\n }\n : previousRow,\n ),\n )\n }\n size=\"small\"\n sx={{ minWidth: 180 }}\n >\n {/* \"Always\" only exists while this is the sole row —\n multi-row chains clear by removing rows instead. */}\n {rows.length === 1 ? (\n <MenuItem value=\"\">{'Always (no condition)'}</MenuItem>\n ) : null}\n <MenuItem value=\"notEmpty\">{'A field is not empty'}</MenuItem>\n <MenuItem value=\"equals\">{'A field equals…'}</MenuItem>\n <MenuItem value=\"contains\">{'A field contains…'}</MenuItem>\n </TextField>\n {row.op ? (\n <TextField\n label=\"Field\"\n placeholder=\"subscribe\"\n value={row.field}\n onChange={(event) =>\n onRowsChange((previous) =>\n previous.map((previousRow, index2) =>\n index2 === index\n ? { ...previousRow, field: event.target.value }\n : previousRow,\n ),\n )\n }\n size=\"small\"\n sx={{ flex: 1 }}\n />\n ) : null}\n {row.op === 'equals' || row.op === 'contains' ? (\n <TextField\n label=\"Value\"\n placeholder=\"Yes\"\n {...placeholderState(\n row.value,\n 'Replace the placeholder with the value to match',\n )}\n value={row.value}\n onChange={(event) =>\n onRowsChange((previous) =>\n previous.map((previousRow, index2) =>\n index2 === index\n ? { ...previousRow, value: event.target.value }\n : previousRow,\n ),\n )\n }\n size=\"small\"\n sx={{ flex: 1 }}\n />\n ) : null}\n {rows.length > 1 ? (\n <IconButton\n size=\"small\"\n aria-label=\"remove condition\"\n onClick={() =>\n onRowsChange((previous) =>\n previous.length > 1\n ? previous.filter((_, index2) => index2 !== index)\n : [EMPTY_CONDITION_ROW],\n )\n }\n >\n {'×'}\n </IconButton>\n ) : null}\n </Stack>\n ))}\n {rows.every((row) => row.op) ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button\n size=\"small\"\n disabled={rows.length >= ACTION_MAX_CONDITIONS}\n onClick={() =>\n onRowsChange((previous) => [\n ...previous,\n // New rows start on the simplest operator so the field input\n // is immediately visible.\n { op: 'notEmpty', field: '', value: '' },\n ])\n }\n >\n {'Add condition'}\n </Button>\n {rows.length >= 2 ? (\n // AND/OR combinator (AGL-565); applies to every row.\n <TextField\n select\n label=\"Match\"\n value={combinator}\n onChange={(event) =>\n onCombinatorChange(event.target.value as TriggerCombinator)\n }\n size=\"small\"\n sx={{ minWidth: 220 }}\n >\n <MenuItem value=\"and\">{'All conditions match (AND)'}</MenuItem>\n <MenuItem value=\"or\">{'Any condition matches (OR)'}</MenuItem>\n </TextField>\n ) : null}\n </Stack>\n ) : null}\n </>\n )\n}\nTriggerConditionRows.displayName = 'TriggerConditionRows'\n"],"names":["ACTION_MAX_CONDITIONS","normalizeTriggerConditions","Button","IconButton","MenuItem","Stack","TextField","Typography","placeholderState","EMPTY_CONDITION_ROW","op","field","value","conditionRowsFromTrigger","trigger","rows","map","condition","length","conditionsFromRows","combinator","some","row","conditions","filter","trim","TriggerConditionRows","onRowsChange","onCombinatorChange","index","direction","spacing","sx","alignItems","variant","color","select","label","onChange","event","previous","previousRow","index2","target","size","minWidth","placeholder","flex","aria-label","onClick","_","every","disabled","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/automation-trigger-conditions.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n ACTION_MAX_CONDITIONS,\n normalizeTriggerConditions,\n type TriggerCombinator,\n type TriggerConditionOp,\n} from '@aglyn/aglyn'\nimport {\n Button,\n IconButton,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { placeholderState } from './automation-step-fields.component'\nimport type { HostActionTrigger, HostActionTriggerCondition } from '../model/host-actions'\n\n/**\n * ONE TRIGGER-CONDITION EDITOR FOR EVERY AUTOMATION THAT HAS A TRIGGER.\n *\n * The structured payload conditions (AGL-557), chainable with AND/OR\n * (AGL-565): the no-code sibling of the filter expression — \"only when\n * `subscribe` is not empty\". A site action and an org automation (AGL-3302)\n * take the same clauses and are judged by the same evaluator, so they are\n * edited by the same rows.\n */\n\n/**\n * \"Form is …\" (AGL-3458): a row that picks ONE FORM by id. Stored as the\n * plain clause `formId equals <id>` — the evaluator has no form operator and\n * needs none — and read back into this row, so a condition on which form it\n * was is chosen from the site's forms rather than typed, and survives the\n * form being renamed where `formName equals …` silently stops matching.\n */\nexport const FORM_IS_OP = 'formIs'\n\n/** The payload key a \"Form is\" row compares. */\nexport const FORM_ID_FIELD = 'formId'\n\n/** One editable condition row; a lone row with an empty op means \"always run\". */\nexport interface ConditionRowDraft {\n op: '' | TriggerConditionOp | typeof FORM_IS_OP\n field: string\n value: string\n}\n\n/** A form a \"Form is\" row can pick. */\nexport interface ConditionFormOption {\n id: string\n name: string\n}\n\nexport const EMPTY_CONDITION_ROW: ConditionRowDraft = {\n op: '',\n field: '',\n value: '',\n}\n\n/**\n * A stored trigger's clauses as editor rows. A legacy single-condition trigger\n * normalizes to one row, and a trigger with none opens on the lone \"always\"\n * row.\n */\nexport function conditionRowsFromTrigger(\n trigger:\n | Pick<HostActionTrigger, 'condition' | 'conditions'>\n | null\n | undefined,\n): ConditionRowDraft[] {\n const rows = normalizeTriggerConditions(trigger).map(\n (condition): ConditionRowDraft =>\n condition.field?.trim() === FORM_ID_FIELD && condition.op === 'equals'\n ? { op: FORM_IS_OP, field: FORM_ID_FIELD, value: condition.value ?? '' }\n : {\n op: condition.op ?? '',\n field: condition.field ?? '',\n value: condition.value ?? '',\n },\n )\n return rows.length ? rows : [EMPTY_CONDITION_ROW]\n}\n\n/**\n * The rows as the stored clause list, or nothing when every row says\n * \"always\". Blank fields are kept, so the validator names what is missing.\n */\nexport function conditionsFromRows(\n rows: readonly ConditionRowDraft[],\n combinator: TriggerCombinator,\n): { conditions?: HostActionTriggerCondition[]; combinator?: TriggerCombinator } {\n if (!rows.some((row) => row.op)) return {}\n return {\n conditions: rows\n .filter((row) => row.op)\n .map((row) =>\n row.op === FORM_IS_OP\n ? { field: FORM_ID_FIELD, op: 'equals' as const, value: row.value.trim() }\n : {\n field: row.field.trim(),\n op: row.op as TriggerConditionOp,\n ...(row.op !== 'notEmpty' ? { value: row.value.trim() } : {}),\n },\n ),\n combinator,\n }\n}\n\nexport interface TriggerConditionRowsProps {\n rows: readonly ConditionRowDraft[]\n combinator: TriggerCombinator\n /** Replaces the rows, from the rows as they stand. */\n onRowsChange: (\n update: (previous: ConditionRowDraft[]) => ConditionRowDraft[],\n ) => void\n onCombinatorChange: (combinator: TriggerCombinator) => void\n /**\n * The site's forms, for a \"Form is\" row — given only where the trigger's\n * event carries the form's id. Absent, the row is offered only to show a\n * stored one, and its form is typed by id.\n */\n formOptions?: readonly ConditionFormOption[]\n}\n\n/** The condition rows, \"Add condition\", and the AND/OR choice once there are two. */\nexport function TriggerConditionRows({\n rows,\n combinator,\n onRowsChange,\n onCombinatorChange,\n formOptions,\n}: TriggerConditionRowsProps) {\n const offerForms = Boolean(formOptions) || rows.some((row) => row.op === FORM_IS_OP)\n return (\n <>\n {rows.map((row, index) => (\n <Stack\n key={index}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n {index > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {combinator === 'or' ? 'or' : 'and'}\n </Typography>\n ) : null}\n <TextField\n select\n label={index === 0 ? 'Only run when' : 'Condition'}\n value={row.op}\n onChange={(event) =>\n onRowsChange((previous) =>\n previous.map((previousRow, index2) => {\n if (index2 !== index) return previousRow\n const op = event.target.value as ConditionRowDraft['op']\n // Into and out of \"Form is\", the field is the form's id or\n // nothing — never a leftover the author did not type.\n if (op === FORM_IS_OP) return { op, field: FORM_ID_FIELD, value: '' }\n if (previousRow.op === FORM_IS_OP) return { op, field: '', value: '' }\n return { ...previousRow, op }\n }),\n )\n }\n size=\"small\"\n sx={{ minWidth: 180 }}\n >\n {/* \"Always\" only exists while this is the sole row —\n multi-row chains clear by removing rows instead. */}\n {rows.length === 1 ? (\n <MenuItem value=\"\">{'Always (no condition)'}</MenuItem>\n ) : null}\n <MenuItem value=\"notEmpty\">{'A field is not empty'}</MenuItem>\n <MenuItem value=\"equals\">{'A field equals…'}</MenuItem>\n <MenuItem value=\"contains\">{'A field contains…'}</MenuItem>\n {offerForms ? <MenuItem value={FORM_IS_OP}>{'Form is…'}</MenuItem> : null}\n </TextField>\n {row.op === FORM_IS_OP ? (\n formOptions ? (\n <TextField\n select\n label=\"Form\"\n value={row.value}\n error={!row.value}\n helperText={row.value ? undefined : 'Pick the form'}\n onChange={(event) =>\n onRowsChange((previous) =>\n previous.map((previousRow, index2) =>\n index2 === index ? { ...previousRow, value: event.target.value } : previousRow,\n ),\n )\n }\n size=\"small\"\n sx={{ flex: 1 }}\n >\n {/* A stored id that is no longer among the site's live forms\n stays visible, so a save does not silently drop it. */}\n {row.value && !formOptions.some((form) => form.id === row.value) ? (\n <MenuItem value={row.value}>{`A form that is gone (${row.value})`}</MenuItem>\n ) : null}\n {formOptions.map((form) => (\n <MenuItem key={form.id} value={form.id}>\n {form.name}\n </MenuItem>\n ))}\n </TextField>\n ) : (\n <TextField\n label=\"Form id\"\n value={row.value}\n onChange={(event) =>\n onRowsChange((previous) =>\n previous.map((previousRow, index2) =>\n index2 === index ? { ...previousRow, value: event.target.value } : previousRow,\n ),\n )\n }\n size=\"small\"\n sx={{ flex: 1 }}\n />\n )\n ) : row.op ? (\n <TextField\n label=\"Field\"\n placeholder=\"subscribe\"\n value={row.field}\n onChange={(event) =>\n onRowsChange((previous) =>\n previous.map((previousRow, index2) =>\n index2 === index\n ? { ...previousRow, field: event.target.value }\n : previousRow,\n ),\n )\n }\n size=\"small\"\n sx={{ flex: 1 }}\n />\n ) : null}\n {row.op === 'equals' || row.op === 'contains' ? (\n <TextField\n label=\"Value\"\n placeholder=\"Yes\"\n {...placeholderState(\n row.value,\n 'Replace the placeholder with the value to match',\n )}\n value={row.value}\n onChange={(event) =>\n onRowsChange((previous) =>\n previous.map((previousRow, index2) =>\n index2 === index\n ? { ...previousRow, value: event.target.value }\n : previousRow,\n ),\n )\n }\n size=\"small\"\n sx={{ flex: 1 }}\n />\n ) : null}\n {rows.length > 1 ? (\n <IconButton\n size=\"small\"\n aria-label=\"remove condition\"\n onClick={() =>\n onRowsChange((previous) =>\n previous.length > 1\n ? previous.filter((_, index2) => index2 !== index)\n : [EMPTY_CONDITION_ROW],\n )\n }\n >\n {'×'}\n </IconButton>\n ) : null}\n </Stack>\n ))}\n {rows.every((row) => row.op) ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button\n size=\"small\"\n disabled={rows.length >= ACTION_MAX_CONDITIONS}\n onClick={() =>\n onRowsChange((previous) => [\n ...previous,\n // New rows start on the simplest operator so the field input\n // is immediately visible.\n { op: 'notEmpty', field: '', value: '' },\n ])\n }\n >\n {'Add condition'}\n </Button>\n {rows.length >= 2 ? (\n // AND/OR combinator (AGL-565); applies to every row.\n <TextField\n select\n label=\"Match\"\n value={combinator}\n onChange={(event) =>\n onCombinatorChange(event.target.value as TriggerCombinator)\n }\n size=\"small\"\n sx={{ minWidth: 220 }}\n >\n <MenuItem value=\"and\">{'All conditions match (AND)'}</MenuItem>\n <MenuItem value=\"or\">{'Any condition matches (OR)'}</MenuItem>\n </TextField>\n ) : null}\n </Stack>\n ) : null}\n </>\n )\n}\nTriggerConditionRows.displayName = 'TriggerConditionRows'\n"],"names":["ACTION_MAX_CONDITIONS","normalizeTriggerConditions","Button","IconButton","MenuItem","Stack","TextField","Typography","placeholderState","FORM_IS_OP","FORM_ID_FIELD","EMPTY_CONDITION_ROW","op","field","value","conditionRowsFromTrigger","trigger","rows","map","condition","trim","length","conditionsFromRows","combinator","some","row","conditions","filter","TriggerConditionRows","onRowsChange","onCombinatorChange","formOptions","offerForms","Boolean","index","direction","spacing","sx","alignItems","variant","color","select","label","onChange","event","previous","previousRow","index2","target","size","minWidth","error","helperText","undefined","flex","form","id","name","placeholder","aria-label","onClick","_","every","disabled","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,SACEA,qBAAqB,EACrBC,0BAA0B,QAGrB,eAAc;AACrB,SACEC,MAAM,EACNC,UAAU,EACVC,QAAQ,EACRC,KAAK,EACLC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,gBAAgB,QAAQ,wCAAoC;AAGrE;;;;;;;;CAQC,GAED;;;;;;CAMC,GACD,OAAO,MAAMC,aAAa,SAAQ;AAElC,8CAA8C,GAC9C,OAAO,MAAMC,gBAAgB,SAAQ;AAerC,OAAO,MAAMC,sBAAyC;IACpDC,IAAI;IACJC,OAAO;IACPC,OAAO;AACT,EAAC;AAED;;;;CAIC,GACD,OAAO,SAASC,yBACdC,OAGa;IAEb,MAAMC,OAAOhB,2BAA2Be,SAASE,GAAG,CAClD,CAACC;YAEoDA,kBAEzCA,eACGA,kBACAA;YALbA;eAAAA,EAAAA,oBAAAA,UAAUN,KAAK,qBAAfM,kBAAiBC,IAAI,QAAOV,iBAAiBS,UAAUP,EAAE,KAAK,WAC1D;YAAEA,IAAIH;YAAYI,OAAOH;YAAeI,KAAK,GAAEK,mBAAAA,UAAUL,KAAK,YAAfK,mBAAmB;QAAG,IACrE;YACEP,EAAE,GAAEO,gBAAAA,UAAUP,EAAE,YAAZO,gBAAgB;YACpBN,KAAK,GAAEM,mBAAAA,UAAUN,KAAK,YAAfM,mBAAmB;YAC1BL,KAAK,GAAEK,oBAAAA,UAAUL,KAAK,YAAfK,oBAAmB;QAC5B;;IAER,OAAOF,KAAKI,MAAM,GAAGJ,OAAO;QAACN;KAAoB;AACnD;AAEA;;;CAGC,GACD,OAAO,SAASW,mBACdL,IAAkC,EAClCM,UAA6B;IAE7B,IAAI,CAACN,KAAKO,IAAI,CAAC,CAACC,MAAQA,IAAIb,EAAE,GAAG,OAAO,CAAC;IACzC,OAAO;QACLc,YAAYT,KACTU,MAAM,CAAC,CAACF,MAAQA,IAAIb,EAAE,EACtBM,GAAG,CAAC,CAACO,MACJA,IAAIb,EAAE,KAAKH,aACP;gBAAEI,OAAOH;gBAAeE,IAAI;gBAAmBE,OAAOW,IAAIX,KAAK,CAACM,IAAI;YAAG,IACvE;gBACEP,OAAOY,IAAIZ,KAAK,CAACO,IAAI;gBACrBR,IAAIa,IAAIb,EAAE;eACNa,IAAIb,EAAE,KAAK,aAAa;gBAAEE,OAAOW,IAAIX,KAAK,CAACM,IAAI;YAAG,IAAI,CAAC;QAGrEG;IACF;AACF;AAkBA,mFAAmF,GACnF,OAAO,SAASK,qBAAqB,EACnCX,IAAI,EACJM,UAAU,EACVM,YAAY,EACZC,kBAAkB,EAClBC,WAAW,EACe;IAC1B,MAAMC,aAAaC,QAAQF,gBAAgBd,KAAKO,IAAI,CAAC,CAACC,MAAQA,IAAIb,EAAE,KAAKH;IACzE,qBACE;;YACGQ,KAAKC,GAAG,CAAC,CAACO,KAAKS,sBACd,MAAC7B;oBAEC8B,WAAU;oBACVC,SAAS;oBACTC,IAAI;wBAAEC,YAAY;oBAAS;;wBAE1BJ,QAAQ,kBACP,KAAC3B;4BAAWgC,SAAQ;4BAAUC,OAAM;sCACjCjB,eAAe,OAAO,OAAO;6BAE9B;sCACJ,MAACjB;4BACCmC,MAAM;4BACNC,OAAOR,UAAU,IAAI,kBAAkB;4BACvCpB,OAAOW,IAAIb,EAAE;4BACb+B,UAAU,CAACC,QACTf,aAAa,CAACgB,WACZA,SAAS3B,GAAG,CAAC,CAAC4B,aAAaC;wCACzB,IAAIA,WAAWb,OAAO,OAAOY;wCAC7B,MAAMlC,KAAKgC,MAAMI,MAAM,CAAClC,KAAK;wCAC7B,2DAA2D;wCAC3D,sDAAsD;wCACtD,IAAIF,OAAOH,YAAY,OAAO;4CAAEG;4CAAIC,OAAOH;4CAAeI,OAAO;wCAAG;wCACpE,IAAIgC,YAAYlC,EAAE,KAAKH,YAAY,OAAO;4CAAEG;4CAAIC,OAAO;4CAAIC,OAAO;wCAAG;wCACrE,OAAO,aAAKgC;4CAAalC;;oCAC3B;4BAGJqC,MAAK;4BACLZ,IAAI;gCAAEa,UAAU;4BAAI;;gCAInBjC,KAAKI,MAAM,KAAK,kBACf,KAACjB;oCAASU,OAAM;8CAAI;qCAClB;8CACJ,KAACV;oCAASU,OAAM;8CAAY;;8CAC5B,KAACV;oCAASU,OAAM;8CAAU;;8CAC1B,KAACV;oCAASU,OAAM;8CAAY;;gCAC3BkB,2BAAa,KAAC5B;oCAASU,OAAOL;8CAAa;qCAAyB;;;wBAEtEgB,IAAIb,EAAE,KAAKH,aACVsB,4BACE,MAACzB;4BACCmC,MAAM;4BACNC,OAAM;4BACN5B,OAAOW,IAAIX,KAAK;4BAChBqC,OAAO,CAAC1B,IAAIX,KAAK;4BACjBsC,YAAY3B,IAAIX,KAAK,GAAGuC,YAAY;4BACpCV,UAAU,CAACC,QACTf,aAAa,CAACgB,WACZA,SAAS3B,GAAG,CAAC,CAAC4B,aAAaC,SACzBA,WAAWb,QAAQ,aAAKY;4CAAahC,OAAO8B,MAAMI,MAAM,CAAClC,KAAK;6CAAKgC;4BAIzEG,MAAK;4BACLZ,IAAI;gCAAEiB,MAAM;4BAAE;;gCAIb7B,IAAIX,KAAK,IAAI,CAACiB,YAAYP,IAAI,CAAC,CAAC+B,OAASA,KAAKC,EAAE,KAAK/B,IAAIX,KAAK,kBAC7D,KAACV;oCAASU,OAAOW,IAAIX,KAAK;8CAAG,CAAC,qBAAqB,EAAEW,IAAIX,KAAK,CAAC,CAAC,CAAC;qCAC/D;gCACHiB,YAAYb,GAAG,CAAC,CAACqC,qBAChB,KAACnD;wCAAuBU,OAAOyC,KAAKC,EAAE;kDACnCD,KAAKE,IAAI;uCADGF,KAAKC,EAAE;;2CAM1B,KAAClD;4BACCoC,OAAM;4BACN5B,OAAOW,IAAIX,KAAK;4BAChB6B,UAAU,CAACC,QACTf,aAAa,CAACgB,WACZA,SAAS3B,GAAG,CAAC,CAAC4B,aAAaC,SACzBA,WAAWb,QAAQ,aAAKY;4CAAahC,OAAO8B,MAAMI,MAAM,CAAClC,KAAK;6CAAKgC;4BAIzEG,MAAK;4BACLZ,IAAI;gCAAEiB,MAAM;4BAAE;6BAGhB7B,IAAIb,EAAE,iBACR,KAACN;4BACCoC,OAAM;4BACNgB,aAAY;4BACZ5C,OAAOW,IAAIZ,KAAK;4BAChB8B,UAAU,CAACC,QACTf,aAAa,CAACgB,WACZA,SAAS3B,GAAG,CAAC,CAAC4B,aAAaC,SACzBA,WAAWb,QACP,aAAKY;4CAAajC,OAAO+B,MAAMI,MAAM,CAAClC,KAAK;6CAC3CgC;4BAIVG,MAAK;4BACLZ,IAAI;gCAAEiB,MAAM;4BAAE;6BAEd;wBACH7B,IAAIb,EAAE,KAAK,YAAYa,IAAIb,EAAE,KAAK,2BACjC,KAACN;4BACCoC,OAAM;4BACNgB,aAAY;2BACRlD,iBACFiB,IAAIX,KAAK,EACT;4BAEFA,OAAOW,IAAIX,KAAK;4BAChB6B,UAAU,CAACC,QACTf,aAAa,CAACgB,WACZA,SAAS3B,GAAG,CAAC,CAAC4B,aAAaC,SACzBA,WAAWb,QACP,aAAKY;4CAAahC,OAAO8B,MAAMI,MAAM,CAAClC,KAAK;6CAC3CgC;4BAIVG,MAAK;4BACLZ,IAAI;gCAAEiB,MAAM;4BAAE;8BAEd;wBACHrC,KAAKI,MAAM,GAAG,kBACb,KAAClB;4BACC8C,MAAK;4BACLU,cAAW;4BACXC,SAAS,IACP/B,aAAa,CAACgB,WACZA,SAASxB,MAAM,GAAG,IACdwB,SAASlB,MAAM,CAAC,CAACkC,GAAGd,SAAWA,WAAWb,SAC1C;wCAACvB;qCAAoB;sCAI5B;6BAED;;mBA1ICuB;YA6IRjB,KAAK6C,KAAK,CAAC,CAACrC,MAAQA,IAAIb,EAAE,kBACzB,MAACP;gBAAM8B,WAAU;gBAAMC,SAAS;gBAAGC,IAAI;oBAAEC,YAAY;gBAAS;;kCAC5D,KAACpC;wBACC+C,MAAK;wBACLc,UAAU9C,KAAKI,MAAM,IAAIrB;wBACzB4D,SAAS,IACP/B,aAAa,CAACgB,WAAa;uCACtBA;oCACH,6DAA6D;oCAC7D,0BAA0B;oCAC1B;wCAAEjC,IAAI;wCAAYC,OAAO;wCAAIC,OAAO;oCAAG;iCACxC;kCAGF;;oBAEFG,KAAKI,MAAM,IAAI,IACd,qDAAqD;kCACrD,MAACf;wBACCmC,MAAM;wBACNC,OAAM;wBACN5B,OAAOS;wBACPoB,UAAU,CAACC,QACTd,mBAAmBc,MAAMI,MAAM,CAAClC,KAAK;wBAEvCmC,MAAK;wBACLZ,IAAI;4BAAEa,UAAU;wBAAI;;0CAEpB,KAAC9C;gCAASU,OAAM;0CAAO;;0CACvB,KAACV;gCAASU,OAAM;0CAAM;;;yBAEtB;;iBAEJ;;;AAGV;AACAc,qBAAqBoC,WAAW,GAAG"}
@@ -16,7 +16,7 @@
16
16
  */ 'use client';
17
17
  import { _ as _extends } from "@swc/helpers/_/_extends";
18
18
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
- import { ACTION_MAX_STEPS, checkEntitlement, createResourceUid, ELEMENT_SCOPED_SITE_EVENTS, HOST_EVENT_TYPES, hostEventLabel, hostEventPayloadHint, isSiteEventType, pluginDocsHelp, SITE_EVENT_TYPES } from "@aglyn/aglyn";
19
+ import { ACTION_MAX_STEPS, checkEntitlement, createResourceUid, ELEMENT_SCOPED_SITE_EVENTS, HOST_EVENT_TYPES, hostEventLabel, HOST_EVENT_PAYLOAD_KEYS, hostEventPayloadHint, isSiteEventType, pluginDocsHelp, SITE_EVENT_TYPES, triggerFilterProblem } from "@aglyn/aglyn";
20
20
  import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot-context";
21
21
  import { interactionRecipe, interactionRecipes, interactionRecipeStamp } from "@aglyn/aglyn/plugin-manager/interaction-recipes";
22
22
  import { pluginRecordListQuery, pluginRecordsFromRows } from "@aglyn/aglyn/plugin-manager/plugin-record-lists";
@@ -37,7 +37,7 @@ import { conditionRowsFromTrigger, conditionsFromRows, EMPTY_CONDITION_ROW, Trig
37
37
  import HostRunHistoryCard from "./host-run-history-card.component.js";
38
38
  import { EDITOR_OPTION_CEILING, useAutomationStepPickers } from "./use-automation-step-pickers.js";
39
39
  import { siteInteractionDocument } from "@aglyn/aglyn/app-utils/site-interactions";
40
- import { HOST_ACTION_STEP_LABELS, validateHostAction } from "../model/host-actions.js";
40
+ import { HOST_ACTION_STEP_LABELS, stepRunsAfterWait, validateHostAction } from "../model/host-actions.js";
41
41
  import { describeInteractionPlaceholder, interactionPlaceholders } from "@aglyn/aglyn/app-utils/draft-placeholders";
42
42
  const CUSTOM_EVENT_VALUE = '__custom__';
43
43
  /**
@@ -100,8 +100,8 @@ const CUSTOM_EVENT_VALUE = '__custom__';
100
100
  * event, dataset append). Runs server-side via runEventActions; paid
101
101
  * feature (`actions` flag, Pro+, `actionRunsPerMonth` metered).
102
102
  */ export function HostActionsCard(props) {
103
- var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _hostEventPayloadHint, _ref7, _ref8, _ref9, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _runsFor_name;
104
- var _pickFor_picks, _interactionRecipe, _pickFor_picks1, _pickFor_picks2, _pickFor_picks3, _pickFor_picks4, _pickFor_picks5;
103
+ var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _triggerFilterProblem, _ref8, _ref9, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _runsFor_name;
104
+ var _pickFor_picks, _HOST_EVENT_PAYLOAD_KEYS_draft_trigger_event, _interactionRecipe, _pickFor_picks1, _pickFor_picks2, _pickFor_picks3, _pickFor_picks4, _pickFor_picks5;
105
105
  const { hostId, org } = props;
106
106
  // Org-shared data root (AGL-237). Null until the org lookup settles
107
107
  // (AGL-1061), and for a host with no owning org — the pre-migration host
@@ -310,8 +310,32 @@ const CUSTOM_EVENT_VALUE = '__custom__';
310
310
  });
311
311
  const { rows: pickRows, truncated: pickTruncated } = ceilingedWindow(pickRead, EDITOR_OPTION_CEILING);
312
312
  // What the owner leaves out (an archived form collects nothing, so a recipe
313
- // keyed on it would never fire) is not offered.
313
+ // keyed on it would never fire) is not offered. The facts the owner shares
314
+ // ride along to the recipe, which may shape its draft by them (AGL-3458).
314
315
  const pickOptions = pluginRecordsFromRows(pickedKind != null ? pickedKind : '', pickRows).map((record)=>({
316
+ id: record.id,
317
+ name: record.name,
318
+ facts: record.facts
319
+ })).sort((a, b)=>a.name.localeCompare(b.name));
320
+ /*
321
+ * The site's forms for the trigger's "Form is" condition (AGL-3458), for an
322
+ * event that carries the form's id — through the same list source the
323
+ * recipe picker reads, latched like it: read once the editor is open on
324
+ * such an event, and never before.
325
+ */ const eventCarriesFormId = Boolean(draft && ((_HOST_EVENT_PAYLOAD_KEYS_draft_trigger_event = HOST_EVENT_PAYLOAD_KEYS[draft.trigger.event]) == null ? void 0 : _HOST_EVENT_PAYLOAD_KEYS_draft_trigger_event.includes('formId')));
326
+ const [formConditionOpened, setFormConditionOpened] = useState(false);
327
+ if (eventCarriesFormId && !formConditionOpened) setFormConditionOpened(true);
328
+ const { data: formConditionRead } = useFirestoreCollection(()=>formConditionOpened ? pluginRecordListQuery('form', firestore, {
329
+ hostId,
330
+ limit: EDITOR_OPTION_CEILING + 1
331
+ }) : null, [
332
+ firestore,
333
+ hostId,
334
+ formConditionOpened
335
+ ], {
336
+ idField: '$id'
337
+ });
338
+ const formOptions = pluginRecordsFromRows('form', ceilingedWindow(formConditionRead, EDITOR_OPTION_CEILING).rows).map((record)=>({
315
339
  id: record.id,
316
340
  name: record.name
317
341
  })).sort((a, b)=>a.name.localeCompare(b.name));
@@ -807,12 +831,16 @@ const CUSTOM_EVENT_VALUE = '__custom__';
807
831
  }
808
832
  }) : /*#__PURE__*/ _jsx(TextField, {
809
833
  label: "Filter (optional)",
810
- placeholder: 'path == "/pricing"',
811
- // What the expression — and the conditions below — can name
812
- // for this event; nothing for an event whose payload is not
834
+ placeholder: "subscribe",
835
+ // A comparison belongs in the conditions below: the filter's
836
+ // evaluator is arithmetic, and `path == "/pricing"` throws on
837
+ // every event, so it is refused here (AGL-3458). Otherwise,
838
+ // what the expression — and the conditions — can name for
839
+ // this event; nothing for an event whose payload is not
813
840
  // written down, rather than a guess.
814
- helperText: (_hostEventPayloadHint = hostEventPayloadHint(draft == null ? void 0 : draft.trigger.event)) != null ? _hostEventPayloadHint : undefined,
815
- value: (_ref7 = draft == null ? void 0 : draft.trigger.filter) != null ? _ref7 : '',
841
+ error: Boolean(triggerFilterProblem(draft == null ? void 0 : draft.trigger.filter)),
842
+ helperText: (_ref7 = (_triggerFilterProblem = triggerFilterProblem(draft == null ? void 0 : draft.trigger.filter)) != null ? _triggerFilterProblem : hostEventPayloadHint(draft == null ? void 0 : draft.trigger.event)) != null ? _ref7 : undefined,
843
+ value: (_ref8 = draft == null ? void 0 : draft.trigger.filter) != null ? _ref8 : '',
816
844
  onChange: (event)=>patch((previous)=>_extends({}, previous, {
817
845
  trigger: _extends({}, previous.trigger, {
818
846
  filter: event.target.value
@@ -825,25 +853,27 @@ const CUSTOM_EVENT_VALUE = '__custom__';
825
853
  })
826
854
  ]
827
855
  }),
828
- /*#__PURE__*/ _jsx(TriggerConditionRows, {
829
- rows: (_ref8 = draft == null ? void 0 : draft.conditionRows) != null ? _ref8 : [],
830
- combinator: (_ref9 = draft == null ? void 0 : draft.conditionCombinator) != null ? _ref9 : 'and',
856
+ /*#__PURE__*/ _jsx(TriggerConditionRows, _extends({
857
+ rows: (_ref9 = draft == null ? void 0 : draft.conditionRows) != null ? _ref9 : [],
858
+ combinator: (_ref10 = draft == null ? void 0 : draft.conditionCombinator) != null ? _ref10 : 'and',
831
859
  onRowsChange: (update)=>patch((previous)=>_extends({}, previous, {
832
860
  conditionRows: update(previous.conditionRows)
833
861
  })),
834
862
  onCombinatorChange: (combinator)=>patch((previous)=>_extends({}, previous, {
835
863
  conditionCombinator: combinator
836
864
  }))
837
- }),
838
- isSiteEventType((_ref10 = draft == null ? void 0 : draft.trigger.event) != null ? _ref10 : '') ? // Site-event config (AGL-256): what/where the trigger watches.
865
+ }, eventCarriesFormId ? {
866
+ formOptions
867
+ } : {})),
868
+ isSiteEventType((_ref11 = draft == null ? void 0 : draft.trigger.event) != null ? _ref11 : '') ? // Site-event config (AGL-256): what/where the trigger watches.
839
869
  /*#__PURE__*/ _jsxs(Stack, {
840
870
  direction: "row",
841
871
  spacing: 1,
842
872
  children: [
843
- ELEMENT_SCOPED_SITE_EVENTS.includes((_ref11 = draft == null ? void 0 : draft.trigger.event) != null ? _ref11 : '') ? /*#__PURE__*/ _jsx(TextField, {
873
+ ELEMENT_SCOPED_SITE_EVENTS.includes((_ref12 = draft == null ? void 0 : draft.trigger.event) != null ? _ref12 : '') ? /*#__PURE__*/ _jsx(TextField, {
844
874
  label: "CSS selector",
845
875
  placeholder: "#pricing-table",
846
- value: (_ref12 = draft == null ? void 0 : draft.trigger.selector) != null ? _ref12 : '',
876
+ value: (_ref13 = draft == null ? void 0 : draft.trigger.selector) != null ? _ref13 : '',
847
877
  onChange: (event)=>patch((previous)=>_extends({}, previous, {
848
878
  trigger: _extends({}, previous.trigger, {
849
879
  selector: event.target.value
@@ -857,10 +887,10 @@ const CUSTOM_EVENT_VALUE = '__custom__';
857
887
  [
858
888
  'scrollDepth',
859
889
  'timeOnPage'
860
- ].includes((_ref13 = draft == null ? void 0 : draft.trigger.event) != null ? _ref13 : '') ? /*#__PURE__*/ _jsx(TextField, {
890
+ ].includes((_ref14 = draft == null ? void 0 : draft.trigger.event) != null ? _ref14 : '') ? /*#__PURE__*/ _jsx(TextField, {
861
891
  type: "number",
862
892
  label: (draft == null ? void 0 : draft.trigger.event) === 'scrollDepth' ? 'Scroll %' : 'Seconds',
863
- value: (_ref14 = draft == null ? void 0 : draft.trigger.threshold) != null ? _ref14 : '',
893
+ value: (_ref15 = draft == null ? void 0 : draft.trigger.threshold) != null ? _ref15 : '',
864
894
  onChange: (event)=>patch((previous)=>_extends({}, previous, {
865
895
  trigger: _extends({}, previous.trigger, {
866
896
  threshold: Number(event.target.value)
@@ -874,7 +904,7 @@ const CUSTOM_EVENT_VALUE = '__custom__';
874
904
  /*#__PURE__*/ _jsx(TextField, {
875
905
  label: "Only on pages (optional)",
876
906
  placeholder: "/pricing or /blog/*",
877
- value: (_ref15 = draft == null ? void 0 : draft.trigger.pathPattern) != null ? _ref15 : '',
907
+ value: (_ref16 = draft == null ? void 0 : draft.trigger.pathPattern) != null ? _ref16 : '',
878
908
  onChange: (event)=>patch((previous)=>_extends({}, previous, {
879
909
  trigger: _extends({}, previous.trigger, {
880
910
  pathPattern: event.target.value
@@ -936,7 +966,7 @@ const CUSTOM_EVENT_VALUE = '__custom__';
936
966
  sx: {
937
967
  width: 150
938
968
  },
939
- value: (_ref16 = draft == null ? void 0 : draft.trigger.cooldownMinutes) != null ? _ref16 : 60,
969
+ value: (_ref17 = draft == null ? void 0 : draft.trigger.cooldownMinutes) != null ? _ref17 : 60,
940
970
  onChange: (event)=>patch((previous)=>_extends({}, previous, {
941
971
  trigger: _extends({}, previous.trigger, {
942
972
  cooldownMinutes: Number(event.target.value)
@@ -950,7 +980,7 @@ const CUSTOM_EVENT_VALUE = '__custom__';
950
980
  color: "text.secondary",
951
981
  children: 'Steps (run in order)'
952
982
  }),
953
- ((_ref17 = draft == null ? void 0 : draft.steps) != null ? _ref17 : []).map((step, index)=>/*#__PURE__*/ _jsx(AutomationStepFields, {
983
+ ((_ref18 = draft == null ? void 0 : draft.steps) != null ? _ref18 : []).map((step, index)=>/*#__PURE__*/ _jsx(AutomationStepFields, {
954
984
  step: step,
955
985
  index: index,
956
986
  kind: step.type,
@@ -959,14 +989,18 @@ const CUSTOM_EVENT_VALUE = '__custom__';
959
989
  pickers: stepPickers,
960
990
  onSteps: (update)=>patch((previous)=>_extends({}, previous, {
961
991
  steps: update(previous.steps)
962
- }))
992
+ })),
993
+ replyContext: {
994
+ event: draft == null ? void 0 : draft.trigger.event,
995
+ afterWait: stepRunsAfterWait(draft == null ? void 0 : draft.steps, index)
996
+ }
963
997
  }, index)),
964
998
  /*#__PURE__*/ _jsx(Button, {
965
999
  size: "small",
966
1000
  sx: {
967
1001
  alignSelf: 'flex-start'
968
1002
  },
969
- disabled: ((_ref18 = draft == null ? void 0 : draft.steps.length) != null ? _ref18 : 0) >= ACTION_MAX_STEPS,
1003
+ disabled: ((_ref19 = draft == null ? void 0 : draft.steps.length) != null ? _ref19 : 0) >= ACTION_MAX_STEPS,
970
1004
  onClick: ()=>patch((previous)=>_extends({}, previous, {
971
1005
  steps: [
972
1006
  ...previous.steps,
@@ -1001,7 +1035,7 @@ const CUSTOM_EVENT_VALUE = '__custom__';
1001
1035
  fullWidth: true,
1002
1036
  children: [
1003
1037
  /*#__PURE__*/ _jsx(DialogTitle, {
1004
- children: (_ref19 = pickFor == null ? void 0 : pickFor.title) != null ? _ref19 : ''
1038
+ children: (_ref20 = pickFor == null ? void 0 : pickFor.title) != null ? _ref20 : ''
1005
1039
  }),
1006
1040
  /*#__PURE__*/ _jsxs(DialogContent, {
1007
1041
  sx: {
@@ -1014,15 +1048,15 @@ const CUSTOM_EVENT_VALUE = '__custom__';
1014
1048
  /*#__PURE__*/ _jsx(Typography, {
1015
1049
  variant: "body2",
1016
1050
  color: "text.secondary",
1017
- children: `${(_ref20 = pickFor == null ? void 0 : (_pickFor_picks1 = pickFor.picks) == null ? void 0 : _pickFor_picks1.prompt) != null ? _ref20 : ''} The action opens ready to edit; ` + 'nothing is saved until you save it.'
1051
+ children: `${(_ref21 = pickFor == null ? void 0 : (_pickFor_picks1 = pickFor.picks) == null ? void 0 : _pickFor_picks1.prompt) != null ? _ref21 : ''} The action opens ready to edit; ` + 'nothing is saved until you save it.'
1018
1052
  }),
1019
1053
  pickTruncated ? /*#__PURE__*/ _jsx(Alert, {
1020
1054
  severity: "info",
1021
- children: `Offering the first ${EDITOR_OPTION_CEILING} ${(_ref21 = pickFor == null ? void 0 : (_pickFor_picks2 = pickFor.picks) == null ? void 0 : _pickFor_picks2.plural) != null ? _ref21 : ''}, ` + `ordered by id. This site has more, so the ` + `${((_ref22 = pickFor == null ? void 0 : (_pickFor_picks3 = pickFor.picks) == null ? void 0 : _pickFor_picks3.label) != null ? _ref22 : '').toLowerCase()} you want may not be listed.`
1055
+ children: `Offering the first ${EDITOR_OPTION_CEILING} ${(_ref22 = pickFor == null ? void 0 : (_pickFor_picks2 = pickFor.picks) == null ? void 0 : _pickFor_picks2.plural) != null ? _ref22 : ''}, ` + `ordered by id. This site has more, so the ` + `${((_ref23 = pickFor == null ? void 0 : (_pickFor_picks3 = pickFor.picks) == null ? void 0 : _pickFor_picks3.label) != null ? _ref23 : '').toLowerCase()} you want may not be listed.`
1022
1056
  }) : null,
1023
1057
  pickOptions.length ? /*#__PURE__*/ _jsx(TextField, {
1024
1058
  select: true,
1025
- label: (_ref23 = pickFor == null ? void 0 : (_pickFor_picks4 = pickFor.picks) == null ? void 0 : _pickFor_picks4.label) != null ? _ref23 : '',
1059
+ label: (_ref24 = pickFor == null ? void 0 : (_pickFor_picks4 = pickFor.picks) == null ? void 0 : _pickFor_picks4.label) != null ? _ref24 : '',
1026
1060
  value: pickedId,
1027
1061
  onChange: (event)=>setPickedId(event.target.value),
1028
1062
  size: "small",
@@ -1036,7 +1070,7 @@ const CUSTOM_EVENT_VALUE = '__custom__';
1036
1070
  }) : /*#__PURE__*/ _jsx(Typography, {
1037
1071
  variant: "body2",
1038
1072
  color: "text.secondary",
1039
- children: (_ref24 = pickFor == null ? void 0 : (_pickFor_picks5 = pickFor.picks) == null ? void 0 : _pickFor_picks5.none) != null ? _ref24 : ''
1073
+ children: (_ref25 = pickFor == null ? void 0 : (_pickFor_picks5 = pickFor.picks) == null ? void 0 : _pickFor_picks5.none) != null ? _ref25 : ''
1040
1074
  })
1041
1075
  ]
1042
1076
  }),
@@ -1065,7 +1099,7 @@ const CUSTOM_EVENT_VALUE = '__custom__';
1065
1099
  fullWidth: true,
1066
1100
  children: [
1067
1101
  /*#__PURE__*/ _jsx(DialogTitle, {
1068
- children: `Runs — ${(_ref25 = runsFor == null ? void 0 : runsFor.name) != null ? _ref25 : ''}`
1102
+ children: `Runs — ${(_ref26 = runsFor == null ? void 0 : runsFor.name) != null ? _ref26 : ''}`
1069
1103
  }),
1070
1104
  /*#__PURE__*/ _jsx(DialogContent, {
1071
1105
  children: runsFor ? /*#__PURE__*/ _jsx(HostRunHistoryCard, {