@arbidocs/blocks 0.4.75 → 0.4.77
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/dist/agents/index.cjs +341 -253
- package/dist/agents/index.js +3 -3
- package/dist/agents/tasks/TaskActivityTimeline.d.ts +1 -1
- package/dist/agents/tasks/WorkflowThreadStrip.d.ts +44 -1
- package/dist/agents/tasks/index.d.ts +2 -1
- package/dist/agents/tasks/linkedRuns.d.ts +31 -0
- package/dist/agents/workflows/SchemaForm.d.ts +6 -1
- package/dist/agents/workflows/StepParts.d.ts +109 -0
- package/dist/agents/workflows/WorkflowRunner.d.ts +11 -2
- package/dist/agents/workflows/index.d.ts +3 -2
- package/dist/agents/workflows/planSteps.d.ts +10 -1
- package/dist/agents/workflows/run.d.ts +61 -8
- package/dist/agents/workflows/types.d.ts +160 -4
- package/dist/agents/workflows/useWorkflowPreview.d.ts +2 -1
- package/dist/agents/workflows/useWorkflowView.d.ts +19 -0
- package/dist/chat/ChatPage.d.ts +1 -1
- package/dist/chat/WorkflowStepChip.d.ts +24 -0
- package/dist/chat/chat-page-types.d.ts +4 -0
- package/dist/chat/index.cjs +130 -122
- package/dist/chat/index.d.ts +1 -0
- package/dist/chat/index.js +2 -2
- package/dist/{chunk-YWYVIQ7T.js → chunk-34UW6XUQ.js} +948 -38
- package/dist/chunk-34UW6XUQ.js.map +1 -0
- package/dist/{chunk-P2RBE3FD.cjs → chunk-3XR3IEGU.cjs} +965 -35
- package/dist/chunk-3XR3IEGU.cjs.map +1 -0
- package/dist/{chunk-ZWA7UUZL.cjs → chunk-7YUFMNFW.cjs} +2 -54
- package/dist/chunk-7YUFMNFW.cjs.map +1 -0
- package/dist/chunk-A24C6BNA.js +92 -0
- package/dist/chunk-A24C6BNA.js.map +1 -0
- package/dist/chunk-AINNADMT.cjs +62 -0
- package/dist/chunk-AINNADMT.cjs.map +1 -0
- package/dist/{chunk-7CVF3DK2.cjs → chunk-EKIUMNCN.cjs} +5 -5
- package/dist/{chunk-7CVF3DK2.cjs.map → chunk-EKIUMNCN.cjs.map} +1 -1
- package/dist/chunk-FE4ULGOR.js +151 -0
- package/dist/chunk-FE4ULGOR.js.map +1 -0
- package/dist/chunk-FT5FFXSJ.js +60 -0
- package/dist/chunk-FT5FFXSJ.js.map +1 -0
- package/dist/{chunk-HITAN7JF.cjs → chunk-GTXOV7IJ.cjs} +4 -4
- package/dist/{chunk-HITAN7JF.cjs.map → chunk-GTXOV7IJ.cjs.map} +1 -1
- package/dist/{chunk-LT2UORRG.js → chunk-HEVVT6IT.js} +205 -38
- package/dist/chunk-HEVVT6IT.js.map +1 -0
- package/dist/{chunk-IPGKTUF7.cjs → chunk-HOD6OM7L.cjs} +68 -3
- package/dist/chunk-HOD6OM7L.cjs.map +1 -0
- package/dist/{chunk-F4LUD3YC.cjs → chunk-JFUN4D7D.cjs} +229 -60
- package/dist/chunk-JFUN4D7D.cjs.map +1 -0
- package/dist/{chunk-UIWLDSTF.js → chunk-NIP5BOP7.js} +68 -5
- package/dist/chunk-NIP5BOP7.js.map +1 -0
- package/dist/{chunk-MK6NJQGF.js → chunk-OEM5ARVM.js} +3 -3
- package/dist/{chunk-MK6NJQGF.js.map → chunk-OEM5ARVM.js.map} +1 -1
- package/dist/{chunk-5EBGRG3X.js → chunk-OPA2Y6YZ.js} +3 -54
- package/dist/chunk-OPA2Y6YZ.js.map +1 -0
- package/dist/chunk-TZR36PPP.cjs +95 -0
- package/dist/chunk-TZR36PPP.cjs.map +1 -0
- package/dist/{chunk-WJEKRSPH.js → chunk-UEU23PTW.js} +3 -3
- package/dist/{chunk-WJEKRSPH.js.map → chunk-UEU23PTW.js.map} +1 -1
- package/dist/chunk-ZVBPRMHJ.cjs +153 -0
- package/dist/chunk-ZVBPRMHJ.cjs.map +1 -0
- package/dist/connect/docx.cjs +10 -88
- package/dist/connect/docx.cjs.map +1 -1
- package/dist/connect/docx.js +6 -86
- package/dist/connect/docx.js.map +1 -1
- package/dist/connect/index.cjs +295 -32
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.js +280 -17
- package/dist/connect/index.js.map +1 -1
- package/dist/connection/ConnectedWorkflowRunner.d.ts +16 -2
- package/dist/connection/WorkflowRunnerDialogs.d.ts +37 -0
- package/dist/connection/useWorkflowThread.d.ts +12 -0
- package/dist/document-tools/index.cjs +7 -143
- package/dist/document-tools/index.cjs.map +1 -1
- package/dist/document-tools/index.js +5 -144
- package/dist/document-tools/index.js.map +1 -1
- package/dist/docx/index.cjs +18 -17
- package/dist/docx/index.cjs.map +1 -1
- package/dist/docx/index.js +3 -2
- package/dist/docx/index.js.map +1 -1
- package/dist/facts/index.cjs +1 -1
- package/dist/facts/index.js +1 -1
- package/dist/files/index.cjs +1 -1
- package/dist/files/index.js +1 -1
- package/dist/home/index.cjs +1 -1
- package/dist/home/index.js +1 -1
- package/dist/index.cjs +631 -535
- package/dist/index.js +8 -8
- package/dist/settings/index.cjs +1 -1
- package/dist/settings/index.js +1 -1
- package/dist/shell/index.cjs +1 -1
- package/dist/shell/index.js +1 -1
- package/dist/studio/index.cjs +4 -4
- package/dist/studio/index.js +3 -3
- package/dist/workflow-canvas/SignalsEditor.d.ts +41 -0
- package/dist/workflow-canvas/StepActionSettings.d.ts +132 -0
- package/dist/workflow-canvas/StepInspector.d.ts +28 -2
- package/dist/workflow-canvas/TriggerInspector.d.ts +11 -2
- package/dist/workflow-canvas/WorkflowBuilder.d.ts +17 -4
- package/dist/workflow-canvas/WorkflowCanvas.d.ts +27 -2
- package/dist/workflow-canvas/edit.d.ts +9 -1
- package/dist/workflow-canvas/index.cjs +1282 -208
- package/dist/workflow-canvas/index.cjs.map +1 -1
- package/dist/workflow-canvas/index.d.ts +2 -1
- package/dist/workflow-canvas/index.js +1255 -188
- package/dist/workflow-canvas/index.js.map +1 -1
- package/dist/workflow-canvas/trigger.d.ts +20 -1
- package/package.json +4 -4
- package/src/workflow-canvas/canvas.css +9 -0
- package/dist/chunk-5EBGRG3X.js.map +0 -1
- package/dist/chunk-F4LUD3YC.cjs.map +0 -1
- package/dist/chunk-IPGKTUF7.cjs.map +0 -1
- package/dist/chunk-LT2UORRG.js.map +0 -1
- package/dist/chunk-P2RBE3FD.cjs.map +0 -1
- package/dist/chunk-UIWLDSTF.js.map +0 -1
- package/dist/chunk-YWYVIQ7T.js.map +0 -1
- package/dist/chunk-ZWA7UUZL.cjs.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useConnection } from '../chunk-OYMWV45N.js';
|
|
3
|
-
import { describeRRule, RecurrenceBuilder, AgentSelect, useWorkflowPreview,
|
|
3
|
+
import { workflowWhen, isoToLocalInput, describeRRule, RecurrenceBuilder, AgentSelect, useWorkflowPreview, localInputToIso } from '../chunk-HEVVT6IT.js';
|
|
4
4
|
import '../chunk-CKB64Z4R.js';
|
|
5
5
|
import '../chunk-P6GHJSLR.js';
|
|
6
|
-
import { workflowSteps, dependsOn, stepLevels,
|
|
6
|
+
import { workflowSteps, dependsOn, stepLevels, isIncompleteSignal, runFields, upstreamSteps, stepDependencies, canAssign, fromPlanSteps, layoutFromPlanSteps, validateWorkflowGraph, stepConfigProblems, WorkflowRunner, skillToWorkflow, assigneeName, orderSteps, toPlanSteps } from '../chunk-34UW6XUQ.js';
|
|
7
7
|
import '../chunk-4WWM3GQ5.js';
|
|
8
8
|
import '../chunk-RFXJYIDV.js';
|
|
9
9
|
import '../chunk-DPLRHEJZ.js';
|
|
@@ -14,23 +14,33 @@ import '../chunk-ZPLG7QYF.js';
|
|
|
14
14
|
import '../chunk-DDF5GPIJ.js';
|
|
15
15
|
import '../chunk-XD2M6LIG.js';
|
|
16
16
|
import '../chunk-S7PSMMRG.js';
|
|
17
|
+
import '../chunk-76JDDMSP.js';
|
|
17
18
|
import '../chunk-HOXKR26S.js';
|
|
18
19
|
import '../chunk-AA3PT6KR.js';
|
|
19
20
|
import '../chunk-SLSBJRWW.js';
|
|
20
21
|
import '../chunk-NPLYHR2E.js';
|
|
21
|
-
import '../chunk-76JDDMSP.js';
|
|
22
22
|
import '../chunk-OXF2IFXG.js';
|
|
23
23
|
import '../chunk-TX2ZICS7.js';
|
|
24
24
|
import '../chunk-7F6CBP3Z.js';
|
|
25
25
|
import '../chunk-VUNV25KB.js';
|
|
26
26
|
import { useMemo, useState, useEffect, useCallback, useRef } from 'react';
|
|
27
|
-
import { useWorkflows, useSkills, useAgents, useConversations, useCreateWorkflow, useUpdateWorkflow, useRunWorkflow } from '@arbidocs/react';
|
|
28
|
-
import {
|
|
29
|
-
import { X,
|
|
27
|
+
import { useWorkspaceUsers, useTags, useDocuments, useWorkflows, useSkills, useAgents, useConversations, useCreateWorkflow, useUpdateWorkflow, useRunWorkflow } from '@arbidocs/react';
|
|
28
|
+
import { Label, Input, cn, Button, Textarea, Checkbox, Popover, PopoverTrigger, PopoverContent } from '@arbidocs/react/ui';
|
|
29
|
+
import { X, Plus, Trash2, Maximize, Table2, Mail, PenLine, ClipboardCheck, Bot, Upload, FileInput, PencilRuler, Eye, LayoutGrid, Settings2, Play, Loader2, Save, Zap, GitBranch, FilePenLine, UserRound, Hourglass, Check, AlertTriangle, MinusCircle } from 'lucide-react';
|
|
30
30
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
31
31
|
import { MarkerType, applyNodeChanges, ReactFlow, Background, BackgroundVariant, Controls, ControlButton, MiniMap, Handle, Position } from '@xyflow/react';
|
|
32
32
|
|
|
33
33
|
// src/workflow-canvas/edit.ts
|
|
34
|
+
var WORKFLOW_STEP_KINDS = [
|
|
35
|
+
"form",
|
|
36
|
+
"upload",
|
|
37
|
+
"agent",
|
|
38
|
+
"review",
|
|
39
|
+
"sign",
|
|
40
|
+
"email_draft",
|
|
41
|
+
"tabular_review"
|
|
42
|
+
];
|
|
43
|
+
var stepKindOf = (step) => step.kind === "action" ? step.action.type : step.kind;
|
|
34
44
|
function newSteppedWorkflow(id, title) {
|
|
35
45
|
return {
|
|
36
46
|
id,
|
|
@@ -63,7 +73,10 @@ var DEFAULT_TITLES = {
|
|
|
63
73
|
form: "Details",
|
|
64
74
|
upload: "Upload files",
|
|
65
75
|
agent: "Agent step",
|
|
66
|
-
review: "Review"
|
|
76
|
+
review: "Review",
|
|
77
|
+
sign: "Sign",
|
|
78
|
+
email_draft: "Email draft",
|
|
79
|
+
tabular_review: "Tabular review"
|
|
67
80
|
};
|
|
68
81
|
function blankStep(kind, id, title) {
|
|
69
82
|
switch (kind) {
|
|
@@ -75,6 +88,22 @@ function blankStep(kind, id, title) {
|
|
|
75
88
|
return { id, kind, title };
|
|
76
89
|
case "review":
|
|
77
90
|
return { id, kind, title, instructions: "" };
|
|
91
|
+
case "sign":
|
|
92
|
+
return { id, kind: "action", title, action: { type: "sign", signers: [] } };
|
|
93
|
+
case "email_draft":
|
|
94
|
+
return {
|
|
95
|
+
id,
|
|
96
|
+
kind: "action",
|
|
97
|
+
title,
|
|
98
|
+
action: { type: "email_draft", to: [], subject: "", body: "" }
|
|
99
|
+
};
|
|
100
|
+
case "tabular_review":
|
|
101
|
+
return {
|
|
102
|
+
id,
|
|
103
|
+
kind: "action",
|
|
104
|
+
title,
|
|
105
|
+
action: { type: "tabular_review", tags: [], documents: "run_uploads" }
|
|
106
|
+
};
|
|
78
107
|
}
|
|
79
108
|
}
|
|
80
109
|
function addStep(workflow, kind, options = {}) {
|
|
@@ -139,14 +168,49 @@ function renameInput(workflow, from, to) {
|
|
|
139
168
|
return {
|
|
140
169
|
...s,
|
|
141
170
|
with: s.with ? Object.fromEntries(Object.entries(s.with).map(([k, v]) => [k, renameIn(v, from, to)])) : s.with,
|
|
142
|
-
run: s.run?.kind === "prompt" ? { ...s.run, promptTemplate: renameIn(s.run.promptTemplate, from, to) } : s.run
|
|
171
|
+
run: s.run?.kind === "prompt" ? { ...s.run, promptTemplate: renameIn(s.run.promptTemplate, from, to) } : s.run,
|
|
172
|
+
...s.editDocument ? { editDocument: renameRef(s.editDocument, from, to) } : {}
|
|
143
173
|
};
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
174
|
+
const assignee = s.assignee && renamePerson(s.assignee, from, to);
|
|
175
|
+
const base = assignee ? { ...s, assignee } : s;
|
|
176
|
+
if (base.kind === "review" && base.instructions)
|
|
177
|
+
return { ...base, instructions: renameIn(base.instructions, from, to) };
|
|
178
|
+
if (base.kind === "action") {
|
|
179
|
+
const a = base.action;
|
|
180
|
+
if (a.type === "sign")
|
|
181
|
+
return {
|
|
182
|
+
...base,
|
|
183
|
+
action: {
|
|
184
|
+
...a,
|
|
185
|
+
document: a.document && renameRef(a.document, from, to),
|
|
186
|
+
signers: a.signers.map((x) => renamePerson(x, from, to)),
|
|
187
|
+
...a.message ? { message: renameIn(a.message, from, to) } : {}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
if (a.type === "email_draft")
|
|
191
|
+
return {
|
|
192
|
+
...base,
|
|
193
|
+
action: {
|
|
194
|
+
...a,
|
|
195
|
+
to: a.to.map((t) => renameIn(t, from, to)),
|
|
196
|
+
...a.cc ? { cc: a.cc.map((t) => renameIn(t, from, to)) } : {},
|
|
197
|
+
subject: renameIn(a.subject, from, to),
|
|
198
|
+
body: renameIn(a.body, from, to),
|
|
199
|
+
...a.attachments ? { attachments: a.attachments.map((r) => renameRef(r, from, to)) } : {}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
if (Array.isArray(a.documents))
|
|
203
|
+
return {
|
|
204
|
+
...base,
|
|
205
|
+
action: { ...a, documents: a.documents.map((r) => renameRef(r, from, to)) }
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return base;
|
|
147
209
|
});
|
|
148
210
|
return withSteps(workflow, steps);
|
|
149
211
|
}
|
|
212
|
+
var renamePerson = (a, from, to) => "input" in a && a.input === from ? { input: to } : a;
|
|
213
|
+
var renameRef = (r, from, to) => "input" in r && r.input === from ? { input: to } : r;
|
|
150
214
|
function moveStep(workflow, id, position) {
|
|
151
215
|
return withSteps(workflow, workflowSteps(workflow), { ...layoutOf(workflow), [id]: position });
|
|
152
216
|
}
|
|
@@ -165,6 +229,753 @@ function layoutWithDefaults(workflow) {
|
|
|
165
229
|
const auto = autoLayout(workflowSteps(workflow));
|
|
166
230
|
return { ...auto, ...layoutOf(workflow) };
|
|
167
231
|
}
|
|
232
|
+
var DEFAULT_SIGNALS_EDITOR_LABELS = {
|
|
233
|
+
heading: "When done, signal\u2026",
|
|
234
|
+
hint: "Another workflow can start on a signal (\u201CWhen a step signals\u201D), as its own thread linked to this one.",
|
|
235
|
+
add: "Add signal",
|
|
236
|
+
name: "Signal",
|
|
237
|
+
namePlaceholder: "e.g. risk-high",
|
|
238
|
+
when: "When",
|
|
239
|
+
always: "Whenever it is done",
|
|
240
|
+
fieldEquals: "A field equals",
|
|
241
|
+
answerContains: "The answer contains",
|
|
242
|
+
answerMatches: "The answer matches (regex)",
|
|
243
|
+
approved: "It is approved",
|
|
244
|
+
field: "Field",
|
|
245
|
+
value: "Value",
|
|
246
|
+
phrase: "Phrase",
|
|
247
|
+
phrasePlaceholder: "e.g. Risk rating: High",
|
|
248
|
+
pattern: "Pattern",
|
|
249
|
+
remove: "Remove signal"
|
|
250
|
+
};
|
|
251
|
+
function signalConditions(kind) {
|
|
252
|
+
switch (kind) {
|
|
253
|
+
case "form":
|
|
254
|
+
return ["always", "field"];
|
|
255
|
+
case "agent":
|
|
256
|
+
return ["always", "contains", "matches"];
|
|
257
|
+
case "review":
|
|
258
|
+
return ["approved", "always"];
|
|
259
|
+
default:
|
|
260
|
+
return ["always"];
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function conditionOf(when) {
|
|
264
|
+
if (!when) return "always";
|
|
265
|
+
if ("field" in when) return "field";
|
|
266
|
+
if ("contains" in when) return "contains";
|
|
267
|
+
if ("matches" in when) return "matches";
|
|
268
|
+
return "approved";
|
|
269
|
+
}
|
|
270
|
+
function signalSlug(text) {
|
|
271
|
+
return text.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^[^a-z0-9]+/, "").slice(0, 64);
|
|
272
|
+
}
|
|
273
|
+
function startCondition(kind, step) {
|
|
274
|
+
switch (kind) {
|
|
275
|
+
case "field":
|
|
276
|
+
return { field: step.kind === "form" ? step.inputs[0]?.name ?? "" : "", equals: "" };
|
|
277
|
+
case "contains":
|
|
278
|
+
return { contains: "" };
|
|
279
|
+
case "matches":
|
|
280
|
+
return { matches: "" };
|
|
281
|
+
case "approved":
|
|
282
|
+
return { approved: true };
|
|
283
|
+
default:
|
|
284
|
+
return void 0;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function newSignal(step, taken) {
|
|
288
|
+
const base = signalSlug(step.id) || "signal";
|
|
289
|
+
let name = `${base}-done`;
|
|
290
|
+
let n = 2;
|
|
291
|
+
while (taken.includes(name)) name = `${base}-done-${n++}`;
|
|
292
|
+
const when = startCondition(signalConditions(step.kind)[0], step);
|
|
293
|
+
return when ? { name, when } : { name };
|
|
294
|
+
}
|
|
295
|
+
var selectClass = "flex h-8 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
|
|
296
|
+
function SignalsEditor({
|
|
297
|
+
step,
|
|
298
|
+
onChange,
|
|
299
|
+
labels,
|
|
300
|
+
testId = "signals-editor"
|
|
301
|
+
}) {
|
|
302
|
+
const l = { ...DEFAULT_SIGNALS_EDITOR_LABELS, ...labels };
|
|
303
|
+
const signals = step.signals ?? [];
|
|
304
|
+
const conditions = signalConditions(step.kind);
|
|
305
|
+
const conditionLabel = {
|
|
306
|
+
always: l.always,
|
|
307
|
+
field: l.fieldEquals,
|
|
308
|
+
contains: l.answerContains,
|
|
309
|
+
matches: l.answerMatches,
|
|
310
|
+
approved: l.approved
|
|
311
|
+
};
|
|
312
|
+
const emit = (next) => onChange(next.length ? next : void 0);
|
|
313
|
+
const set = (i, signal) => emit(signals.map((s, j) => j === i ? signal : s));
|
|
314
|
+
const fields = step.kind === "form" ? step.inputs : [];
|
|
315
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", "data-testid": testId, children: [
|
|
316
|
+
/* @__PURE__ */ jsx(Label, { className: "text-xs", children: l.heading }),
|
|
317
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
318
|
+
signals.map((signal, i) => {
|
|
319
|
+
const condition = conditionOf(signal.when);
|
|
320
|
+
const when = signal.when;
|
|
321
|
+
return /* @__PURE__ */ jsxs(
|
|
322
|
+
"div",
|
|
323
|
+
{
|
|
324
|
+
className: cn(
|
|
325
|
+
"space-y-1.5 rounded-md border p-2",
|
|
326
|
+
isIncompleteSignal(signal) ? "border-destructive/50" : "border-border"
|
|
327
|
+
),
|
|
328
|
+
"data-incomplete": isIncompleteSignal(signal) || void 0,
|
|
329
|
+
"data-testid": `${testId}-${i}`,
|
|
330
|
+
children: [
|
|
331
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
332
|
+
/* @__PURE__ */ jsx(
|
|
333
|
+
Input,
|
|
334
|
+
{
|
|
335
|
+
value: signal.name,
|
|
336
|
+
"aria-label": l.name,
|
|
337
|
+
placeholder: l.namePlaceholder,
|
|
338
|
+
onChange: (e) => set(i, { ...signal, name: signalSlug(e.target.value) }),
|
|
339
|
+
className: "h-8 font-mono text-xs",
|
|
340
|
+
"data-testid": `${testId}-${i}-name`
|
|
341
|
+
}
|
|
342
|
+
),
|
|
343
|
+
/* @__PURE__ */ jsx(
|
|
344
|
+
"button",
|
|
345
|
+
{
|
|
346
|
+
type: "button",
|
|
347
|
+
"aria-label": l.remove,
|
|
348
|
+
onClick: () => emit(signals.filter((_, j) => j !== i)),
|
|
349
|
+
"data-testid": `${testId}-${i}-remove`,
|
|
350
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4 text-muted-foreground" })
|
|
351
|
+
}
|
|
352
|
+
)
|
|
353
|
+
] }),
|
|
354
|
+
/* @__PURE__ */ jsx(
|
|
355
|
+
"select",
|
|
356
|
+
{
|
|
357
|
+
"aria-label": l.when,
|
|
358
|
+
className: selectClass,
|
|
359
|
+
value: condition,
|
|
360
|
+
onChange: (e) => {
|
|
361
|
+
const nextWhen = startCondition(e.target.value, step);
|
|
362
|
+
set(i, nextWhen ? { name: signal.name, when: nextWhen } : { name: signal.name });
|
|
363
|
+
},
|
|
364
|
+
"data-testid": `${testId}-${i}-when`,
|
|
365
|
+
children: [...conditions, ...conditions.includes(condition) ? [] : [condition]].map(
|
|
366
|
+
(c) => /* @__PURE__ */ jsx("option", { value: c, children: conditionLabel[c] }, c)
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
),
|
|
370
|
+
when && "field" in when && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
371
|
+
/* @__PURE__ */ jsxs(
|
|
372
|
+
"select",
|
|
373
|
+
{
|
|
374
|
+
"aria-label": l.field,
|
|
375
|
+
className: cn(selectClass, "flex-1"),
|
|
376
|
+
value: when.field,
|
|
377
|
+
onChange: (e) => set(i, { ...signal, when: { ...when, field: e.target.value } }),
|
|
378
|
+
"data-testid": `${testId}-${i}-field`,
|
|
379
|
+
children: [
|
|
380
|
+
fields.map((f) => /* @__PURE__ */ jsx("option", { value: f.name, children: f.label }, f.name)),
|
|
381
|
+
!fields.some((f) => f.name === when.field) && /* @__PURE__ */ jsx("option", { value: when.field, children: when.field || "\u2014" })
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
),
|
|
385
|
+
/* @__PURE__ */ jsx(
|
|
386
|
+
Input,
|
|
387
|
+
{
|
|
388
|
+
"aria-label": l.value,
|
|
389
|
+
placeholder: l.value,
|
|
390
|
+
value: String(when.equals),
|
|
391
|
+
onChange: (e) => set(i, { ...signal, when: { ...when, equals: e.target.value } }),
|
|
392
|
+
className: "h-8 flex-1",
|
|
393
|
+
"data-testid": `${testId}-${i}-equals`
|
|
394
|
+
}
|
|
395
|
+
)
|
|
396
|
+
] }),
|
|
397
|
+
when && "contains" in when && /* @__PURE__ */ jsx(
|
|
398
|
+
Input,
|
|
399
|
+
{
|
|
400
|
+
"aria-label": l.phrase,
|
|
401
|
+
placeholder: l.phrasePlaceholder,
|
|
402
|
+
value: when.contains,
|
|
403
|
+
onChange: (e) => set(i, { ...signal, when: { contains: e.target.value } }),
|
|
404
|
+
className: "h-8",
|
|
405
|
+
"data-testid": `${testId}-${i}-contains`
|
|
406
|
+
}
|
|
407
|
+
),
|
|
408
|
+
when && "matches" in when && /* @__PURE__ */ jsx(
|
|
409
|
+
Input,
|
|
410
|
+
{
|
|
411
|
+
"aria-label": l.pattern,
|
|
412
|
+
placeholder: "risk rating:\\s*high",
|
|
413
|
+
value: when.matches,
|
|
414
|
+
onChange: (e) => set(i, { ...signal, when: { matches: e.target.value } }),
|
|
415
|
+
className: "h-8 font-mono text-xs",
|
|
416
|
+
"data-testid": `${testId}-${i}-matches`
|
|
417
|
+
}
|
|
418
|
+
)
|
|
419
|
+
]
|
|
420
|
+
},
|
|
421
|
+
i
|
|
422
|
+
);
|
|
423
|
+
}),
|
|
424
|
+
/* @__PURE__ */ jsxs(
|
|
425
|
+
Button,
|
|
426
|
+
{
|
|
427
|
+
variant: "outline",
|
|
428
|
+
size: "sm",
|
|
429
|
+
onClick: () => emit([
|
|
430
|
+
...signals,
|
|
431
|
+
newSignal(
|
|
432
|
+
step,
|
|
433
|
+
signals.map((s) => s.name)
|
|
434
|
+
)
|
|
435
|
+
]),
|
|
436
|
+
"data-testid": `${testId}-add`,
|
|
437
|
+
children: [
|
|
438
|
+
/* @__PURE__ */ jsx(Plus, { className: "size-4" }),
|
|
439
|
+
" ",
|
|
440
|
+
l.add
|
|
441
|
+
]
|
|
442
|
+
}
|
|
443
|
+
)
|
|
444
|
+
] }),
|
|
445
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: l.hint })
|
|
446
|
+
] });
|
|
447
|
+
}
|
|
448
|
+
var DEFAULT_STEP_ACTION_SETTINGS_LABELS = {
|
|
449
|
+
assignee: "Who does it",
|
|
450
|
+
assigneeAnyone: "Anyone on the run",
|
|
451
|
+
assigneeRunOwner: "Whoever started the run",
|
|
452
|
+
assigneeProjectLead: "The project lead",
|
|
453
|
+
assigneeFields: "The person in a form field",
|
|
454
|
+
assigneePeople: "A person",
|
|
455
|
+
remind: "Remind after (hours)",
|
|
456
|
+
remindHint: "Left that long, they are told again. Blank: no reminder.",
|
|
457
|
+
documentFrom: "Document",
|
|
458
|
+
docFromStep: "From an earlier step",
|
|
459
|
+
docFromInput: "From a run input",
|
|
460
|
+
docById: "A document",
|
|
461
|
+
chooseStep: "Choose a step\u2026",
|
|
462
|
+
chooseInput: "Choose an input\u2026",
|
|
463
|
+
chooseDocument: "Choose a document\u2026",
|
|
464
|
+
documentIdPlaceholder: "doc-\u2026",
|
|
465
|
+
signDocument: "Document to sign",
|
|
466
|
+
signers: "Signers",
|
|
467
|
+
addSigner: "Add signer",
|
|
468
|
+
signMessage: "Message to the signers",
|
|
469
|
+
expires: "Expires after (hours)",
|
|
470
|
+
expiresHint: "Unsigned by then, the step fails. Blank: no expiry.",
|
|
471
|
+
emailTo: "To",
|
|
472
|
+
emailCc: "Cc",
|
|
473
|
+
emailSubject: "Subject",
|
|
474
|
+
emailBody: "Body",
|
|
475
|
+
addressesHint: "Separate several with commas.",
|
|
476
|
+
variablesHint: "Variables (click to add to the body):",
|
|
477
|
+
attachments: "Attachments",
|
|
478
|
+
addAttachment: "Add attachment",
|
|
479
|
+
reviewColumns: "Columns (labels)",
|
|
480
|
+
noTags: "No labels in this workspace yet.",
|
|
481
|
+
reviewDocuments: "Documents",
|
|
482
|
+
reviewRunUploads: "The files this run collects",
|
|
483
|
+
reviewWorkspace: "Every document in the workspace",
|
|
484
|
+
reviewChosen: "Chosen documents",
|
|
485
|
+
addDocument: "Add document",
|
|
486
|
+
overwrite: "Fill cells that already have a value again",
|
|
487
|
+
editDocument: "Edit this document (tracked changes)",
|
|
488
|
+
editDocumentHint: "The instruction says what to change; the edited copy is saved as its next version, to accept or reject change by change."
|
|
489
|
+
};
|
|
490
|
+
var selectClass2 = "flex h-9 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
|
|
491
|
+
function Field({
|
|
492
|
+
label,
|
|
493
|
+
children,
|
|
494
|
+
hint
|
|
495
|
+
}) {
|
|
496
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
497
|
+
/* @__PURE__ */ jsx(Label, { className: "text-xs", children: label }),
|
|
498
|
+
children,
|
|
499
|
+
hint && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: hint })
|
|
500
|
+
] });
|
|
501
|
+
}
|
|
502
|
+
var assigneeValue = (a) => !a ? "" : "user" in a ? `user:${a.user}` : "input" in a ? `input:${a.input}` : "runOwner" in a ? "owner" : "lead";
|
|
503
|
+
var parseAssignee = (v) => v === "owner" ? { runOwner: true } : v === "lead" ? { projectLead: true } : v.startsWith("user:") ? { user: v.slice(5) } : v.startsWith("input:") ? { input: v.slice(6) } : void 0;
|
|
504
|
+
function AssigneeSelect({
|
|
505
|
+
value,
|
|
506
|
+
onChange,
|
|
507
|
+
ctx,
|
|
508
|
+
l,
|
|
509
|
+
anyone,
|
|
510
|
+
testId,
|
|
511
|
+
ariaLabel
|
|
512
|
+
}) {
|
|
513
|
+
const personFields = ctx.fields.filter((f) => f.type === "person");
|
|
514
|
+
const v = assigneeValue(value);
|
|
515
|
+
const known = !v.startsWith("user:") || ctx.people.some((p) => `user:${p.value}` === v) ? null : v.slice(5);
|
|
516
|
+
return /* @__PURE__ */ jsxs(
|
|
517
|
+
"select",
|
|
518
|
+
{
|
|
519
|
+
className: selectClass2,
|
|
520
|
+
value: v,
|
|
521
|
+
"aria-label": ariaLabel,
|
|
522
|
+
onChange: (e) => onChange(parseAssignee(e.target.value)),
|
|
523
|
+
"data-testid": testId,
|
|
524
|
+
children: [
|
|
525
|
+
anyone !== void 0 ? /* @__PURE__ */ jsx("option", { value: "", children: anyone }) : /* @__PURE__ */ jsx("option", { value: "", disabled: true }),
|
|
526
|
+
/* @__PURE__ */ jsx("option", { value: "owner", children: l.assigneeRunOwner }),
|
|
527
|
+
/* @__PURE__ */ jsx("option", { value: "lead", children: l.assigneeProjectLead }),
|
|
528
|
+
personFields.length > 0 && /* @__PURE__ */ jsx("optgroup", { label: l.assigneeFields, children: personFields.map((f) => /* @__PURE__ */ jsx("option", { value: `input:${f.name}`, children: f.label }, f.name)) }),
|
|
529
|
+
(ctx.people.length > 0 || known) && /* @__PURE__ */ jsxs("optgroup", { label: l.assigneePeople, children: [
|
|
530
|
+
ctx.people.map((p) => /* @__PURE__ */ jsx("option", { value: `user:${p.value}`, children: p.label }, p.value)),
|
|
531
|
+
known && /* @__PURE__ */ jsx("option", { value: v, children: known })
|
|
532
|
+
] })
|
|
533
|
+
]
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
function AssigneeSettings({
|
|
538
|
+
step,
|
|
539
|
+
patch,
|
|
540
|
+
ctx,
|
|
541
|
+
l,
|
|
542
|
+
testId
|
|
543
|
+
}) {
|
|
544
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
545
|
+
/* @__PURE__ */ jsx(Field, { label: l.assignee, children: /* @__PURE__ */ jsx(
|
|
546
|
+
AssigneeSelect,
|
|
547
|
+
{
|
|
548
|
+
value: step.assignee,
|
|
549
|
+
onChange: (assignee) => patch({ assignee }),
|
|
550
|
+
ctx,
|
|
551
|
+
l,
|
|
552
|
+
anyone: l.assigneeAnyone,
|
|
553
|
+
testId: `${testId}-assignee`
|
|
554
|
+
}
|
|
555
|
+
) }),
|
|
556
|
+
/* @__PURE__ */ jsx(Field, { label: l.remind, hint: l.remindHint, children: /* @__PURE__ */ jsx(
|
|
557
|
+
Input,
|
|
558
|
+
{
|
|
559
|
+
type: "number",
|
|
560
|
+
min: 1,
|
|
561
|
+
value: step.remindAfterHours ?? "",
|
|
562
|
+
onChange: (e) => {
|
|
563
|
+
const n = Number(e.target.value);
|
|
564
|
+
patch({ remindAfterHours: e.target.value && n > 0 ? n : void 0 });
|
|
565
|
+
},
|
|
566
|
+
className: "w-28",
|
|
567
|
+
"data-testid": `${testId}-remind`
|
|
568
|
+
}
|
|
569
|
+
) })
|
|
570
|
+
] });
|
|
571
|
+
}
|
|
572
|
+
var modeOf = (ref) => !ref ? "step" : "step" in ref ? "step" : "input" in ref ? "input" : "id";
|
|
573
|
+
function DocumentRefPicker({
|
|
574
|
+
value,
|
|
575
|
+
onChange,
|
|
576
|
+
ctx,
|
|
577
|
+
l,
|
|
578
|
+
testId
|
|
579
|
+
}) {
|
|
580
|
+
const mode = modeOf(value);
|
|
581
|
+
const inputs = ctx.fields.filter((f) => f.type === "documents" || f.type === "text");
|
|
582
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", "data-testid": testId, children: [
|
|
583
|
+
/* @__PURE__ */ jsxs(
|
|
584
|
+
"select",
|
|
585
|
+
{
|
|
586
|
+
className: cn(selectClass2, "h-8"),
|
|
587
|
+
value: mode,
|
|
588
|
+
"aria-label": l.documentFrom,
|
|
589
|
+
onChange: (e) => {
|
|
590
|
+
const m = e.target.value;
|
|
591
|
+
onChange(m === "step" ? { step: "" } : m === "input" ? { input: "" } : { id: "" });
|
|
592
|
+
},
|
|
593
|
+
"data-testid": `${testId}-mode`,
|
|
594
|
+
children: [
|
|
595
|
+
/* @__PURE__ */ jsx("option", { value: "step", children: l.docFromStep }),
|
|
596
|
+
/* @__PURE__ */ jsx("option", { value: "input", children: l.docFromInput }),
|
|
597
|
+
/* @__PURE__ */ jsx("option", { value: "id", children: l.docById })
|
|
598
|
+
]
|
|
599
|
+
}
|
|
600
|
+
),
|
|
601
|
+
mode === "step" && /* @__PURE__ */ jsxs(
|
|
602
|
+
"select",
|
|
603
|
+
{
|
|
604
|
+
className: cn(selectClass2, "h-8"),
|
|
605
|
+
value: value && "step" in value ? value.step : "",
|
|
606
|
+
onChange: (e) => onChange({ step: e.target.value }),
|
|
607
|
+
"data-testid": `${testId}-step`,
|
|
608
|
+
children: [
|
|
609
|
+
/* @__PURE__ */ jsx("option", { value: "", disabled: true, children: l.chooseStep }),
|
|
610
|
+
ctx.earlier.map((s) => /* @__PURE__ */ jsx("option", { value: s.id, children: s.title }, s.id))
|
|
611
|
+
]
|
|
612
|
+
}
|
|
613
|
+
),
|
|
614
|
+
mode === "input" && /* @__PURE__ */ jsxs(
|
|
615
|
+
"select",
|
|
616
|
+
{
|
|
617
|
+
className: cn(selectClass2, "h-8"),
|
|
618
|
+
value: value && "input" in value ? value.input : "",
|
|
619
|
+
onChange: (e) => onChange({ input: e.target.value }),
|
|
620
|
+
"data-testid": `${testId}-input`,
|
|
621
|
+
children: [
|
|
622
|
+
/* @__PURE__ */ jsx("option", { value: "", disabled: true, children: l.chooseInput }),
|
|
623
|
+
inputs.map((f) => /* @__PURE__ */ jsx("option", { value: f.name, children: f.label }, f.name))
|
|
624
|
+
]
|
|
625
|
+
}
|
|
626
|
+
),
|
|
627
|
+
mode === "id" && (ctx.documents.length ? /* @__PURE__ */ jsxs(
|
|
628
|
+
"select",
|
|
629
|
+
{
|
|
630
|
+
className: cn(selectClass2, "h-8"),
|
|
631
|
+
value: value && "id" in value ? value.id : "",
|
|
632
|
+
onChange: (e) => onChange({ id: e.target.value }),
|
|
633
|
+
"data-testid": `${testId}-id`,
|
|
634
|
+
children: [
|
|
635
|
+
/* @__PURE__ */ jsx("option", { value: "", disabled: true, children: l.chooseDocument }),
|
|
636
|
+
ctx.documents.map((d) => /* @__PURE__ */ jsx("option", { value: d.value, children: d.label }, d.value))
|
|
637
|
+
]
|
|
638
|
+
}
|
|
639
|
+
) : /* @__PURE__ */ jsx(
|
|
640
|
+
Input,
|
|
641
|
+
{
|
|
642
|
+
className: "h-8",
|
|
643
|
+
value: value && "id" in value ? value.id : "",
|
|
644
|
+
placeholder: l.documentIdPlaceholder,
|
|
645
|
+
onChange: (e) => onChange({ id: e.target.value }),
|
|
646
|
+
"data-testid": `${testId}-id`
|
|
647
|
+
}
|
|
648
|
+
))
|
|
649
|
+
] });
|
|
650
|
+
}
|
|
651
|
+
function DocumentRefList({
|
|
652
|
+
value,
|
|
653
|
+
onChange,
|
|
654
|
+
ctx,
|
|
655
|
+
l,
|
|
656
|
+
addLabel,
|
|
657
|
+
testId
|
|
658
|
+
}) {
|
|
659
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
660
|
+
value.map((ref, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1.5", children: [
|
|
661
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
|
|
662
|
+
DocumentRefPicker,
|
|
663
|
+
{
|
|
664
|
+
value: ref,
|
|
665
|
+
onChange: (next) => onChange(value.map((r, j) => j === i ? next : r)),
|
|
666
|
+
ctx,
|
|
667
|
+
l,
|
|
668
|
+
testId: `${testId}-${i}`
|
|
669
|
+
}
|
|
670
|
+
) }),
|
|
671
|
+
/* @__PURE__ */ jsx(
|
|
672
|
+
"button",
|
|
673
|
+
{
|
|
674
|
+
type: "button",
|
|
675
|
+
className: "mt-2",
|
|
676
|
+
"aria-label": `${addLabel} \xD7`,
|
|
677
|
+
onClick: () => onChange(value.filter((_, j) => j !== i)),
|
|
678
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4 text-muted-foreground" })
|
|
679
|
+
}
|
|
680
|
+
)
|
|
681
|
+
] }, i)),
|
|
682
|
+
/* @__PURE__ */ jsxs(
|
|
683
|
+
Button,
|
|
684
|
+
{
|
|
685
|
+
variant: "outline",
|
|
686
|
+
size: "sm",
|
|
687
|
+
onClick: () => onChange([...value, { step: "" }]),
|
|
688
|
+
"data-testid": `${testId}-add`,
|
|
689
|
+
children: [
|
|
690
|
+
/* @__PURE__ */ jsx(Plus, { className: "size-4" }),
|
|
691
|
+
" ",
|
|
692
|
+
addLabel
|
|
693
|
+
]
|
|
694
|
+
}
|
|
695
|
+
)
|
|
696
|
+
] });
|
|
697
|
+
}
|
|
698
|
+
var optionalNumber = (raw) => {
|
|
699
|
+
const n = Number(raw);
|
|
700
|
+
return raw && n > 0 ? n : void 0;
|
|
701
|
+
};
|
|
702
|
+
function SignSettings({
|
|
703
|
+
action,
|
|
704
|
+
onChange,
|
|
705
|
+
ctx,
|
|
706
|
+
l,
|
|
707
|
+
testId
|
|
708
|
+
}) {
|
|
709
|
+
const set = (p) => onChange({ ...action, ...p });
|
|
710
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
711
|
+
/* @__PURE__ */ jsx(Field, { label: l.signDocument, children: /* @__PURE__ */ jsx(
|
|
712
|
+
DocumentRefPicker,
|
|
713
|
+
{
|
|
714
|
+
value: action.document,
|
|
715
|
+
onChange: (document) => set({ document }),
|
|
716
|
+
ctx,
|
|
717
|
+
l,
|
|
718
|
+
testId: `${testId}-document`
|
|
719
|
+
}
|
|
720
|
+
) }),
|
|
721
|
+
/* @__PURE__ */ jsx(Field, { label: l.signers, children: /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", "data-testid": `${testId}-signers`, children: [
|
|
722
|
+
action.signers.map((s, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
723
|
+
/* @__PURE__ */ jsx(
|
|
724
|
+
AssigneeSelect,
|
|
725
|
+
{
|
|
726
|
+
value: s,
|
|
727
|
+
onChange: (next) => set({
|
|
728
|
+
signers: next ? action.signers.map((x, j) => j === i ? next : x) : action.signers.filter((_, j) => j !== i)
|
|
729
|
+
}),
|
|
730
|
+
ctx,
|
|
731
|
+
l,
|
|
732
|
+
ariaLabel: `${l.signers} ${i + 1}`,
|
|
733
|
+
testId: `${testId}-signer-${i}`
|
|
734
|
+
}
|
|
735
|
+
),
|
|
736
|
+
/* @__PURE__ */ jsx(
|
|
737
|
+
"button",
|
|
738
|
+
{
|
|
739
|
+
type: "button",
|
|
740
|
+
"aria-label": `${l.signers} ${i + 1} \xD7`,
|
|
741
|
+
onClick: () => set({ signers: action.signers.filter((_, j) => j !== i) }),
|
|
742
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4 text-muted-foreground" })
|
|
743
|
+
}
|
|
744
|
+
)
|
|
745
|
+
] }, i)),
|
|
746
|
+
/* @__PURE__ */ jsxs(
|
|
747
|
+
Button,
|
|
748
|
+
{
|
|
749
|
+
variant: "outline",
|
|
750
|
+
size: "sm",
|
|
751
|
+
onClick: () => set({ signers: [...action.signers, { runOwner: true }] }),
|
|
752
|
+
"data-testid": `${testId}-add-signer`,
|
|
753
|
+
children: [
|
|
754
|
+
/* @__PURE__ */ jsx(Plus, { className: "size-4" }),
|
|
755
|
+
" ",
|
|
756
|
+
l.addSigner
|
|
757
|
+
]
|
|
758
|
+
}
|
|
759
|
+
)
|
|
760
|
+
] }) }),
|
|
761
|
+
/* @__PURE__ */ jsx(Field, { label: l.signMessage, children: /* @__PURE__ */ jsx(
|
|
762
|
+
Textarea,
|
|
763
|
+
{
|
|
764
|
+
rows: 3,
|
|
765
|
+
value: action.message ?? "",
|
|
766
|
+
onChange: (e) => set({ message: e.target.value || void 0 }),
|
|
767
|
+
"data-testid": `${testId}-message`
|
|
768
|
+
}
|
|
769
|
+
) }),
|
|
770
|
+
/* @__PURE__ */ jsx(Field, { label: l.expires, hint: l.expiresHint, children: /* @__PURE__ */ jsx(
|
|
771
|
+
Input,
|
|
772
|
+
{
|
|
773
|
+
type: "number",
|
|
774
|
+
min: 1,
|
|
775
|
+
className: "w-28",
|
|
776
|
+
value: action.expiresAfterHours ?? "",
|
|
777
|
+
onChange: (e) => set({ expiresAfterHours: optionalNumber(e.target.value) }),
|
|
778
|
+
"data-testid": `${testId}-expires`
|
|
779
|
+
}
|
|
780
|
+
) })
|
|
781
|
+
] });
|
|
782
|
+
}
|
|
783
|
+
var addresses = (list) => (list ?? []).join(", ");
|
|
784
|
+
var parseAddresses = (text) => text.split(",").map((t) => t.trim());
|
|
785
|
+
function templateVariables(ctx) {
|
|
786
|
+
return [
|
|
787
|
+
...ctx.fields.map((f) => `{{${f.name}}}`),
|
|
788
|
+
...ctx.earlier.filter((s) => s.kind === "agent").map((s) => `{{${s.id}.answer}}`)
|
|
789
|
+
];
|
|
790
|
+
}
|
|
791
|
+
function EmailDraftSettings({
|
|
792
|
+
action,
|
|
793
|
+
onChange,
|
|
794
|
+
ctx,
|
|
795
|
+
l,
|
|
796
|
+
testId
|
|
797
|
+
}) {
|
|
798
|
+
const set = (p) => onChange({ ...action, ...p });
|
|
799
|
+
const vars = templateVariables(ctx);
|
|
800
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
801
|
+
/* @__PURE__ */ jsx(Field, { label: l.emailTo, hint: l.addressesHint, children: /* @__PURE__ */ jsx(
|
|
802
|
+
Input,
|
|
803
|
+
{
|
|
804
|
+
value: addresses(action.to),
|
|
805
|
+
onChange: (e) => set({ to: parseAddresses(e.target.value) }),
|
|
806
|
+
placeholder: "{{client_email}}",
|
|
807
|
+
"data-testid": `${testId}-to`
|
|
808
|
+
}
|
|
809
|
+
) }),
|
|
810
|
+
/* @__PURE__ */ jsx(Field, { label: l.emailCc, children: /* @__PURE__ */ jsx(
|
|
811
|
+
Input,
|
|
812
|
+
{
|
|
813
|
+
value: addresses(action.cc),
|
|
814
|
+
onChange: (e) => {
|
|
815
|
+
const cc = parseAddresses(e.target.value);
|
|
816
|
+
set({ cc: cc.some(Boolean) ? cc : void 0 });
|
|
817
|
+
},
|
|
818
|
+
"data-testid": `${testId}-cc`
|
|
819
|
+
}
|
|
820
|
+
) }),
|
|
821
|
+
/* @__PURE__ */ jsx(Field, { label: l.emailSubject, children: /* @__PURE__ */ jsx(
|
|
822
|
+
Input,
|
|
823
|
+
{
|
|
824
|
+
value: action.subject,
|
|
825
|
+
onChange: (e) => set({ subject: e.target.value }),
|
|
826
|
+
"data-testid": `${testId}-subject`
|
|
827
|
+
}
|
|
828
|
+
) }),
|
|
829
|
+
/* @__PURE__ */ jsx(Field, { label: l.emailBody, children: /* @__PURE__ */ jsx(
|
|
830
|
+
Textarea,
|
|
831
|
+
{
|
|
832
|
+
rows: 6,
|
|
833
|
+
value: action.body,
|
|
834
|
+
onChange: (e) => set({ body: e.target.value }),
|
|
835
|
+
"data-testid": `${testId}-body`
|
|
836
|
+
}
|
|
837
|
+
) }),
|
|
838
|
+
vars.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1", "data-testid": `${testId}-variables`, children: [
|
|
839
|
+
/* @__PURE__ */ jsx("span", { className: "w-full text-xs text-muted-foreground", children: l.variablesHint }),
|
|
840
|
+
vars.map((v) => /* @__PURE__ */ jsx(
|
|
841
|
+
"button",
|
|
842
|
+
{
|
|
843
|
+
type: "button",
|
|
844
|
+
onClick: () => set({ body: `${action.body}${action.body ? " " : ""}${v}` }),
|
|
845
|
+
className: "rounded bg-muted px-1.5 py-0.5 font-mono text-[11px] text-muted-foreground hover:text-foreground",
|
|
846
|
+
title: v,
|
|
847
|
+
children: v
|
|
848
|
+
},
|
|
849
|
+
v
|
|
850
|
+
))
|
|
851
|
+
] }),
|
|
852
|
+
/* @__PURE__ */ jsx(Field, { label: l.attachments, children: /* @__PURE__ */ jsx(
|
|
853
|
+
DocumentRefList,
|
|
854
|
+
{
|
|
855
|
+
value: action.attachments ?? [],
|
|
856
|
+
onChange: (attachments) => set({ attachments: attachments.length ? attachments : void 0 }),
|
|
857
|
+
ctx,
|
|
858
|
+
l,
|
|
859
|
+
addLabel: l.addAttachment,
|
|
860
|
+
testId: `${testId}-attachments`
|
|
861
|
+
}
|
|
862
|
+
) })
|
|
863
|
+
] });
|
|
864
|
+
}
|
|
865
|
+
function TabularReviewSettings({
|
|
866
|
+
action,
|
|
867
|
+
onChange,
|
|
868
|
+
ctx,
|
|
869
|
+
l,
|
|
870
|
+
testId
|
|
871
|
+
}) {
|
|
872
|
+
const set = (p) => onChange({ ...action, ...p });
|
|
873
|
+
const mode = Array.isArray(action.documents) ? "chosen" : action.documents;
|
|
874
|
+
const toggle = (id) => set({
|
|
875
|
+
tags: action.tags.includes(id) ? action.tags.filter((t) => t !== id) : [...action.tags, id]
|
|
876
|
+
});
|
|
877
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
878
|
+
/* @__PURE__ */ jsx(Field, { label: l.reviewColumns, children: /* @__PURE__ */ jsxs(
|
|
879
|
+
"div",
|
|
880
|
+
{
|
|
881
|
+
className: "max-h-44 space-y-1 overflow-y-auto rounded-md border border-border p-2",
|
|
882
|
+
"data-testid": `${testId}-tags`,
|
|
883
|
+
children: [
|
|
884
|
+
ctx.tags.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: l.noTags }),
|
|
885
|
+
ctx.tags.map((t) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
|
|
886
|
+
/* @__PURE__ */ jsx(
|
|
887
|
+
Checkbox,
|
|
888
|
+
{
|
|
889
|
+
checked: action.tags.includes(t.value),
|
|
890
|
+
onCheckedChange: () => toggle(t.value),
|
|
891
|
+
"data-testid": `${testId}-tag-${t.value}`
|
|
892
|
+
}
|
|
893
|
+
),
|
|
894
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: t.label })
|
|
895
|
+
] }, t.value)),
|
|
896
|
+
action.tags.filter((t) => !ctx.tags.some((x) => x.value === t)).map((t) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
897
|
+
/* @__PURE__ */ jsx(Checkbox, { checked: true, onCheckedChange: () => toggle(t) }),
|
|
898
|
+
/* @__PURE__ */ jsx("code", { className: "text-xs", children: t })
|
|
899
|
+
] }, t))
|
|
900
|
+
]
|
|
901
|
+
}
|
|
902
|
+
) }),
|
|
903
|
+
/* @__PURE__ */ jsx(Field, { label: l.reviewDocuments, children: /* @__PURE__ */ jsxs(
|
|
904
|
+
"select",
|
|
905
|
+
{
|
|
906
|
+
className: selectClass2,
|
|
907
|
+
value: mode,
|
|
908
|
+
onChange: (e) => {
|
|
909
|
+
const v = e.target.value;
|
|
910
|
+
set({ documents: v === "chosen" ? [] : v });
|
|
911
|
+
},
|
|
912
|
+
"data-testid": `${testId}-documents`,
|
|
913
|
+
children: [
|
|
914
|
+
/* @__PURE__ */ jsx("option", { value: "run_uploads", children: l.reviewRunUploads }),
|
|
915
|
+
/* @__PURE__ */ jsx("option", { value: "workspace", children: l.reviewWorkspace }),
|
|
916
|
+
/* @__PURE__ */ jsx("option", { value: "chosen", children: l.reviewChosen })
|
|
917
|
+
]
|
|
918
|
+
}
|
|
919
|
+
) }),
|
|
920
|
+
Array.isArray(action.documents) && /* @__PURE__ */ jsx(
|
|
921
|
+
DocumentRefList,
|
|
922
|
+
{
|
|
923
|
+
value: action.documents,
|
|
924
|
+
onChange: (documents) => set({ documents }),
|
|
925
|
+
ctx,
|
|
926
|
+
l,
|
|
927
|
+
addLabel: l.addDocument,
|
|
928
|
+
testId: `${testId}-chosen`
|
|
929
|
+
}
|
|
930
|
+
),
|
|
931
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
|
|
932
|
+
/* @__PURE__ */ jsx(
|
|
933
|
+
Checkbox,
|
|
934
|
+
{
|
|
935
|
+
checked: !!action.overwrite,
|
|
936
|
+
onCheckedChange: (v) => set({ overwrite: v === true || void 0 })
|
|
937
|
+
}
|
|
938
|
+
),
|
|
939
|
+
l.overwrite
|
|
940
|
+
] })
|
|
941
|
+
] });
|
|
942
|
+
}
|
|
943
|
+
function EditDocumentSettings({
|
|
944
|
+
step,
|
|
945
|
+
patch,
|
|
946
|
+
ctx,
|
|
947
|
+
l,
|
|
948
|
+
testId
|
|
949
|
+
}) {
|
|
950
|
+
const on = !!step.editDocument;
|
|
951
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
952
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-start gap-2 text-sm", children: [
|
|
953
|
+
/* @__PURE__ */ jsx(
|
|
954
|
+
Checkbox,
|
|
955
|
+
{
|
|
956
|
+
checked: on,
|
|
957
|
+
disabled: !!step.branch,
|
|
958
|
+
onCheckedChange: (v) => patch({ editDocument: v === true ? { step: "" } : void 0 }),
|
|
959
|
+
"data-testid": `${testId}-edit-document`
|
|
960
|
+
}
|
|
961
|
+
),
|
|
962
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
963
|
+
l.editDocument,
|
|
964
|
+
/* @__PURE__ */ jsx("span", { className: "block text-xs text-muted-foreground", children: l.editDocumentHint })
|
|
965
|
+
] })
|
|
966
|
+
] }),
|
|
967
|
+
on && /* @__PURE__ */ jsx(
|
|
968
|
+
DocumentRefPicker,
|
|
969
|
+
{
|
|
970
|
+
value: step.editDocument,
|
|
971
|
+
onChange: (editDocument) => patch({ editDocument }),
|
|
972
|
+
ctx,
|
|
973
|
+
l,
|
|
974
|
+
testId: `${testId}-edit-document-ref`
|
|
975
|
+
}
|
|
976
|
+
)
|
|
977
|
+
] });
|
|
978
|
+
}
|
|
168
979
|
var DEFAULT_STEP_INSPECTOR_LABELS = {
|
|
169
980
|
workflow: "Workflow",
|
|
170
981
|
title: "Title",
|
|
@@ -194,17 +1005,19 @@ var DEFAULT_STEP_INSPECTOR_LABELS = {
|
|
|
194
1005
|
hint: "Hint",
|
|
195
1006
|
instructions: "What to review",
|
|
196
1007
|
instructionsHint: "Shown on the review task; {{field}} and {{steps.<id>}} work here.",
|
|
1008
|
+
reviewFields: "Filled in when approving",
|
|
1009
|
+
reviewFieldsHint: "E.g. a rating or a comment; the answers join the run\u2019s inputs.",
|
|
197
1010
|
deleteStep: "Delete step"
|
|
198
1011
|
};
|
|
199
|
-
var FIELD_TYPES = [
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
1012
|
+
var FIELD_TYPES = [
|
|
1013
|
+
"text",
|
|
1014
|
+
"longtext",
|
|
1015
|
+
"boolean",
|
|
1016
|
+
"number",
|
|
1017
|
+
"date",
|
|
1018
|
+
"scope",
|
|
1019
|
+
"person"
|
|
1020
|
+
];
|
|
208
1021
|
var fieldName = (label, taken) => {
|
|
209
1022
|
const base = label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").replace(/^[^a-z]+/, "") || "field";
|
|
210
1023
|
let name = base;
|
|
@@ -218,7 +1031,12 @@ function StepInspector({
|
|
|
218
1031
|
onChange,
|
|
219
1032
|
onSelect,
|
|
220
1033
|
workflows = [],
|
|
1034
|
+
people = [],
|
|
1035
|
+
tags = [],
|
|
1036
|
+
documents = [],
|
|
221
1037
|
labels,
|
|
1038
|
+
actionLabels,
|
|
1039
|
+
signalLabels,
|
|
222
1040
|
testId = "step-inspector"
|
|
223
1041
|
}) {
|
|
224
1042
|
const l = { ...DEFAULT_STEP_INSPECTOR_LABELS, ...labels };
|
|
@@ -247,6 +1065,30 @@ function StepInspector({
|
|
|
247
1065
|
] });
|
|
248
1066
|
}
|
|
249
1067
|
const patch = (p) => onChange(updateStep(workflow, step.id, p));
|
|
1068
|
+
const al = { ...DEFAULT_STEP_ACTION_SETTINGS_LABELS, ...actionLabels };
|
|
1069
|
+
const ctx = {
|
|
1070
|
+
earlier: upstreamSteps(steps, step.id),
|
|
1071
|
+
fields: runFields(workflow),
|
|
1072
|
+
people,
|
|
1073
|
+
tags,
|
|
1074
|
+
documents
|
|
1075
|
+
};
|
|
1076
|
+
const takenBy = (id) => steps.flatMap(
|
|
1077
|
+
(s) => s.id === id ? [] : s.kind === "form" ? s.inputs : s.kind === "review" ? s.fields ?? [] : []
|
|
1078
|
+
).map((f) => f.name);
|
|
1079
|
+
const setFields = (before, after, key) => {
|
|
1080
|
+
const renamed = before.flatMap(
|
|
1081
|
+
(f, i) => after[i] && after[i].name !== f.name ? [[f.name, after[i].name]] : []
|
|
1082
|
+
);
|
|
1083
|
+
onChange(
|
|
1084
|
+
renamed.reduce(
|
|
1085
|
+
(w, [from, to]) => renameInput(w, from, to),
|
|
1086
|
+
updateStep(workflow, step.id, {
|
|
1087
|
+
[key]: key === "fields" && !after.length ? void 0 : after
|
|
1088
|
+
})
|
|
1089
|
+
)
|
|
1090
|
+
);
|
|
1091
|
+
};
|
|
250
1092
|
const deps = stepDependencies(steps, step.id);
|
|
251
1093
|
const titleOf = (id) => steps.find((s) => s.id === id)?.title ?? id;
|
|
252
1094
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", "data-testid": `${testId}-step`, children: [
|
|
@@ -296,18 +1138,8 @@ function StepInspector({
|
|
|
296
1138
|
FormFields,
|
|
297
1139
|
{
|
|
298
1140
|
fields: step.inputs,
|
|
299
|
-
taken:
|
|
300
|
-
onChange: (inputs) =>
|
|
301
|
-
const renamed = step.inputs.flatMap(
|
|
302
|
-
(f, i) => inputs[i] && inputs[i].name !== f.name ? [[f.name, inputs[i].name]] : []
|
|
303
|
-
);
|
|
304
|
-
onChange(
|
|
305
|
-
renamed.reduce(
|
|
306
|
-
(w, [from, to]) => renameInput(w, from, to),
|
|
307
|
-
updateStep(workflow, step.id, { inputs })
|
|
308
|
-
)
|
|
309
|
-
);
|
|
310
|
-
},
|
|
1141
|
+
taken: takenBy(step.id),
|
|
1142
|
+
onChange: (inputs) => setFields(step.inputs, inputs, "inputs"),
|
|
311
1143
|
l,
|
|
312
1144
|
testId
|
|
313
1145
|
}
|
|
@@ -326,31 +1158,87 @@ function StepInspector({
|
|
|
326
1158
|
/* @__PURE__ */ jsx(Field, { label: l.hint, children: /* @__PURE__ */ jsx(
|
|
327
1159
|
Input,
|
|
328
1160
|
{
|
|
329
|
-
value: step.hint ?? "",
|
|
330
|
-
onChange: (e) => patch({ hint: e.target.value || void 0 })
|
|
1161
|
+
value: step.hint ?? "",
|
|
1162
|
+
onChange: (e) => patch({ hint: e.target.value || void 0 })
|
|
1163
|
+
}
|
|
1164
|
+
) })
|
|
1165
|
+
] }),
|
|
1166
|
+
step.kind === "agent" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1167
|
+
/* @__PURE__ */ jsx(
|
|
1168
|
+
AgentSettings,
|
|
1169
|
+
{
|
|
1170
|
+
step,
|
|
1171
|
+
workflows,
|
|
1172
|
+
fields: runFields(workflow).map((f) => f.name),
|
|
1173
|
+
patch,
|
|
1174
|
+
l,
|
|
1175
|
+
testId
|
|
1176
|
+
}
|
|
1177
|
+
),
|
|
1178
|
+
/* @__PURE__ */ jsx(EditDocumentSettings, { step, patch, ctx, l: al, testId })
|
|
1179
|
+
] }),
|
|
1180
|
+
step.kind === "review" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1181
|
+
/* @__PURE__ */ jsx(Field, { label: l.instructions, hint: l.instructionsHint, children: /* @__PURE__ */ jsx(
|
|
1182
|
+
Textarea,
|
|
1183
|
+
{
|
|
1184
|
+
value: step.instructions ?? "",
|
|
1185
|
+
rows: 4,
|
|
1186
|
+
onChange: (e) => patch({ instructions: e.target.value || void 0 }),
|
|
1187
|
+
"data-testid": `${testId}-instructions`
|
|
1188
|
+
}
|
|
1189
|
+
) }),
|
|
1190
|
+
/* @__PURE__ */ jsx(
|
|
1191
|
+
FormFields,
|
|
1192
|
+
{
|
|
1193
|
+
fields: step.fields ?? [],
|
|
1194
|
+
taken: takenBy(step.id),
|
|
1195
|
+
onChange: (fields) => setFields(step.fields ?? [], fields, "fields"),
|
|
1196
|
+
l: { ...l, fields: l.reviewFields },
|
|
1197
|
+
hint: l.reviewFieldsHint,
|
|
1198
|
+
testId: `${testId}-review`
|
|
331
1199
|
}
|
|
332
|
-
)
|
|
1200
|
+
)
|
|
333
1201
|
] }),
|
|
334
|
-
step.kind === "
|
|
335
|
-
|
|
1202
|
+
step.kind === "action" && step.action.type === "sign" && /* @__PURE__ */ jsx(
|
|
1203
|
+
SignSettings,
|
|
336
1204
|
{
|
|
337
|
-
step,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
testId
|
|
1205
|
+
action: step.action,
|
|
1206
|
+
onChange: (action) => patch({ action }),
|
|
1207
|
+
ctx,
|
|
1208
|
+
l: al,
|
|
1209
|
+
testId: `${testId}-sign`
|
|
343
1210
|
}
|
|
344
1211
|
),
|
|
345
|
-
step.kind === "
|
|
346
|
-
|
|
1212
|
+
step.kind === "action" && step.action.type === "email_draft" && /* @__PURE__ */ jsx(
|
|
1213
|
+
EmailDraftSettings,
|
|
347
1214
|
{
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
1215
|
+
action: step.action,
|
|
1216
|
+
onChange: (action) => patch({ action }),
|
|
1217
|
+
ctx,
|
|
1218
|
+
l: al,
|
|
1219
|
+
testId: `${testId}-email`
|
|
352
1220
|
}
|
|
353
|
-
)
|
|
1221
|
+
),
|
|
1222
|
+
step.kind === "action" && step.action.type === "tabular_review" && /* @__PURE__ */ jsx(
|
|
1223
|
+
TabularReviewSettings,
|
|
1224
|
+
{
|
|
1225
|
+
action: step.action,
|
|
1226
|
+
onChange: (action) => patch({ action }),
|
|
1227
|
+
ctx,
|
|
1228
|
+
l: al,
|
|
1229
|
+
testId: `${testId}-review-grid`
|
|
1230
|
+
}
|
|
1231
|
+
),
|
|
1232
|
+
canAssign(step) && /* @__PURE__ */ jsx(AssigneeSettings, { step, patch, ctx, l: al, testId }),
|
|
1233
|
+
/* @__PURE__ */ jsx(
|
|
1234
|
+
SignalsEditor,
|
|
1235
|
+
{
|
|
1236
|
+
step,
|
|
1237
|
+
onChange: (signals) => patch({ signals }),
|
|
1238
|
+
labels: signalLabels,
|
|
1239
|
+
testId: `${testId}-signals`
|
|
1240
|
+
}
|
|
1241
|
+
),
|
|
354
1242
|
/* @__PURE__ */ jsxs(
|
|
355
1243
|
Button,
|
|
356
1244
|
{
|
|
@@ -376,10 +1264,11 @@ function FormFields({
|
|
|
376
1264
|
taken,
|
|
377
1265
|
onChange,
|
|
378
1266
|
l,
|
|
1267
|
+
hint,
|
|
379
1268
|
testId
|
|
380
1269
|
}) {
|
|
381
1270
|
const set = (i, p) => onChange(fields.map((f, j) => j === i ? { ...f, ...p } : f));
|
|
382
|
-
return /* @__PURE__ */ jsx(Field, { label: l.fields, children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", "data-testid": `${testId}-fields`, children: [
|
|
1271
|
+
return /* @__PURE__ */ jsx(Field, { label: l.fields, hint, children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", "data-testid": `${testId}-fields`, children: [
|
|
383
1272
|
fields.map((f, i) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 rounded-md border border-border p-2", children: [
|
|
384
1273
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
385
1274
|
/* @__PURE__ */ jsx(
|
|
@@ -412,10 +1301,12 @@ function FormFields({
|
|
|
412
1301
|
"select",
|
|
413
1302
|
{
|
|
414
1303
|
"aria-label": l.fieldType,
|
|
415
|
-
className: cn(
|
|
1304
|
+
className: cn(selectClass2, "h-8"),
|
|
416
1305
|
value: f.type,
|
|
417
1306
|
onChange: (e) => set(i, { type: e.target.value }),
|
|
418
|
-
children: FIELD_TYPES.
|
|
1307
|
+
children: (FIELD_TYPES.includes(f.type) ? FIELD_TYPES : [...FIELD_TYPES, f.type]).map(
|
|
1308
|
+
(t) => /* @__PURE__ */ jsx("option", { value: t, children: t }, t)
|
|
1309
|
+
)
|
|
419
1310
|
}
|
|
420
1311
|
),
|
|
421
1312
|
/* @__PURE__ */ jsxs("label", { className: "flex shrink-0 items-center gap-1.5 text-xs", children: [
|
|
@@ -479,7 +1370,7 @@ function AgentSettings({
|
|
|
479
1370
|
/* @__PURE__ */ jsx(Field, { label: l.runs, children: /* @__PURE__ */ jsxs(
|
|
480
1371
|
"select",
|
|
481
1372
|
{
|
|
482
|
-
className:
|
|
1373
|
+
className: selectClass2,
|
|
483
1374
|
value,
|
|
484
1375
|
onChange: (e) => {
|
|
485
1376
|
const id = e.target.value;
|
|
@@ -536,7 +1427,7 @@ function AgentSettings({
|
|
|
536
1427
|
/* @__PURE__ */ jsx(Field, { label: l.thread, children: /* @__PURE__ */ jsxs(
|
|
537
1428
|
"select",
|
|
538
1429
|
{
|
|
539
|
-
className:
|
|
1430
|
+
className: selectClass2,
|
|
540
1431
|
value: step.branch ? "branch" : "main",
|
|
541
1432
|
onChange: (e) => patch({ branch: e.target.value === "branch" || void 0 }),
|
|
542
1433
|
"data-testid": `${testId}-thread`,
|
|
@@ -549,7 +1440,7 @@ function AgentSettings({
|
|
|
549
1440
|
/* @__PURE__ */ jsx(Field, { label: l.output, children: /* @__PURE__ */ jsxs(
|
|
550
1441
|
"select",
|
|
551
1442
|
{
|
|
552
|
-
className:
|
|
1443
|
+
className: selectClass2,
|
|
553
1444
|
value: step.output ?? "answer",
|
|
554
1445
|
onChange: (e) => patch({ output: e.target.value === "artifact" ? "artifact" : void 0 }),
|
|
555
1446
|
children: [
|
|
@@ -560,6 +1451,138 @@ function AgentSettings({
|
|
|
560
1451
|
) })
|
|
561
1452
|
] });
|
|
562
1453
|
}
|
|
1454
|
+
|
|
1455
|
+
// src/workflow-canvas/trigger.ts
|
|
1456
|
+
var WORKFLOW_SIGNAL_EVENT = "workflow_signal";
|
|
1457
|
+
var DEFAULT_WORKFLOW_TRIGGER = {
|
|
1458
|
+
start: "manual",
|
|
1459
|
+
runAt: "",
|
|
1460
|
+
rrule: "",
|
|
1461
|
+
timezone: typeof Intl !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC",
|
|
1462
|
+
eventKind: "",
|
|
1463
|
+
signal: "",
|
|
1464
|
+
afterTaskId: "",
|
|
1465
|
+
agentId: "",
|
|
1466
|
+
access: "ask_each_run"
|
|
1467
|
+
};
|
|
1468
|
+
function triggerProblem(trigger) {
|
|
1469
|
+
if (trigger.start === "once" && !trigger.runAt) return "runAt";
|
|
1470
|
+
if (trigger.start === "repeat" && !trigger.rrule.trim()) return "rrule";
|
|
1471
|
+
if (trigger.start === "event" && !trigger.eventKind.trim()) return "event";
|
|
1472
|
+
if (isSignalTrigger(trigger) && !trigger.signal.trim()) return "signal";
|
|
1473
|
+
if (trigger.start === "task" && !trigger.afterTaskId) return "task";
|
|
1474
|
+
return null;
|
|
1475
|
+
}
|
|
1476
|
+
var isSignalTrigger = (trigger) => trigger.start === "event" && trigger.eventKind.trim() === WORKFLOW_SIGNAL_EVENT;
|
|
1477
|
+
function eventFilter(trigger) {
|
|
1478
|
+
if (trigger.start !== "event") return null;
|
|
1479
|
+
if (isSignalTrigger(trigger)) return { signal: trigger.signal.trim() };
|
|
1480
|
+
return trigger.eventFilter && Object.keys(trigger.eventFilter).length ? trigger.eventFilter : null;
|
|
1481
|
+
}
|
|
1482
|
+
function triggerFields(trigger, clearOthers) {
|
|
1483
|
+
const none = clearOthers ? null : void 0;
|
|
1484
|
+
const filter = eventFilter(trigger);
|
|
1485
|
+
return {
|
|
1486
|
+
timezone: trigger.timezone,
|
|
1487
|
+
rrule: trigger.start === "repeat" ? trigger.rrule.trim() : none,
|
|
1488
|
+
run_at: trigger.start === "once" ? localInputToIso(trigger.runAt) : none,
|
|
1489
|
+
event_kind: trigger.start === "event" ? trigger.eventKind.trim() : none,
|
|
1490
|
+
// A step signal is `workflow_signal` narrowed to one signal name.
|
|
1491
|
+
event_filter: filter ?? none,
|
|
1492
|
+
after_conversation_ext_id: trigger.start === "task" ? trigger.afterTaskId : none,
|
|
1493
|
+
on_demand: trigger.start === "manual"
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
var authMode = (trigger) => trigger.start === "manual" ? "run_now_temp" : trigger.access === "standing" ? "scheduled_permanent" : "scheduled_require_auth";
|
|
1497
|
+
var withoutUndefined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
1498
|
+
function plan(value, options) {
|
|
1499
|
+
return {
|
|
1500
|
+
goal: value.workflow.description,
|
|
1501
|
+
criteria: [],
|
|
1502
|
+
max_passes: 3,
|
|
1503
|
+
documents: [],
|
|
1504
|
+
steps: toPlanSteps(value.workflow, options)
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
function builderToWorkflowRequest(value, workspaceId, options = {}) {
|
|
1508
|
+
return withoutUndefined({
|
|
1509
|
+
workspace_ext_id: workspaceId,
|
|
1510
|
+
name: value.workflow.title,
|
|
1511
|
+
description: value.workflow.description,
|
|
1512
|
+
active: true,
|
|
1513
|
+
shared: true,
|
|
1514
|
+
...triggerFields(value.trigger, false),
|
|
1515
|
+
prompt: "",
|
|
1516
|
+
skill_ref: null,
|
|
1517
|
+
skill_inputs: null,
|
|
1518
|
+
agent_ext_id: value.trigger.agentId || null,
|
|
1519
|
+
auth_mode: authMode(value.trigger),
|
|
1520
|
+
plan: plan(value, options)
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
function builderToWorkflowUpdate(value, options = {}) {
|
|
1524
|
+
return withoutUndefined({
|
|
1525
|
+
name: value.workflow.title,
|
|
1526
|
+
description: value.workflow.description,
|
|
1527
|
+
...triggerFields(value.trigger, true),
|
|
1528
|
+
// The steps are what runs (a classic automation's single prompt or skill becomes its one step).
|
|
1529
|
+
prompt: "",
|
|
1530
|
+
skill_ref: null,
|
|
1531
|
+
skill_inputs: null,
|
|
1532
|
+
agent_ext_id: value.trigger.agentId || null,
|
|
1533
|
+
auth_mode: authMode(value.trigger),
|
|
1534
|
+
plan: plan(value, options)
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
function workflowRecordToBuilder(record) {
|
|
1538
|
+
const planned = record.plan?.steps ?? [];
|
|
1539
|
+
const steps = planned.length ? fromPlanSteps(planned) : record.prompt || record.skill_ref ? [
|
|
1540
|
+
{
|
|
1541
|
+
id: "work",
|
|
1542
|
+
kind: "agent",
|
|
1543
|
+
title: record.name,
|
|
1544
|
+
...record.skill_ref ? { workflow: record.skill_ref } : { run: { kind: "prompt", promptTemplate: record.prompt } }
|
|
1545
|
+
}
|
|
1546
|
+
] : [];
|
|
1547
|
+
const when = workflowWhen(record);
|
|
1548
|
+
return {
|
|
1549
|
+
workflow: {
|
|
1550
|
+
id: record.external_id,
|
|
1551
|
+
title: record.name,
|
|
1552
|
+
description: record.description ?? "",
|
|
1553
|
+
inputs: [],
|
|
1554
|
+
landing: "chat",
|
|
1555
|
+
schedulable: false,
|
|
1556
|
+
run: { kind: "steps", steps, layout: layoutFromPlanSteps(planned ?? []) }
|
|
1557
|
+
},
|
|
1558
|
+
trigger: {
|
|
1559
|
+
...DEFAULT_WORKFLOW_TRIGGER,
|
|
1560
|
+
start: when === "now" ? "manual" : when,
|
|
1561
|
+
runAt: isoToLocalInput(record.run_at),
|
|
1562
|
+
rrule: record.rrule ?? "",
|
|
1563
|
+
timezone: record.timezone || DEFAULT_WORKFLOW_TRIGGER.timezone,
|
|
1564
|
+
eventKind: record.event_kind ?? "",
|
|
1565
|
+
signal: record.event_kind === WORKFLOW_SIGNAL_EVENT && typeof record.event_filter?.signal === "string" ? record.event_filter.signal : "",
|
|
1566
|
+
eventFilter: record.event_kind && record.event_kind !== WORKFLOW_SIGNAL_EVENT && record.event_filter ? record.event_filter : void 0,
|
|
1567
|
+
afterTaskId: record.after_conversation_ext_id ?? "",
|
|
1568
|
+
agentId: record.agent_ext_id ?? "",
|
|
1569
|
+
access: record.auth_mode === "scheduled_permanent" ? "standing" : "ask_each_run"
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
function declaredSignals(records, current) {
|
|
1574
|
+
const out = /* @__PURE__ */ new Map();
|
|
1575
|
+
const add = (workflow, steps) => {
|
|
1576
|
+
for (const step of steps)
|
|
1577
|
+
for (const signal of step.signals ?? [])
|
|
1578
|
+
if (signal.name && !out.has(signal.name))
|
|
1579
|
+
out.set(signal.name, { value: signal.name, label: `${workflow} \u203A ${step.title}` });
|
|
1580
|
+
};
|
|
1581
|
+
for (const record of records)
|
|
1582
|
+
add(record.name, record.plan?.steps ?? []);
|
|
1583
|
+
if (current?.run.kind === "steps") add(current.title, current.run.steps);
|
|
1584
|
+
return [...out.values()];
|
|
1585
|
+
}
|
|
563
1586
|
var DEFAULT_TRIGGER_INSPECTOR_LABELS = {
|
|
564
1587
|
heading: "Start",
|
|
565
1588
|
start: "Starts",
|
|
@@ -573,6 +1596,11 @@ var DEFAULT_TRIGGER_INSPECTOR_LABELS = {
|
|
|
573
1596
|
runAt: "When",
|
|
574
1597
|
event: "Event",
|
|
575
1598
|
eventPlaceholder: "Pick an event",
|
|
1599
|
+
stepSignals: "When a step signals",
|
|
1600
|
+
signal: "Signal",
|
|
1601
|
+
signalPlaceholder: "e.g. risk-high",
|
|
1602
|
+
signalHint: "Starts as its own thread, linked to the thread whose step raised it, with that run\u2019s inputs.",
|
|
1603
|
+
signalSummary: "When a step signals \u201C{signal}\u201D",
|
|
576
1604
|
afterTask: "After this task",
|
|
577
1605
|
afterTaskPlaceholder: "Pick a task",
|
|
578
1606
|
agent: "Runs as",
|
|
@@ -588,6 +1616,8 @@ function triggerSummary(trigger, options = {}) {
|
|
|
588
1616
|
case "repeat":
|
|
589
1617
|
return trigger.rrule ? describeRRule(trigger.rrule) : l.starts.repeat;
|
|
590
1618
|
case "event":
|
|
1619
|
+
if (isSignalTrigger(trigger))
|
|
1620
|
+
return trigger.signal ? l.signalSummary.replace("{signal}", trigger.signal) : l.stepSignals;
|
|
591
1621
|
return trigger.eventKind && options.eventLabel?.(trigger.eventKind) || trigger.eventKind || l.starts.event;
|
|
592
1622
|
case "task":
|
|
593
1623
|
return trigger.afterTaskId && options.taskLabel?.(trigger.afterTaskId) || l.starts.task;
|
|
@@ -595,7 +1625,7 @@ function triggerSummary(trigger, options = {}) {
|
|
|
595
1625
|
return l.starts.manual;
|
|
596
1626
|
}
|
|
597
1627
|
}
|
|
598
|
-
var
|
|
1628
|
+
var selectClass3 = "flex h-9 w-full rounded-md border border-input bg-background px-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
|
|
599
1629
|
var STARTS = ["manual", "once", "repeat", "event", "task"];
|
|
600
1630
|
function TriggerInspector({
|
|
601
1631
|
trigger,
|
|
@@ -603,11 +1633,13 @@ function TriggerInspector({
|
|
|
603
1633
|
agents = [],
|
|
604
1634
|
eventKinds = [],
|
|
605
1635
|
tasks = [],
|
|
1636
|
+
signals = [],
|
|
606
1637
|
labels,
|
|
607
1638
|
testId = "trigger-inspector"
|
|
608
1639
|
}) {
|
|
609
1640
|
const l = { ...DEFAULT_TRIGGER_INSPECTOR_LABELS, ...labels };
|
|
610
1641
|
const set = (patch) => onChange({ ...trigger, ...patch });
|
|
1642
|
+
const events = eventKinds.some((k) => k.value === WORKFLOW_SIGNAL_EVENT) ? eventKinds : [...eventKinds, { value: WORKFLOW_SIGNAL_EVENT, label: l.stepSignals }];
|
|
611
1643
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", "data-testid": testId, children: [
|
|
612
1644
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: l.heading }),
|
|
613
1645
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
@@ -647,23 +1679,39 @@ function TriggerInspector({
|
|
|
647
1679
|
/* @__PURE__ */ jsxs(
|
|
648
1680
|
"select",
|
|
649
1681
|
{
|
|
650
|
-
className:
|
|
1682
|
+
className: selectClass3,
|
|
651
1683
|
value: trigger.eventKind,
|
|
652
|
-
onChange: (e) => set({ eventKind: e.target.value }),
|
|
1684
|
+
onChange: (e) => set({ eventKind: e.target.value, eventFilter: void 0 }),
|
|
653
1685
|
"data-testid": `${testId}-event`,
|
|
654
1686
|
children: [
|
|
655
1687
|
/* @__PURE__ */ jsx("option", { value: "", children: l.eventPlaceholder }),
|
|
656
|
-
|
|
1688
|
+
events.map((k) => /* @__PURE__ */ jsx("option", { value: k.value, children: k.label }, k.value))
|
|
657
1689
|
]
|
|
658
1690
|
}
|
|
659
1691
|
)
|
|
660
1692
|
] }),
|
|
1693
|
+
isSignalTrigger(trigger) && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1694
|
+
/* @__PURE__ */ jsx(Label, { className: "text-xs", htmlFor: `${testId}-signal`, children: l.signal }),
|
|
1695
|
+
/* @__PURE__ */ jsx(
|
|
1696
|
+
Input,
|
|
1697
|
+
{
|
|
1698
|
+
id: `${testId}-signal`,
|
|
1699
|
+
list: `${testId}-signal-options`,
|
|
1700
|
+
value: trigger.signal,
|
|
1701
|
+
placeholder: l.signalPlaceholder,
|
|
1702
|
+
onChange: (e) => set({ signal: e.target.value.trim().toLowerCase() }),
|
|
1703
|
+
"data-testid": `${testId}-signal`
|
|
1704
|
+
}
|
|
1705
|
+
),
|
|
1706
|
+
/* @__PURE__ */ jsx("datalist", { id: `${testId}-signal-options`, children: signals.map((s) => /* @__PURE__ */ jsx("option", { value: s.value, children: s.label }, s.value)) }),
|
|
1707
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: l.signalHint })
|
|
1708
|
+
] }),
|
|
661
1709
|
trigger.start === "task" && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
662
1710
|
/* @__PURE__ */ jsx(Label, { className: "text-xs", children: l.afterTask }),
|
|
663
1711
|
/* @__PURE__ */ jsxs(
|
|
664
1712
|
"select",
|
|
665
1713
|
{
|
|
666
|
-
className:
|
|
1714
|
+
className: selectClass3,
|
|
667
1715
|
value: trigger.afterTaskId,
|
|
668
1716
|
onChange: (e) => set({ afterTaskId: e.target.value }),
|
|
669
1717
|
"data-testid": `${testId}-after-task`,
|
|
@@ -692,7 +1740,7 @@ function TriggerInspector({
|
|
|
692
1740
|
/* @__PURE__ */ jsxs(
|
|
693
1741
|
"select",
|
|
694
1742
|
{
|
|
695
|
-
className:
|
|
1743
|
+
className: selectClass3,
|
|
696
1744
|
value: trigger.access,
|
|
697
1745
|
onChange: (e) => set({ access: e.target.value === "standing" ? "standing" : "ask_each_run" }),
|
|
698
1746
|
"data-testid": `${testId}-access`,
|
|
@@ -749,12 +1797,19 @@ var DEFAULT_WORKFLOW_CANVAS_LABELS = {
|
|
|
749
1797
|
upload: "Upload",
|
|
750
1798
|
agent: "AI step",
|
|
751
1799
|
review: "Approval",
|
|
1800
|
+
sign: "Sign",
|
|
1801
|
+
email_draft: "Email draft",
|
|
1802
|
+
tabular_review: "Tabular review",
|
|
1803
|
+
redline: "Tracked changes",
|
|
752
1804
|
branch: "Branch",
|
|
753
1805
|
kindHints: {
|
|
754
1806
|
form: "Ask for details",
|
|
755
1807
|
upload: "Collect files",
|
|
756
1808
|
agent: "Run a skill or an instruction",
|
|
757
|
-
review: "A person approves"
|
|
1809
|
+
review: "A person approves",
|
|
1810
|
+
sign: "People e-sign a document",
|
|
1811
|
+
email_draft: "Prepare an email to send",
|
|
1812
|
+
tabular_review: "Review documents in a grid"
|
|
758
1813
|
},
|
|
759
1814
|
addNext: "Add the next step",
|
|
760
1815
|
runs: "Runs \u201C{name}\u201D",
|
|
@@ -762,6 +1817,14 @@ var DEFAULT_WORKFLOW_CANVAS_LABELS = {
|
|
|
762
1817
|
fields: "{count} fields",
|
|
763
1818
|
noFields: "No fields yet",
|
|
764
1819
|
approvedBy: "A person approves",
|
|
1820
|
+
signers: "{count} signers",
|
|
1821
|
+
chooseDocument: "Choose the document to sign",
|
|
1822
|
+
emailTo: "To {to}",
|
|
1823
|
+
noRecipients: "Add a recipient",
|
|
1824
|
+
reviewColumns: "{count} columns",
|
|
1825
|
+
noColumns: "Choose the columns",
|
|
1826
|
+
assignee: "{name}",
|
|
1827
|
+
remind: "Reminds after {hours}h",
|
|
765
1828
|
emptyTitle: "Start your workflow",
|
|
766
1829
|
emptyHint: "Add the first step: what should happen when it starts?",
|
|
767
1830
|
fitView: "Fit view"
|
|
@@ -771,9 +1834,12 @@ var STEP_KIND_ICONS = {
|
|
|
771
1834
|
form: FileInput,
|
|
772
1835
|
upload: Upload,
|
|
773
1836
|
agent: Bot,
|
|
774
|
-
review: ClipboardCheck
|
|
1837
|
+
review: ClipboardCheck,
|
|
1838
|
+
sign: PenLine,
|
|
1839
|
+
email_draft: Mail,
|
|
1840
|
+
tabular_review: Table2
|
|
775
1841
|
};
|
|
776
|
-
var KINDS =
|
|
1842
|
+
var KINDS = WORKFLOW_STEP_KINDS;
|
|
777
1843
|
function AddNext({
|
|
778
1844
|
from,
|
|
779
1845
|
onAdd,
|
|
@@ -826,20 +1892,23 @@ function AddNext({
|
|
|
826
1892
|
}
|
|
827
1893
|
function PhaseMark({ phase }) {
|
|
828
1894
|
if (phase === "running") return /* @__PURE__ */ jsx(Loader2, { className: "wf-kind-text size-4 animate-spin" });
|
|
1895
|
+
if (phase === "waiting") return /* @__PURE__ */ jsx(Hourglass, { className: "wf-kind-text size-4" });
|
|
829
1896
|
if (phase === "done") return /* @__PURE__ */ jsx(Check, { className: "wf-kind-text size-4" });
|
|
830
1897
|
if (phase === "failed") return /* @__PURE__ */ jsx(AlertTriangle, { className: "size-4 text-destructive" });
|
|
831
1898
|
if (phase === "skipped") return /* @__PURE__ */ jsx(MinusCircle, { className: "size-4 text-muted-foreground" });
|
|
832
1899
|
return null;
|
|
833
1900
|
}
|
|
834
1901
|
function StepNode({ data }) {
|
|
835
|
-
const { step, phase, summary, icon, l, onAdd, testId } = data;
|
|
836
|
-
const
|
|
1902
|
+
const { step, phase, summary, assignee, icon, l, onAdd, testId } = data;
|
|
1903
|
+
const kind = stepKindOf(step);
|
|
1904
|
+
const KindIcon = STEP_KIND_ICONS[kind];
|
|
1905
|
+
const redline = step.kind === "agent" && !!step.editDocument;
|
|
837
1906
|
return /* @__PURE__ */ jsxs(
|
|
838
1907
|
"div",
|
|
839
1908
|
{
|
|
840
1909
|
className: cn(
|
|
841
1910
|
"wf-node w-64 py-3 pl-4 pr-3 text-left",
|
|
842
|
-
`wf-kind-${
|
|
1911
|
+
`wf-kind-${kind}`,
|
|
843
1912
|
phase && `wf-phase-${phase}`
|
|
844
1913
|
),
|
|
845
1914
|
"data-testid": `${testId}-node-${step.id}`,
|
|
@@ -850,15 +1919,44 @@ function StepNode({ data }) {
|
|
|
850
1919
|
/* @__PURE__ */ jsx("span", { className: "wf-badge flex size-8 shrink-0 items-center justify-center rounded-lg [&_svg]:size-4", children: icon ?? /* @__PURE__ */ jsx(KindIcon, {}) }),
|
|
851
1920
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
852
1921
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
853
|
-
/* @__PURE__ */ jsx("span", { className: "wf-kind-text text-[10px] font-semibold uppercase tracking-wider", children: l[
|
|
1922
|
+
/* @__PURE__ */ jsx("span", { className: "wf-kind-text text-[10px] font-semibold uppercase tracking-wider", children: l[kind] }),
|
|
854
1923
|
/* @__PURE__ */ jsx(PhaseMark, { phase })
|
|
855
1924
|
] }),
|
|
856
1925
|
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold leading-snug", children: step.title }),
|
|
857
1926
|
summary && /* @__PURE__ */ jsx("p", { className: "mt-0.5 line-clamp-2 text-xs leading-snug text-muted-foreground", children: summary }),
|
|
858
|
-
step.kind === "agent" && step.branch && /* @__PURE__ */ jsxs("
|
|
859
|
-
/* @__PURE__ */
|
|
860
|
-
|
|
861
|
-
|
|
1927
|
+
(redline || step.kind === "agent" && step.branch || assignee) && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex flex-wrap gap-1", children: [
|
|
1928
|
+
step.kind === "agent" && step.branch && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground", children: [
|
|
1929
|
+
/* @__PURE__ */ jsx(GitBranch, { className: "size-3" }),
|
|
1930
|
+
" ",
|
|
1931
|
+
l.branch
|
|
1932
|
+
] }),
|
|
1933
|
+
redline && /* @__PURE__ */ jsxs(
|
|
1934
|
+
"span",
|
|
1935
|
+
{
|
|
1936
|
+
className: "inline-flex items-center gap-1 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
|
|
1937
|
+
"data-testid": `${testId}-node-${step.id}-redline`,
|
|
1938
|
+
children: [
|
|
1939
|
+
/* @__PURE__ */ jsx(FilePenLine, { className: "size-3" }),
|
|
1940
|
+
" ",
|
|
1941
|
+
l.redline
|
|
1942
|
+
]
|
|
1943
|
+
}
|
|
1944
|
+
),
|
|
1945
|
+
assignee && /* @__PURE__ */ jsxs(
|
|
1946
|
+
"span",
|
|
1947
|
+
{
|
|
1948
|
+
className: "inline-flex max-w-full items-center gap-1 truncate rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
|
|
1949
|
+
title: assignee,
|
|
1950
|
+
"data-testid": `${testId}-node-${step.id}-assignee`,
|
|
1951
|
+
children: [
|
|
1952
|
+
/* @__PURE__ */ jsx(UserRound, { className: "size-3 shrink-0" }),
|
|
1953
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: l.assignee.replace(
|
|
1954
|
+
"{name}",
|
|
1955
|
+
assignee.charAt(0).toUpperCase() + assignee.slice(1)
|
|
1956
|
+
) })
|
|
1957
|
+
]
|
|
1958
|
+
}
|
|
1959
|
+
)
|
|
862
1960
|
] })
|
|
863
1961
|
] })
|
|
864
1962
|
] }),
|
|
@@ -907,6 +2005,16 @@ function summaryOf(step, l, workflowTitle) {
|
|
|
907
2005
|
return step.hint;
|
|
908
2006
|
case "review":
|
|
909
2007
|
return step.instructions ? firstWords(step.instructions) : l.approvedBy;
|
|
2008
|
+
case "action": {
|
|
2009
|
+
const a = step.action;
|
|
2010
|
+
if (a.type === "sign")
|
|
2011
|
+
return !a.document ? l.chooseDocument : a.signers.length ? l.signers.replace("{count}", String(a.signers.length)) : void 0;
|
|
2012
|
+
if (a.type === "email_draft") {
|
|
2013
|
+
const to = a.to.find((t) => t.trim());
|
|
2014
|
+
return to ? l.emailTo.replace("{to}", to) : l.noRecipients;
|
|
2015
|
+
}
|
|
2016
|
+
return a.tags.length ? l.reviewColumns.replace("{count}", String(a.tags.length)) : l.noColumns;
|
|
2017
|
+
}
|
|
910
2018
|
}
|
|
911
2019
|
}
|
|
912
2020
|
function WorkflowCanvas({
|
|
@@ -918,6 +2026,7 @@ function WorkflowCanvas({
|
|
|
918
2026
|
onAddAfter,
|
|
919
2027
|
phases,
|
|
920
2028
|
workflowTitle,
|
|
2029
|
+
assigneeOf,
|
|
921
2030
|
trigger,
|
|
922
2031
|
renderIcon,
|
|
923
2032
|
labels,
|
|
@@ -956,6 +2065,7 @@ function WorkflowCanvas({
|
|
|
956
2065
|
step,
|
|
957
2066
|
phase: phases?.[step.id],
|
|
958
2067
|
summary: summaryOf(step, l, workflowTitle),
|
|
2068
|
+
assignee: assigneeOf?.(step),
|
|
959
2069
|
icon: step.icon && renderIcon ? renderIcon(step.icon) : void 0,
|
|
960
2070
|
l,
|
|
961
2071
|
onAdd: onAddAfter,
|
|
@@ -963,7 +2073,18 @@ function WorkflowCanvas({
|
|
|
963
2073
|
}
|
|
964
2074
|
}))
|
|
965
2075
|
];
|
|
966
|
-
}, [
|
|
2076
|
+
}, [
|
|
2077
|
+
workflow,
|
|
2078
|
+
selected,
|
|
2079
|
+
phases,
|
|
2080
|
+
workflowTitle,
|
|
2081
|
+
assigneeOf,
|
|
2082
|
+
trigger,
|
|
2083
|
+
renderIcon,
|
|
2084
|
+
l,
|
|
2085
|
+
onAddAfter,
|
|
2086
|
+
testId
|
|
2087
|
+
]);
|
|
967
2088
|
const [nodes, setNodes] = useState(built);
|
|
968
2089
|
useEffect(() => setNodes(built), [built]);
|
|
969
2090
|
const edges = useMemo(
|
|
@@ -1177,10 +2298,17 @@ var DEFAULT_WORKFLOW_BUILDER_LABELS = {
|
|
|
1177
2298
|
unknown: "\u201C{step}\u201D waits for a step that does not exist.",
|
|
1178
2299
|
cycle: "\u201C{step}\u201D waits for itself (a loop).",
|
|
1179
2300
|
order: "\u201C{step}\u201D is listed before a step it waits for."
|
|
2301
|
+
},
|
|
2302
|
+
missing: {
|
|
2303
|
+
document: "\u201C{step}\u201D needs a document.",
|
|
2304
|
+
signers: "\u201C{step}\u201D needs at least one signer.",
|
|
2305
|
+
recipients: "\u201C{step}\u201D needs a recipient.",
|
|
2306
|
+
tags: "\u201C{step}\u201D needs at least one column.",
|
|
2307
|
+
documents: "\u201C{step}\u201D needs documents to review."
|
|
1180
2308
|
}
|
|
1181
2309
|
};
|
|
1182
2310
|
var PANEL_WIDTH = 344;
|
|
1183
|
-
var KINDS2 =
|
|
2311
|
+
var KINDS2 = WORKFLOW_STEP_KINDS;
|
|
1184
2312
|
function WorkflowBuilder({
|
|
1185
2313
|
value,
|
|
1186
2314
|
onChange,
|
|
@@ -1188,6 +2316,10 @@ function WorkflowBuilder({
|
|
|
1188
2316
|
agents = [],
|
|
1189
2317
|
eventKinds = [],
|
|
1190
2318
|
tasks = [],
|
|
2319
|
+
signals = [],
|
|
2320
|
+
people = [],
|
|
2321
|
+
tags = [],
|
|
2322
|
+
documents = [],
|
|
1191
2323
|
compose,
|
|
1192
2324
|
renderIcon,
|
|
1193
2325
|
actions,
|
|
@@ -1210,6 +2342,10 @@ function WorkflowBuilder({
|
|
|
1210
2342
|
const { workflow, trigger } = value;
|
|
1211
2343
|
const steps = workflowSteps(workflow);
|
|
1212
2344
|
const problems = validateWorkflowGraph(steps);
|
|
2345
|
+
const missing = stepConfigProblems(steps);
|
|
2346
|
+
const nameOf = (id) => people.find((p) => p.value === id)?.label;
|
|
2347
|
+
const fields = runFields(workflow);
|
|
2348
|
+
const assigneeOf = (step) => canAssign(step) ? assigneeName(step.assignee, { nameOf, fields }) : void 0;
|
|
1213
2349
|
const titles = useMemo(() => new Map(workflows.map((w) => [w.id, w.title])), [workflows]);
|
|
1214
2350
|
const setWorkflow = (next) => onChange({ ...value, workflow: next });
|
|
1215
2351
|
const addAfter = (from, kind) => {
|
|
@@ -1322,10 +2458,16 @@ function WorkflowBuilder({
|
|
|
1322
2458
|
] }),
|
|
1323
2459
|
actions && /* @__PURE__ */ jsx("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: actions })
|
|
1324
2460
|
] }),
|
|
1325
|
-
problems.length > 0 && /* @__PURE__ */
|
|
1326
|
-
"
|
|
1327
|
-
|
|
1328
|
-
|
|
2461
|
+
problems.length + missing.length > 0 && /* @__PURE__ */ jsxs("ul", { className: "space-y-1 text-sm text-destructive", "data-testid": `${testId}-problems`, children: [
|
|
2462
|
+
problems.map((p, i) => /* @__PURE__ */ jsx("li", { children: l.problems[p.code].replace(
|
|
2463
|
+
"{step}",
|
|
2464
|
+
steps.find((s) => s.id === p.step)?.title ?? p.step
|
|
2465
|
+
) }, i)),
|
|
2466
|
+
missing.map((p, i) => /* @__PURE__ */ jsx("li", { "data-testid": `${testId}-missing-${p.step}-${p.missing}`, children: l.missing[p.missing].replace(
|
|
2467
|
+
"{step}",
|
|
2468
|
+
steps.find((s) => s.id === p.step)?.title ?? p.step
|
|
2469
|
+
) }, `m${i}`))
|
|
2470
|
+
] }),
|
|
1329
2471
|
mode === "design" ? /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1330
2472
|
/* @__PURE__ */ jsx(
|
|
1331
2473
|
WorkflowCanvas,
|
|
@@ -1338,6 +2480,7 @@ function WorkflowBuilder({
|
|
|
1338
2480
|
onAddAfter: addAfter,
|
|
1339
2481
|
trigger: { title: l.start, summary },
|
|
1340
2482
|
workflowTitle: (id) => titles.get(id),
|
|
2483
|
+
assigneeOf,
|
|
1341
2484
|
renderIcon,
|
|
1342
2485
|
labels: canvasLabels,
|
|
1343
2486
|
reserveRight: panelOpen ? PANEL_WIDTH : 0,
|
|
@@ -1370,6 +2513,12 @@ function WorkflowBuilder({
|
|
|
1370
2513
|
agents,
|
|
1371
2514
|
eventKinds,
|
|
1372
2515
|
tasks,
|
|
2516
|
+
signals: [
|
|
2517
|
+
...signals,
|
|
2518
|
+
...declaredSignals([], workflow).filter(
|
|
2519
|
+
(d) => !signals.some((s) => s.value === d.value)
|
|
2520
|
+
)
|
|
2521
|
+
],
|
|
1373
2522
|
labels: triggerLabels,
|
|
1374
2523
|
testId: `${testId}-trigger`
|
|
1375
2524
|
}
|
|
@@ -1381,6 +2530,9 @@ function WorkflowBuilder({
|
|
|
1381
2530
|
onChange: setWorkflow,
|
|
1382
2531
|
onSelect: setSelected,
|
|
1383
2532
|
workflows,
|
|
2533
|
+
people,
|
|
2534
|
+
tags,
|
|
2535
|
+
documents,
|
|
1384
2536
|
labels: inspectorLabels,
|
|
1385
2537
|
testId: `${testId}-inspector`
|
|
1386
2538
|
}
|
|
@@ -1447,111 +2599,6 @@ function WorkflowPreview({
|
|
|
1447
2599
|
}
|
|
1448
2600
|
);
|
|
1449
2601
|
}
|
|
1450
|
-
|
|
1451
|
-
// src/workflow-canvas/trigger.ts
|
|
1452
|
-
var DEFAULT_WORKFLOW_TRIGGER = {
|
|
1453
|
-
start: "manual",
|
|
1454
|
-
runAt: "",
|
|
1455
|
-
rrule: "",
|
|
1456
|
-
timezone: typeof Intl !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC",
|
|
1457
|
-
eventKind: "",
|
|
1458
|
-
afterTaskId: "",
|
|
1459
|
-
agentId: "",
|
|
1460
|
-
access: "ask_each_run"
|
|
1461
|
-
};
|
|
1462
|
-
function triggerProblem(trigger) {
|
|
1463
|
-
if (trigger.start === "once" && !trigger.runAt) return "runAt";
|
|
1464
|
-
if (trigger.start === "repeat" && !trigger.rrule.trim()) return "rrule";
|
|
1465
|
-
if (trigger.start === "event" && !trigger.eventKind.trim()) return "event";
|
|
1466
|
-
if (trigger.start === "task" && !trigger.afterTaskId) return "task";
|
|
1467
|
-
return null;
|
|
1468
|
-
}
|
|
1469
|
-
function triggerFields(trigger, clearOthers) {
|
|
1470
|
-
const none = clearOthers ? null : void 0;
|
|
1471
|
-
return {
|
|
1472
|
-
timezone: trigger.timezone,
|
|
1473
|
-
rrule: trigger.start === "repeat" ? trigger.rrule.trim() : none,
|
|
1474
|
-
run_at: trigger.start === "once" ? localInputToIso(trigger.runAt) : none,
|
|
1475
|
-
event_kind: trigger.start === "event" ? trigger.eventKind.trim() : none,
|
|
1476
|
-
after_conversation_ext_id: trigger.start === "task" ? trigger.afterTaskId : none,
|
|
1477
|
-
on_demand: trigger.start === "manual"
|
|
1478
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
var authMode = (trigger) => trigger.start === "manual" ? "run_now_temp" : trigger.access === "standing" ? "scheduled_permanent" : "scheduled_require_auth";
|
|
1481
|
-
var withoutUndefined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
1482
|
-
function plan(value, options) {
|
|
1483
|
-
return {
|
|
1484
|
-
goal: value.workflow.description,
|
|
1485
|
-
criteria: [],
|
|
1486
|
-
max_passes: 3,
|
|
1487
|
-
documents: [],
|
|
1488
|
-
steps: toPlanSteps(value.workflow, options)
|
|
1489
|
-
};
|
|
1490
|
-
}
|
|
1491
|
-
function builderToWorkflowRequest(value, workspaceId, options = {}) {
|
|
1492
|
-
return withoutUndefined({
|
|
1493
|
-
workspace_ext_id: workspaceId,
|
|
1494
|
-
name: value.workflow.title,
|
|
1495
|
-
description: value.workflow.description,
|
|
1496
|
-
active: true,
|
|
1497
|
-
shared: true,
|
|
1498
|
-
...triggerFields(value.trigger, false),
|
|
1499
|
-
prompt: "",
|
|
1500
|
-
skill_ref: null,
|
|
1501
|
-
skill_inputs: null,
|
|
1502
|
-
agent_ext_id: value.trigger.agentId || null,
|
|
1503
|
-
auth_mode: authMode(value.trigger),
|
|
1504
|
-
plan: plan(value, options)
|
|
1505
|
-
});
|
|
1506
|
-
}
|
|
1507
|
-
function builderToWorkflowUpdate(value, options = {}) {
|
|
1508
|
-
return withoutUndefined({
|
|
1509
|
-
name: value.workflow.title,
|
|
1510
|
-
description: value.workflow.description,
|
|
1511
|
-
...triggerFields(value.trigger, true),
|
|
1512
|
-
// The steps are what runs (a classic automation's single prompt or skill becomes its one step).
|
|
1513
|
-
prompt: "",
|
|
1514
|
-
skill_ref: null,
|
|
1515
|
-
skill_inputs: null,
|
|
1516
|
-
agent_ext_id: value.trigger.agentId || null,
|
|
1517
|
-
auth_mode: authMode(value.trigger),
|
|
1518
|
-
plan: plan(value, options)
|
|
1519
|
-
});
|
|
1520
|
-
}
|
|
1521
|
-
function workflowRecordToBuilder(record) {
|
|
1522
|
-
const planned = record.plan?.steps ?? [];
|
|
1523
|
-
const steps = planned.length ? fromPlanSteps(planned) : record.prompt || record.skill_ref ? [
|
|
1524
|
-
{
|
|
1525
|
-
id: "work",
|
|
1526
|
-
kind: "agent",
|
|
1527
|
-
title: record.name,
|
|
1528
|
-
...record.skill_ref ? { workflow: record.skill_ref } : { run: { kind: "prompt", promptTemplate: record.prompt } }
|
|
1529
|
-
}
|
|
1530
|
-
] : [];
|
|
1531
|
-
const when = workflowWhen(record);
|
|
1532
|
-
return {
|
|
1533
|
-
workflow: {
|
|
1534
|
-
id: record.external_id,
|
|
1535
|
-
title: record.name,
|
|
1536
|
-
description: record.description ?? "",
|
|
1537
|
-
inputs: [],
|
|
1538
|
-
landing: "chat",
|
|
1539
|
-
schedulable: false,
|
|
1540
|
-
run: { kind: "steps", steps, layout: layoutFromPlanSteps(planned ?? []) }
|
|
1541
|
-
},
|
|
1542
|
-
trigger: {
|
|
1543
|
-
...DEFAULT_WORKFLOW_TRIGGER,
|
|
1544
|
-
start: when === "now" ? "manual" : when,
|
|
1545
|
-
runAt: isoToLocalInput(record.run_at),
|
|
1546
|
-
rrule: record.rrule ?? "",
|
|
1547
|
-
timezone: record.timezone || DEFAULT_WORKFLOW_TRIGGER.timezone,
|
|
1548
|
-
eventKind: record.event_kind ?? "",
|
|
1549
|
-
afterTaskId: record.after_conversation_ext_id ?? "",
|
|
1550
|
-
agentId: record.agent_ext_id ?? "",
|
|
1551
|
-
access: record.auth_mode === "scheduled_permanent" ? "standing" : "ask_each_run"
|
|
1552
|
-
}
|
|
1553
|
-
};
|
|
1554
|
-
}
|
|
1555
2602
|
var DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS = {
|
|
1556
2603
|
save: "Save",
|
|
1557
2604
|
saving: "Saving\u2026",
|
|
@@ -1564,7 +2611,8 @@ var DEFAULT_WORKFLOW_EVENT_KINDS = [
|
|
|
1564
2611
|
{ value: "conversation_ready", label: "Work is ready for me" },
|
|
1565
2612
|
{ value: "mention", label: "Someone mentions me" },
|
|
1566
2613
|
{ value: "workflow_completed", label: "Another workflow finishes" },
|
|
1567
|
-
{ value: "workflow_failed", label: "Another workflow fails" }
|
|
2614
|
+
{ value: "workflow_failed", label: "Another workflow fails" },
|
|
2615
|
+
{ value: "workflow_signal", label: "When a step signals" }
|
|
1568
2616
|
];
|
|
1569
2617
|
function ConnectedWorkflowBuilder({
|
|
1570
2618
|
workflowId,
|
|
@@ -1580,7 +2628,22 @@ function ConnectedWorkflowBuilder({
|
|
|
1580
2628
|
...builderProps
|
|
1581
2629
|
}) {
|
|
1582
2630
|
const l = { ...DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS, ...connectedLabels };
|
|
1583
|
-
const { workspaceId } = useConnection();
|
|
2631
|
+
const { workspaceId, status, ensureWorkspaceOpen } = useConnection();
|
|
2632
|
+
const members = useWorkspaceUsers(workspaceId ?? void 0, {
|
|
2633
|
+
enabled: status === "authenticated" && !!workspaceId,
|
|
2634
|
+
beforeFetch: ensureWorkspaceOpen
|
|
2635
|
+
});
|
|
2636
|
+
const tagsQuery = useTags(workspaceId ?? void 0);
|
|
2637
|
+
const docsQuery = useDocuments(workspaceId ?? void 0);
|
|
2638
|
+
const people = (members.data ?? []).map((m) => ({
|
|
2639
|
+
value: m.user.external_id,
|
|
2640
|
+
label: [m.user.given_name, m.user.family_name].filter(Boolean).join(" ") || m.user.email
|
|
2641
|
+
}));
|
|
2642
|
+
const tagOptions = (tagsQuery.data ?? []).map((t) => ({ value: t.external_id, label: t.name }));
|
|
2643
|
+
const documentOptions = (docsQuery.data ?? []).map((d) => ({
|
|
2644
|
+
value: d.external_id,
|
|
2645
|
+
label: d.file_name ?? d.external_id
|
|
2646
|
+
}));
|
|
1584
2647
|
const saved = useWorkflows();
|
|
1585
2648
|
const skills = useSkills();
|
|
1586
2649
|
const agentsQuery = useAgents();
|
|
@@ -1619,7 +2682,7 @@ function ConnectedWorkflowBuilder({
|
|
|
1619
2682
|
}));
|
|
1620
2683
|
const tasks = (conversations.data ?? []).filter((c) => c.status && c.status !== "done").map((c) => ({ value: c.external_id, label: c.title || c.external_id }));
|
|
1621
2684
|
const options = { resolveWorkflow: (wid) => workflows.find((w) => w.id === wid), compose };
|
|
1622
|
-
const problem = triggerProblem(value.trigger) ?? (validateWorkflowGraph(workflowSteps(value.workflow)).length ? "graph" : null);
|
|
2685
|
+
const problem = triggerProblem(value.trigger) ?? (validateWorkflowGraph(workflowSteps(value.workflow)).length || stepConfigProblems(workflowSteps(value.workflow)).length ? "graph" : null);
|
|
1623
2686
|
const busy = create.isPending || update.isPending || run.isPending;
|
|
1624
2687
|
const save = async () => {
|
|
1625
2688
|
if (!workspaceId) return void 0;
|
|
@@ -1641,6 +2704,10 @@ function ConnectedWorkflowBuilder({
|
|
|
1641
2704
|
agents,
|
|
1642
2705
|
eventKinds,
|
|
1643
2706
|
tasks,
|
|
2707
|
+
people,
|
|
2708
|
+
tags: tagOptions,
|
|
2709
|
+
documents: documentOptions,
|
|
2710
|
+
signals: declaredSignals(saved.data ?? []),
|
|
1644
2711
|
compose,
|
|
1645
2712
|
testId,
|
|
1646
2713
|
actions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -1685,6 +2752,6 @@ function ConnectedWorkflowBuilder({
|
|
|
1685
2752
|
);
|
|
1686
2753
|
}
|
|
1687
2754
|
|
|
1688
|
-
export { ConnectedWorkflowBuilder, DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS, DEFAULT_STEP_INSPECTOR_LABELS, DEFAULT_TRIGGER_INSPECTOR_LABELS, DEFAULT_WORKFLOW_BUILDER_LABELS, DEFAULT_WORKFLOW_CANVAS_LABELS, DEFAULT_WORKFLOW_EVENT_KINDS, DEFAULT_WORKFLOW_TRIGGER, StepInspector, TRIGGER_NODE, TriggerInspector, WorkflowBuilder, WorkflowCanvas, addStep, autoLayout, builderToWorkflowRequest, builderToWorkflowUpdate, connectSteps, disconnectSteps, layoutWithDefaults, moveStep, newSteppedWorkflow, removeStep, stepIdFor, triggerProblem, triggerSummary, updateStep, workflowRecordToBuilder, wouldCycle };
|
|
2755
|
+
export { ConnectedWorkflowBuilder, DEFAULT_CONNECTED_WORKFLOW_BUILDER_LABELS, DEFAULT_SIGNALS_EDITOR_LABELS, DEFAULT_STEP_INSPECTOR_LABELS, DEFAULT_TRIGGER_INSPECTOR_LABELS, DEFAULT_WORKFLOW_BUILDER_LABELS, DEFAULT_WORKFLOW_CANVAS_LABELS, DEFAULT_WORKFLOW_EVENT_KINDS, DEFAULT_WORKFLOW_TRIGGER, SignalsEditor, StepInspector, TRIGGER_NODE, TriggerInspector, WORKFLOW_SIGNAL_EVENT, WorkflowBuilder, WorkflowCanvas, addStep, autoLayout, builderToWorkflowRequest, builderToWorkflowUpdate, connectSteps, declaredSignals, disconnectSteps, isSignalTrigger, layoutWithDefaults, moveStep, newSteppedWorkflow, removeStep, signalConditions, signalSlug, stepIdFor, triggerProblem, triggerSummary, updateStep, workflowRecordToBuilder, wouldCycle };
|
|
1689
2756
|
//# sourceMappingURL=index.js.map
|
|
1690
2757
|
//# sourceMappingURL=index.js.map
|