@aglyn/plugins-workflows 1.0.0-beta.222 → 1.0.0-beta.223
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.
- package/package.json +12 -12
- package/src/lib/components/automation-step-fields.component.d.ts +16 -2
- package/src/lib/components/automation-step-fields.component.js +53 -2
- package/src/lib/components/automation-step-fields.component.js.map +1 -1
- package/src/lib/components/automation-trigger-conditions.component.d.ts +23 -2
- package/src/lib/components/automation-trigger-conditions.component.js +89 -10
- package/src/lib/components/automation-trigger-conditions.component.js.map +1 -1
- package/src/lib/components/host-actions-card.component.js +64 -30
- package/src/lib/components/host-actions-card.component.js.map +1 -1
- package/src/lib/components/host-workflows-card.component.js +31 -10
- package/src/lib/components/host-workflows-card.component.js.map +1 -1
- package/src/lib/components/org-automation-editor.component.d.ts +1 -1
- package/src/lib/components/org-automation-editor.component.js +20 -11
- package/src/lib/components/org-automation-editor.component.js.map +1 -1
- package/src/lib/engine/email-merge.d.ts +69 -0
- package/src/lib/engine/email-merge.js +98 -0
- package/src/lib/engine/email-merge.js.map +1 -0
- package/src/lib/engine/run-event-actions.d.ts +30 -0
- package/src/lib/engine/run-event-actions.js +158 -54
- package/src/lib/engine/run-event-actions.js.map +1 -1
- package/src/lib/engine/run-event-workflows.js +44 -8
- package/src/lib/engine/run-event-workflows.js.map +1 -1
- package/src/lib/engine/run-org-automations.js +3 -1
- package/src/lib/engine/run-org-automations.js.map +1 -1
- package/src/lib/engine/triggered-docs.d.ts +39 -0
- package/src/lib/engine/triggered-docs.js +47 -0
- package/src/lib/engine/triggered-docs.js.map +1 -0
- package/src/lib/model/host-actions.d.ts +53 -0
- package/src/lib/model/host-actions.js +50 -2
- package/src/lib/model/host-actions.js.map +1 -1
- package/src/lib/model/org-automations.d.ts +6 -4
- package/src/lib/model/org-automations.js +8 -5
- package/src/lib/model/org-automations.js.map +1 -1
- package/src/lib/server-automation-drafts.js +2 -1
- package/src/lib/server-automation-drafts.js.map +1 -1
- package/src/lib/testing/stand-in-crm-steps.js +1 -1
- package/src/lib/testing/stand-in-crm-steps.js.map +1 -1
- package/src/lib/testing/stand-in-recipes.d.ts +3 -2
- package/src/lib/testing/stand-in-recipes.js +17 -12
- 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,
|
|
34
|
-
|
|
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: (
|
|
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)=>
|
|
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)=>
|
|
79
|
-
|
|
80
|
-
|
|
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__*/
|
|
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":"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"}
|
|
@@ -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,
|
|
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:
|
|
811
|
-
//
|
|
812
|
-
//
|
|
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
|
-
|
|
815
|
-
|
|
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: (
|
|
830
|
-
combinator: (
|
|
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
|
-
|
|
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((
|
|
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: (
|
|
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((
|
|
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: (
|
|
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: (
|
|
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: (
|
|
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
|
-
((
|
|
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: ((
|
|
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: (
|
|
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: `${(
|
|
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} ${(
|
|
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: (
|
|
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: (
|
|
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 — ${(
|
|
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, {
|