@aglyn/plugins-outreach 1.0.0-beta.219 → 1.0.0-beta.220
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 +15 -15
- package/src/lib/components/compliance-section.d.ts +5 -0
- package/src/lib/components/compliance-section.js +233 -149
- package/src/lib/components/compliance-section.js.map +1 -1
- package/src/lib/components/enrollment-detail.js +2 -2
- package/src/lib/components/enrollment-detail.js.map +1 -1
- package/src/lib/components/sequence-editor.d.ts +13 -0
- package/src/lib/components/sequence-editor.js +508 -302
- package/src/lib/components/sequence-editor.js.map +1 -1
- package/src/lib/components/sequence-preview.js +25 -10
- package/src/lib/components/sequence-preview.js.map +1 -1
- package/src/lib/components/sequence-step-card.d.ts +10 -0
- package/src/lib/components/sequence-step-card.js +320 -202
- package/src/lib/components/sequence-step-card.js.map +1 -1
- package/src/lib/engine/compose.js +2 -2
- package/src/lib/engine/compose.js.map +1 -1
- package/src/lib/engine/delivery-status.js +1 -1
- package/src/lib/engine/delivery-status.js.map +1 -1
- package/src/lib/engine/enrollment-state.js +2 -2
- package/src/lib/engine/enrollment-state.js.map +1 -1
- package/src/lib/engine/gates.d.ts +2 -2
- package/src/lib/engine/gates.js.map +1 -1
- package/src/lib/engine/opt-out-intent.js +1 -1
- package/src/lib/engine/opt-out-intent.js.map +1 -1
- package/src/lib/engine/sequence-validation.js +2 -2
- package/src/lib/engine/sequence-validation.js.map +1 -1
- package/src/lib/engine/thread-classification.js +3 -3
- package/src/lib/engine/thread-classification.js.map +1 -1
- package/src/lib/enrollment/gate-lookups.js +5 -4
- package/src/lib/enrollment/gate-lookups.js.map +1 -1
- package/src/lib/model/outreach.types.d.ts +1 -1
- package/src/lib/model/outreach.types.js.map +1 -1
- package/src/lib/model/sales-topic.d.ts +27 -0
- package/src/lib/model/sales-topic.js +27 -0
- package/src/lib/model/sales-topic.js.map +1 -0
- package/src/lib/model/sequence-draft.d.ts +1 -1
- package/src/lib/model/sequence-draft.js +2 -2
- package/src/lib/model/sequence-draft.js.map +1 -1
- package/src/lib/model/stored-records.js +2 -2
- package/src/lib/model/stored-records.js.map +1 -1
- package/src/lib/routes/curate-routes.js +7 -7
- package/src/lib/routes/curate-routes.js.map +1 -1
- package/src/lib/routes/enroll-routes.js +10 -13
- package/src/lib/routes/enroll-routes.js.map +1 -1
- package/src/lib/routes/sequence-routes.js +9 -9
- package/src/lib/routes/sequence-routes.js.map +1 -1
- package/src/lib/runtime/campaign-credit.js +2 -2
- package/src/lib/runtime/campaign-credit.js.map +1 -1
- package/src/lib/runtime/campaign-detach.js +3 -2
- package/src/lib/runtime/campaign-detach.js.map +1 -1
- package/src/lib/runtime/lead-records.d.ts +6 -1
- package/src/lib/runtime/lead-records.js +25 -6
- package/src/lib/runtime/lead-records.js.map +1 -1
- package/src/lib/runtime/platform-runtime-deps.js +3 -3
- package/src/lib/runtime/platform-runtime-deps.js.map +1 -1
- package/src/lib/runtime/send-job.js +20 -4
- package/src/lib/runtime/send-job.js.map +1 -1
- package/src/lib/runtime/sync-job.js +3 -3
- package/src/lib/runtime/sync-job.js.map +1 -1
|
@@ -19,18 +19,75 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
19
19
|
import { mdiArrowDown, mdiArrowUp, mdiCodeBraces, mdiEmailFastOutline, mdiTrashCanOutline } from "@aglyn/shared-data-mdi";
|
|
20
20
|
import { MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
21
21
|
import { Autocomplete, Button, Card, CardContent, FormControlLabel, IconButton, ListSubheader, Menu, MenuItem, Stack, Switch, TextField, Tooltip, Typography } from "@mui/material";
|
|
22
|
-
import { useRef, useState } from "react";
|
|
22
|
+
import { memo, useCallback, useRef, useState } from "react";
|
|
23
23
|
import { OUTREACH_MERGE_FIELDS } from "../engine/sequence-validation.js";
|
|
24
24
|
import { OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS, OUTREACH_TASK_KIND_LABELS, OUTREACH_TASK_KINDS } from "../model/outreach.types.js";
|
|
25
25
|
/** The merge fields, grouped the way the picker heads them. */ const FIELD_GROUPS = [
|
|
26
26
|
...new Set(OUTREACH_MERGE_FIELDS.map((field)=>field.group))
|
|
27
27
|
];
|
|
28
|
+
/** A text field that redraws only when what it shows changes (AGL-3423). */ const MemoTextField = /*#__PURE__*/ memo(TextField);
|
|
29
|
+
const HEADER_ROW = {
|
|
30
|
+
alignItems: 'center'
|
|
31
|
+
};
|
|
32
|
+
const TITLE = {
|
|
33
|
+
flexGrow: 1
|
|
34
|
+
};
|
|
35
|
+
const DELAY_FIELD = {
|
|
36
|
+
maxWidth: {
|
|
37
|
+
sm: 360
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const DELAY_SLOT_PROPS = {
|
|
41
|
+
htmlInput: {
|
|
42
|
+
min: 0,
|
|
43
|
+
max: OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS,
|
|
44
|
+
step: 1
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const ACTIONS_ROW = {
|
|
48
|
+
flexWrap: 'wrap',
|
|
49
|
+
rowGap: 1
|
|
50
|
+
};
|
|
51
|
+
const TASK_ROW = {
|
|
52
|
+
xs: 'column',
|
|
53
|
+
sm: 'row'
|
|
54
|
+
};
|
|
55
|
+
const TASK_KIND_FIELD = {
|
|
56
|
+
minWidth: {
|
|
57
|
+
sm: 180
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const templateName = (option)=>option.name;
|
|
61
|
+
const sameTemplate = (option, value)=>option.id === value.id;
|
|
62
|
+
/** Every task kind, as the Task select lists them, built once. */ const TASK_KIND_MENU = OUTREACH_TASK_KINDS.map((kind)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
63
|
+
value: kind,
|
|
64
|
+
children: OUTREACH_TASK_KIND_LABELS[kind]
|
|
65
|
+
}, kind));
|
|
28
66
|
/**
|
|
29
67
|
* One step of the sequence editor (AGL-2980): an email — subject, body or a
|
|
30
68
|
* CRM template, merge fields, the wait before it — or a task for the rep.
|
|
69
|
+
*
|
|
70
|
+
* ## Each field redraws on its own (AGL-3423)
|
|
71
|
+
*
|
|
72
|
+
* The editor keeps the whole sequence in one draft, so a letter typed into a
|
|
73
|
+
* step hands its card a new step. The card's fields are memoized, with
|
|
74
|
+
* handlers that keep their identity and write the step as last drawn, so the
|
|
75
|
+
* letter redraws the field it lands in rather than the wait, the subject, the
|
|
76
|
+
* template picker and the body together. A keystroke that costs one field
|
|
77
|
+
* leaves typed input nothing to queue behind, which is what kept the
|
|
78
|
+
* countries chips from being redrawn by fifty keystrokes in a row (#185).
|
|
31
79
|
*/ export function OutreachStepCard(props) {
|
|
32
80
|
var _OUTREACH_TASK_KIND_LABELS_step_taskKind, _props_issues_step;
|
|
33
81
|
const { step, index, count, disabled } = props;
|
|
82
|
+
// The card as last drawn, which every handler below writes from.
|
|
83
|
+
const latest = useRef(props);
|
|
84
|
+
latest.current = props;
|
|
85
|
+
const moveUp = useCallback(()=>latest.current.onMove(-1), []);
|
|
86
|
+
const moveDown = useCallback(()=>latest.current.onMove(1), []);
|
|
87
|
+
const remove = useCallback(()=>latest.current.onRemove(), []);
|
|
88
|
+
const setDelay = useCallback((event)=>latest.current.onChange(_extends({}, latest.current.step, {
|
|
89
|
+
delayBusinessDays: event.target.value === '' ? Number.NaN : Number(event.target.value)
|
|
90
|
+
})), []);
|
|
34
91
|
const title = step.kind === 'email' ? `Step ${index + 1} · Email` : `Step ${index + 1} · ${(_OUTREACH_TASK_KIND_LABELS_step_taskKind = OUTREACH_TASK_KIND_LABELS[step.taskKind]) != null ? _OUTREACH_TASK_KIND_LABELS_step_taskKind : 'Task'}`;
|
|
35
92
|
return /*#__PURE__*/ _jsx(Card, {
|
|
36
93
|
variant: "outlined",
|
|
@@ -40,68 +97,22 @@ import { OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS, OUTREACH_TASK_KIND_LABELS, OUTRE
|
|
|
40
97
|
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
41
98
|
spacing: 2,
|
|
42
99
|
children: [
|
|
43
|
-
/*#__PURE__*/
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
title: "Move up",
|
|
59
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
60
|
-
children: /*#__PURE__*/ _jsx(IconButton, {
|
|
61
|
-
size: "small",
|
|
62
|
-
"aria-label": `Move step ${index + 1} up`,
|
|
63
|
-
disabled: disabled || index === 0,
|
|
64
|
-
onClick: ()=>props.onMove(-1),
|
|
65
|
-
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
66
|
-
path: mdiArrowUp.path,
|
|
67
|
-
fontSize: "small"
|
|
68
|
-
})
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
}),
|
|
72
|
-
/*#__PURE__*/ _jsx(Tooltip, {
|
|
73
|
-
title: "Move down",
|
|
74
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
75
|
-
children: /*#__PURE__*/ _jsx(IconButton, {
|
|
76
|
-
size: "small",
|
|
77
|
-
"aria-label": `Move step ${index + 1} down`,
|
|
78
|
-
disabled: disabled || index === count - 1,
|
|
79
|
-
onClick: ()=>props.onMove(1),
|
|
80
|
-
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
81
|
-
path: mdiArrowDown.path,
|
|
82
|
-
fontSize: "small"
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
}),
|
|
87
|
-
/*#__PURE__*/ _jsx(Tooltip, {
|
|
88
|
-
title: "Remove",
|
|
89
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
90
|
-
children: /*#__PURE__*/ _jsx(IconButton, {
|
|
91
|
-
size: "small",
|
|
92
|
-
"aria-label": `Remove step ${index + 1}`,
|
|
93
|
-
disabled: disabled || count === 1,
|
|
94
|
-
onClick: props.onRemove,
|
|
95
|
-
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
96
|
-
path: mdiTrashCanOutline.path,
|
|
97
|
-
fontSize: "small"
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
]
|
|
100
|
+
/*#__PURE__*/ _jsx(StepHeader, {
|
|
101
|
+
title: title,
|
|
102
|
+
index: index,
|
|
103
|
+
count: count,
|
|
104
|
+
disabled: disabled,
|
|
105
|
+
onMoveUp: moveUp,
|
|
106
|
+
onMoveDown: moveDown,
|
|
107
|
+
onRemove: remove
|
|
108
|
+
}),
|
|
109
|
+
/*#__PURE__*/ _jsx(DelayField, {
|
|
110
|
+
first: index === 0,
|
|
111
|
+
value: step.delayBusinessDays,
|
|
112
|
+
issue: props.issues['delayBusinessDays'],
|
|
113
|
+
disabled: disabled,
|
|
114
|
+
onChange: setDelay
|
|
103
115
|
}),
|
|
104
|
-
/*#__PURE__*/ _jsx(DelayField, _extends({}, props)),
|
|
105
116
|
step.kind === 'email' ? /*#__PURE__*/ _jsx(EmailStepFields, _extends({}, props, {
|
|
106
117
|
step: step
|
|
107
118
|
})) : step.kind === 'task' ? /*#__PURE__*/ _jsx(TaskStepFields, _extends({}, props, {
|
|
@@ -117,82 +128,159 @@ import { OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS, OUTREACH_TASK_KIND_LABELS, OUTRE
|
|
|
117
128
|
});
|
|
118
129
|
}
|
|
119
130
|
OutreachStepCard.displayName = 'OutreachStepCard';
|
|
120
|
-
function
|
|
121
|
-
const {
|
|
122
|
-
|
|
123
|
-
|
|
131
|
+
/** The step's title and the controls that move or remove it. */ const StepHeader = /*#__PURE__*/ memo(function StepHeader(props) {
|
|
132
|
+
const { index, count, disabled } = props;
|
|
133
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
134
|
+
direction: "row",
|
|
135
|
+
spacing: 1,
|
|
136
|
+
sx: HEADER_ROW,
|
|
137
|
+
children: [
|
|
138
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
139
|
+
variant: "subtitle1",
|
|
140
|
+
sx: TITLE,
|
|
141
|
+
children: props.title
|
|
142
|
+
}),
|
|
143
|
+
/*#__PURE__*/ _jsx(Tooltip, {
|
|
144
|
+
title: "Move up",
|
|
145
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
146
|
+
children: /*#__PURE__*/ _jsx(IconButton, {
|
|
147
|
+
size: "small",
|
|
148
|
+
"aria-label": `Move step ${index + 1} up`,
|
|
149
|
+
disabled: disabled || index === 0,
|
|
150
|
+
onClick: props.onMoveUp,
|
|
151
|
+
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
152
|
+
path: mdiArrowUp.path,
|
|
153
|
+
fontSize: "small"
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
}),
|
|
158
|
+
/*#__PURE__*/ _jsx(Tooltip, {
|
|
159
|
+
title: "Move down",
|
|
160
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
161
|
+
children: /*#__PURE__*/ _jsx(IconButton, {
|
|
162
|
+
size: "small",
|
|
163
|
+
"aria-label": `Move step ${index + 1} down`,
|
|
164
|
+
disabled: disabled || index === count - 1,
|
|
165
|
+
onClick: props.onMoveDown,
|
|
166
|
+
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
167
|
+
path: mdiArrowDown.path,
|
|
168
|
+
fontSize: "small"
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
}),
|
|
173
|
+
/*#__PURE__*/ _jsx(Tooltip, {
|
|
174
|
+
title: "Remove",
|
|
175
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
176
|
+
children: /*#__PURE__*/ _jsx(IconButton, {
|
|
177
|
+
size: "small",
|
|
178
|
+
"aria-label": `Remove step ${index + 1}`,
|
|
179
|
+
disabled: disabled || count === 1,
|
|
180
|
+
onClick: props.onRemove,
|
|
181
|
+
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
182
|
+
path: mdiTrashCanOutline.path,
|
|
183
|
+
fontSize: "small"
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
]
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
const DelayField = /*#__PURE__*/ memo(function DelayField(props) {
|
|
192
|
+
const { first, value, issue } = props;
|
|
124
193
|
return /*#__PURE__*/ _jsx(TextField, {
|
|
125
194
|
type: "number",
|
|
126
195
|
label: first ? 'Wait after enrolling (business days)' : 'Wait after the step before (business days)',
|
|
127
|
-
value: Number.isFinite(
|
|
128
|
-
onChange:
|
|
129
|
-
|
|
130
|
-
})),
|
|
131
|
-
disabled: disabled,
|
|
196
|
+
value: Number.isFinite(value) ? value : '',
|
|
197
|
+
onChange: props.onChange,
|
|
198
|
+
disabled: props.disabled,
|
|
132
199
|
error: Boolean(issue),
|
|
133
200
|
helperText: issue != null ? issue : first ? '0 sends at the next opening of the sending hours.' : 'Counted in the mailbox’s timezone, skipping weekends.',
|
|
134
|
-
slotProps:
|
|
135
|
-
|
|
136
|
-
min: 0,
|
|
137
|
-
max: OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS,
|
|
138
|
-
step: 1
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
sx: {
|
|
142
|
-
maxWidth: {
|
|
143
|
-
sm: 360
|
|
144
|
-
}
|
|
145
|
-
}
|
|
201
|
+
slotProps: DELAY_SLOT_PROPS,
|
|
202
|
+
sx: DELAY_FIELD
|
|
146
203
|
});
|
|
147
|
-
}
|
|
204
|
+
});
|
|
148
205
|
function EmailStepFields(props) {
|
|
149
|
-
var _props_templates_find
|
|
206
|
+
var _props_templates_find;
|
|
150
207
|
const { step, disabled, issues } = props;
|
|
208
|
+
// The step as last drawn, which every handler below writes from.
|
|
209
|
+
const latest = useRef(props);
|
|
210
|
+
latest.current = props;
|
|
151
211
|
const subjectRef = useRef(null);
|
|
152
212
|
const bodyRef = useRef(null);
|
|
153
213
|
const lastFocused = useRef('body');
|
|
154
214
|
const [fieldsAnchor, setFieldsAnchor] = useState(null);
|
|
155
215
|
const template = (_props_templates_find = props.templates.find((option)=>option.id === step.templateId)) != null ? _props_templates_find : null;
|
|
156
216
|
const showSubject = !props.inThread;
|
|
157
|
-
|
|
217
|
+
const write = useCallback((patch)=>latest.current.onChange(_extends({}, latest.current.step, patch)), []);
|
|
218
|
+
const setReply = useCallback((event)=>write({
|
|
219
|
+
replyInThread: event.target.checked
|
|
220
|
+
}), [
|
|
221
|
+
write
|
|
222
|
+
]);
|
|
223
|
+
const setSubject = useCallback((event)=>write({
|
|
224
|
+
subject: event.target.value
|
|
225
|
+
}), [
|
|
226
|
+
write
|
|
227
|
+
]);
|
|
228
|
+
const setBody = useCallback((event)=>write({
|
|
229
|
+
body: event.target.value
|
|
230
|
+
}), [
|
|
231
|
+
write
|
|
232
|
+
]);
|
|
233
|
+
const pickTemplate = useCallback((next)=>{
|
|
234
|
+
var _ref;
|
|
235
|
+
return write({
|
|
236
|
+
templateId: (_ref = next == null ? void 0 : next.id) != null ? _ref : null,
|
|
237
|
+
body: next ? '' : latest.current.step.body
|
|
238
|
+
});
|
|
239
|
+
}, [
|
|
240
|
+
write
|
|
241
|
+
]);
|
|
242
|
+
const focusSubject = useCallback(()=>{
|
|
243
|
+
lastFocused.current = 'subject';
|
|
244
|
+
}, []);
|
|
245
|
+
const focusBody = useCallback(()=>{
|
|
246
|
+
lastFocused.current = 'body';
|
|
247
|
+
}, []);
|
|
248
|
+
const openFields = useCallback((event)=>setFieldsAnchor(event.currentTarget), []);
|
|
249
|
+
const closeFields = useCallback(()=>setFieldsAnchor(null), []);
|
|
250
|
+
const sendTest = useCallback(()=>latest.current.onSendTest == null ? void 0 : latest.current.onSendTest.call(latest.current), []);
|
|
251
|
+
/** Puts `{{key}}` where the caret was in the field last focused. */ const insertField = useCallback((key)=>{
|
|
158
252
|
var _ref, _ref1;
|
|
159
253
|
setFieldsAnchor(null);
|
|
254
|
+
const current = latest.current;
|
|
160
255
|
const token = `{{${key}}}`;
|
|
161
|
-
const target = lastFocused.current === 'subject' &&
|
|
256
|
+
const target = lastFocused.current === 'subject' && !current.inThread ? 'subject' : current.templates.some((option)=>option.id === current.step.templateId) ? null : 'body';
|
|
162
257
|
if (!target) return;
|
|
163
258
|
const element = target === 'subject' ? subjectRef.current : bodyRef.current;
|
|
164
|
-
const value = step[target];
|
|
259
|
+
const value = current.step[target];
|
|
165
260
|
const start = (_ref = element == null ? void 0 : element.selectionStart) != null ? _ref : value.length;
|
|
166
261
|
const end = (_ref1 = element == null ? void 0 : element.selectionEnd) != null ? _ref1 : value.length;
|
|
167
|
-
|
|
262
|
+
current.onChange(_extends({}, current.step, {
|
|
168
263
|
[target]: `${value.slice(0, start)}${token}${value.slice(end)}`
|
|
169
264
|
}));
|
|
170
265
|
requestAnimationFrame(()=>{
|
|
171
266
|
element == null ? void 0 : element.focus();
|
|
172
267
|
element == null ? void 0 : element.setSelectionRange(start + token.length, start + token.length);
|
|
173
268
|
});
|
|
174
|
-
};
|
|
269
|
+
}, []);
|
|
175
270
|
return /*#__PURE__*/ _jsxs(Stack, {
|
|
176
271
|
spacing: 2,
|
|
177
272
|
children: [
|
|
178
|
-
props.firstEmail ? null : /*#__PURE__*/ _jsx(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
replyInThread: event.target.checked
|
|
183
|
-
})),
|
|
184
|
-
disabled: disabled
|
|
185
|
-
}),
|
|
186
|
-
label: "Reply in the same thread"
|
|
273
|
+
props.firstEmail ? null : /*#__PURE__*/ _jsx(ReplyInThreadField, {
|
|
274
|
+
checked: step.replyInThread !== false,
|
|
275
|
+
disabled: disabled,
|
|
276
|
+
onChange: setReply
|
|
187
277
|
}),
|
|
188
|
-
showSubject ? /*#__PURE__*/ _jsx(
|
|
278
|
+
showSubject ? /*#__PURE__*/ _jsx(MemoTextField, {
|
|
189
279
|
label: "Subject",
|
|
190
280
|
value: step.subject,
|
|
191
281
|
inputRef: subjectRef,
|
|
192
|
-
onFocus:
|
|
193
|
-
onChange:
|
|
194
|
-
subject: event.target.value
|
|
195
|
-
})),
|
|
282
|
+
onFocus: focusSubject,
|
|
283
|
+
onChange: setSubject,
|
|
196
284
|
disabled: disabled,
|
|
197
285
|
error: Boolean(issues['subject']),
|
|
198
286
|
helperText: issues['subject'],
|
|
@@ -202,41 +290,25 @@ function EmailStepFields(props) {
|
|
|
202
290
|
color: "text.secondary",
|
|
203
291
|
children: props.threadSubject ? `Sent as a reply: “Re: ${props.threadSubject}”.` : 'Sent as a reply in the thread the earlier email started.'
|
|
204
292
|
}),
|
|
205
|
-
/*#__PURE__*/ _jsx(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
],
|
|
209
|
-
value: template,
|
|
210
|
-
getOptionLabel: (option)=>option.name,
|
|
211
|
-
isOptionEqualToValue: (option, value)=>option.id === value.id,
|
|
212
|
-
onChange: (_event, next)=>{
|
|
213
|
-
var _ref;
|
|
214
|
-
return props.onChange(_extends({}, step, {
|
|
215
|
-
templateId: (_ref = next == null ? void 0 : next.id) != null ? _ref : null,
|
|
216
|
-
body: next ? '' : step.body
|
|
217
|
-
}));
|
|
218
|
-
},
|
|
293
|
+
/*#__PURE__*/ _jsx(TemplateField, {
|
|
294
|
+
templates: props.templates,
|
|
295
|
+
template: template,
|
|
219
296
|
disabled: disabled,
|
|
220
|
-
|
|
221
|
-
label: "CRM email template",
|
|
222
|
-
helperText: template ? 'The template’s body is sent; this step’s own subject is the one used.' : 'Optional. Pick one to send its body instead of writing it here.'
|
|
223
|
-
}))
|
|
297
|
+
onChange: pickTemplate
|
|
224
298
|
}),
|
|
225
|
-
template ? /*#__PURE__*/ _jsx(
|
|
299
|
+
template ? /*#__PURE__*/ _jsx(MemoTextField, {
|
|
226
300
|
label: "Body (from the template)",
|
|
227
301
|
value: template.body,
|
|
228
302
|
multiline: true,
|
|
229
303
|
minRows: 4,
|
|
230
304
|
fullWidth: true,
|
|
231
305
|
disabled: true
|
|
232
|
-
}) : /*#__PURE__*/ _jsx(
|
|
306
|
+
}) : /*#__PURE__*/ _jsx(MemoTextField, {
|
|
233
307
|
label: "Body",
|
|
234
308
|
value: step.body,
|
|
235
309
|
inputRef: bodyRef,
|
|
236
|
-
onFocus:
|
|
237
|
-
onChange:
|
|
238
|
-
body: event.target.value
|
|
239
|
-
})),
|
|
310
|
+
onFocus: focusBody,
|
|
311
|
+
onChange: setBody,
|
|
240
312
|
disabled: disabled,
|
|
241
313
|
error: Boolean(issues['body']),
|
|
242
314
|
helperText: issues['body'],
|
|
@@ -244,98 +316,131 @@ function EmailStepFields(props) {
|
|
|
244
316
|
minRows: 6,
|
|
245
317
|
fullWidth: true
|
|
246
318
|
}),
|
|
247
|
-
/*#__PURE__*/
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
319
|
+
/*#__PURE__*/ _jsx(EmailStepActions, {
|
|
320
|
+
stepNumber: props.index + 1,
|
|
321
|
+
insertDisabled: Boolean(disabled || template && !showSubject),
|
|
322
|
+
canSendTest: Boolean(props.onSendTest),
|
|
323
|
+
sendTestDisabledReason: props.sendTestDisabledReason,
|
|
324
|
+
onInsertField: openFields,
|
|
325
|
+
onSendTest: sendTest
|
|
326
|
+
}),
|
|
327
|
+
/*#__PURE__*/ _jsx(MergeFieldMenu, {
|
|
328
|
+
anchorEl: fieldsAnchor,
|
|
329
|
+
onClose: closeFields,
|
|
330
|
+
onPick: insertField
|
|
331
|
+
})
|
|
332
|
+
]
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const ReplyInThreadField = /*#__PURE__*/ memo(function ReplyInThreadField(props) {
|
|
336
|
+
return /*#__PURE__*/ _jsx(FormControlLabel, {
|
|
337
|
+
control: /*#__PURE__*/ _jsx(Switch, {
|
|
338
|
+
checked: props.checked,
|
|
339
|
+
onChange: props.onChange,
|
|
340
|
+
disabled: props.disabled
|
|
341
|
+
}),
|
|
342
|
+
label: "Reply in the same thread"
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
const TemplateField = /*#__PURE__*/ memo(function TemplateField(props) {
|
|
346
|
+
const { template, onChange } = props;
|
|
347
|
+
return /*#__PURE__*/ _jsx(Autocomplete, {
|
|
348
|
+
options: [
|
|
349
|
+
...props.templates
|
|
350
|
+
],
|
|
351
|
+
value: template,
|
|
352
|
+
getOptionLabel: templateName,
|
|
353
|
+
isOptionEqualToValue: sameTemplate,
|
|
354
|
+
onChange: (_event, next)=>onChange(next),
|
|
355
|
+
disabled: props.disabled,
|
|
356
|
+
renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
|
|
357
|
+
label: "CRM email template",
|
|
358
|
+
helperText: template ? 'The template’s body is sent; this step’s own subject is the one used.' : 'Optional. Pick one to send its body instead of writing it here.'
|
|
359
|
+
}))
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
const EmailStepActions = /*#__PURE__*/ memo(function EmailStepActions(props) {
|
|
363
|
+
var _props_sendTestDisabledReason;
|
|
364
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
365
|
+
direction: "row",
|
|
366
|
+
spacing: 1,
|
|
367
|
+
sx: ACTIONS_ROW,
|
|
368
|
+
children: [
|
|
369
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
370
|
+
size: "small",
|
|
371
|
+
variant: "outlined",
|
|
372
|
+
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
373
|
+
path: mdiCodeBraces.path,
|
|
374
|
+
fontSize: "small"
|
|
375
|
+
}),
|
|
376
|
+
onClick: props.onInsertField,
|
|
377
|
+
disabled: props.insertDisabled,
|
|
378
|
+
"aria-haspopup": "menu",
|
|
379
|
+
children: "Insert field"
|
|
380
|
+
}),
|
|
381
|
+
props.canSendTest ? /*#__PURE__*/ _jsx(Tooltip, {
|
|
382
|
+
title: (_props_sendTestDisabledReason = props.sendTestDisabledReason) != null ? _props_sendTestDisabledReason : '',
|
|
383
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
384
|
+
children: /*#__PURE__*/ _jsx(Button, {
|
|
256
385
|
size: "small",
|
|
257
386
|
variant: "outlined",
|
|
258
387
|
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
259
|
-
path:
|
|
388
|
+
path: mdiEmailFastOutline.path,
|
|
260
389
|
fontSize: "small"
|
|
261
390
|
}),
|
|
262
|
-
onClick:
|
|
263
|
-
disabled:
|
|
264
|
-
"aria-
|
|
265
|
-
children: "
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
270
|
-
children: /*#__PURE__*/ _jsx(Button, {
|
|
271
|
-
size: "small",
|
|
272
|
-
variant: "outlined",
|
|
273
|
-
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
274
|
-
path: mdiEmailFastOutline.path,
|
|
275
|
-
fontSize: "small"
|
|
276
|
-
}),
|
|
277
|
-
onClick: props.onSendTest,
|
|
278
|
-
disabled: Boolean(props.sendTestDisabledReason),
|
|
279
|
-
"aria-label": `Send a test of step ${props.index + 1}`,
|
|
280
|
-
children: "Send a test"
|
|
281
|
-
})
|
|
282
|
-
})
|
|
283
|
-
}) : null
|
|
284
|
-
]
|
|
285
|
-
}),
|
|
286
|
-
/*#__PURE__*/ _jsx(Menu, {
|
|
287
|
-
anchorEl: fieldsAnchor,
|
|
288
|
-
open: Boolean(fieldsAnchor),
|
|
289
|
-
onClose: ()=>setFieldsAnchor(null),
|
|
290
|
-
children: FIELD_GROUPS.flatMap((group)=>[
|
|
291
|
-
/*#__PURE__*/ _jsx(ListSubheader, {
|
|
292
|
-
children: group
|
|
293
|
-
}, `group-${group}`),
|
|
294
|
-
...OUTREACH_MERGE_FIELDS.filter((field)=>field.group === group).map((field)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
295
|
-
onClick: ()=>insertField(field.key),
|
|
296
|
-
children: field.label
|
|
297
|
-
}, field.key))
|
|
298
|
-
])
|
|
299
|
-
})
|
|
391
|
+
onClick: props.onSendTest,
|
|
392
|
+
disabled: Boolean(props.sendTestDisabledReason),
|
|
393
|
+
"aria-label": `Send a test of step ${props.stepNumber}`,
|
|
394
|
+
children: "Send a test"
|
|
395
|
+
})
|
|
396
|
+
})
|
|
397
|
+
}) : null
|
|
300
398
|
]
|
|
301
399
|
});
|
|
302
|
-
}
|
|
400
|
+
});
|
|
401
|
+
const MergeFieldMenu = /*#__PURE__*/ memo(function MergeFieldMenu(props) {
|
|
402
|
+
const { onPick } = props;
|
|
403
|
+
return /*#__PURE__*/ _jsx(Menu, {
|
|
404
|
+
anchorEl: props.anchorEl,
|
|
405
|
+
open: Boolean(props.anchorEl),
|
|
406
|
+
onClose: props.onClose,
|
|
407
|
+
children: FIELD_GROUPS.flatMap((group)=>[
|
|
408
|
+
/*#__PURE__*/ _jsx(ListSubheader, {
|
|
409
|
+
children: group
|
|
410
|
+
}, `group-${group}`),
|
|
411
|
+
...OUTREACH_MERGE_FIELDS.filter((field)=>field.group === group).map((field)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
412
|
+
onClick: ()=>onPick(field.key),
|
|
413
|
+
children: field.label
|
|
414
|
+
}, field.key))
|
|
415
|
+
])
|
|
416
|
+
});
|
|
417
|
+
});
|
|
303
418
|
function TaskStepFields(props) {
|
|
304
419
|
var _issues_title;
|
|
305
420
|
const { step, disabled, issues } = props;
|
|
421
|
+
// The step as last drawn, which both handlers write from.
|
|
422
|
+
const latest = useRef(props);
|
|
423
|
+
latest.current = props;
|
|
424
|
+
const setKind = useCallback((event)=>latest.current.onChange(_extends({}, latest.current.step, {
|
|
425
|
+
taskKind: event.target.value
|
|
426
|
+
})), []);
|
|
427
|
+
const setTitle = useCallback((event)=>latest.current.onChange(_extends({}, latest.current.step, {
|
|
428
|
+
title: event.target.value
|
|
429
|
+
})), []);
|
|
306
430
|
return /*#__PURE__*/ _jsxs(Stack, {
|
|
307
|
-
direction:
|
|
308
|
-
xs: 'column',
|
|
309
|
-
sm: 'row'
|
|
310
|
-
},
|
|
431
|
+
direction: TASK_ROW,
|
|
311
432
|
spacing: 2,
|
|
312
433
|
children: [
|
|
313
|
-
/*#__PURE__*/ _jsx(
|
|
314
|
-
select: true,
|
|
315
|
-
label: "Task",
|
|
434
|
+
/*#__PURE__*/ _jsx(TaskKindField, {
|
|
316
435
|
value: OUTREACH_TASK_KINDS.includes(step.taskKind) ? step.taskKind : '',
|
|
317
|
-
|
|
318
|
-
taskKind: event.target.value
|
|
319
|
-
})),
|
|
436
|
+
issue: issues['taskKind'],
|
|
320
437
|
disabled: disabled,
|
|
321
|
-
|
|
322
|
-
helperText: issues['taskKind'],
|
|
323
|
-
sx: {
|
|
324
|
-
minWidth: {
|
|
325
|
-
sm: 180
|
|
326
|
-
}
|
|
327
|
-
},
|
|
328
|
-
children: OUTREACH_TASK_KINDS.map((kind)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
329
|
-
value: kind,
|
|
330
|
-
children: OUTREACH_TASK_KIND_LABELS[kind]
|
|
331
|
-
}, kind))
|
|
438
|
+
onChange: setKind
|
|
332
439
|
}),
|
|
333
|
-
/*#__PURE__*/ _jsx(
|
|
440
|
+
/*#__PURE__*/ _jsx(MemoTextField, {
|
|
334
441
|
label: "Title",
|
|
335
442
|
value: step.title,
|
|
336
|
-
onChange:
|
|
337
|
-
title: event.target.value
|
|
338
|
-
})),
|
|
443
|
+
onChange: setTitle,
|
|
339
444
|
disabled: disabled,
|
|
340
445
|
error: Boolean(issues['title']),
|
|
341
446
|
helperText: (_issues_title = issues['title']) != null ? _issues_title : 'What the task says, for example “Connect on LinkedIn”.',
|
|
@@ -344,6 +449,19 @@ function TaskStepFields(props) {
|
|
|
344
449
|
]
|
|
345
450
|
});
|
|
346
451
|
}
|
|
452
|
+
const TaskKindField = /*#__PURE__*/ memo(function TaskKindField(props) {
|
|
453
|
+
return /*#__PURE__*/ _jsx(TextField, {
|
|
454
|
+
select: true,
|
|
455
|
+
label: "Task",
|
|
456
|
+
value: props.value,
|
|
457
|
+
onChange: props.onChange,
|
|
458
|
+
disabled: props.disabled,
|
|
459
|
+
error: Boolean(props.issue),
|
|
460
|
+
helperText: props.issue,
|
|
461
|
+
sx: TASK_KIND_FIELD,
|
|
462
|
+
children: TASK_KIND_MENU
|
|
463
|
+
});
|
|
464
|
+
});
|
|
347
465
|
export default OutreachStepCard;
|
|
348
466
|
|
|
349
467
|
//# sourceMappingURL=sequence-step-card.js.map
|