@burdenoff/fluidgrids-fe-sdk 2026.1111.16 → 2026.1111.17
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/README.md +41 -0
- package/dist/AiStudioPage-kJHSsIcj.js +774 -0
- package/dist/ApprovalInboxPage-dbBDSJ-f.js +558 -0
- package/dist/DataSinkDashboardPage-1h2QsMzH.js +270 -0
- package/dist/DataTablesPage-BZy2z0LB.js +1690 -0
- package/dist/EntityTagPicker-BqRNsLCQ.js +135 -0
- package/dist/{ExecutionDetailPage-D-0_POAf.js → ExecutionDetailPage-BdcZmLT4.js} +799 -4420
- package/dist/ExecutionDetailPage-DvQ667OE.d.ts +10 -0
- package/dist/ExecutionsListPage-BCUJoAIE.js +2742 -0
- package/dist/{FailureTriagePanel-BHiduL7A.js → FailureTriagePanel-B9hDQymi.js} +4 -97
- package/dist/{FluidGridsProvider-DmvFADFa.js → FluidGridsProvider-dvZSNtSW.js} +10 -350
- package/dist/HistoryPage-BLoRpJ1R.d.ts +6 -0
- package/dist/{HistoryPage-BbUgxx1I.js → HistoryPage-U0tqupBN.js} +6 -4
- package/dist/HumanRequestCard-D-ooVPDn.js +439 -0
- package/dist/{InsightsPage-s6f5bU-8.js → InsightsPage-CALo16eB.js} +17 -328
- package/dist/ManagedAiPolicyAuthority-BxwJ3ptd.js +40 -0
- package/dist/{ManagedAiPolicyAuthority-CGux_DaV.d.ts → ManagedAiPolicyAuthority-DM3t3wNr.d.ts} +1 -5
- package/dist/ManagedAiPolicyAuthorityContext-CNkxIOZ1.js +10 -0
- package/dist/{MissingNodesInstallModal-B-9gHy2K.js → MissingNodesInstallModal-CMybsI8h.js} +3 -2
- package/dist/{NodesPage-ClvhUfbh.js → NodesPage-Bf5PhfBK.js} +7 -3
- package/dist/NodesPage-DEbnz9iU.d.ts +6 -0
- package/dist/{PublishCustomNodeAction-D2F7oN0S.js → PublishCustomNodeAction-5XXnSA3H.js} +3 -45
- package/dist/QuotaExhaustedDialog-7IFSaLyi.js +54 -0
- package/dist/RunnerStatusPill-Ctg4TJcP.js +187 -0
- package/dist/RunnersPage-CLCOCclc.js +467 -0
- package/dist/{ManagedAiPolicyAuthority-DsSaz-vg.js → SettingsPage-CgkRlCM5.js} +19 -50
- package/dist/SettingsPage-DMHQFj5-.d.ts +9 -0
- package/dist/TagFilterSelect-DCBOH6Ou.js +41 -0
- package/dist/TaskQueuesPage-9VgSnLdL.js +951 -0
- package/dist/TemplateCard-DsC0KNH1.js +1416 -0
- package/dist/{TemplateConfigurationModal-D_Mvgxqa.js → TemplateConfigurationModal-BhGWIzpm.js} +3 -2
- package/dist/{TemplateDetailPanel-DfFfUcOe.js → TemplateDetailPanel-D4cUVr-m.js} +3 -3
- package/dist/TemplatePreview-GthzCOSo.js +231 -0
- package/dist/TemplatesPage-BG3H-6v6.d.ts +10 -0
- package/dist/{TemplatesPage-Cku13bav.js → TemplatesPage-CsDNT54N.js} +11 -228
- package/dist/VariablesPage-67WR_lVE.js +949 -0
- package/dist/{VersionDiffDialog-Dq2xlPFI.js → VersionDiffDialog-DSN0mU0f.js} +2 -1
- package/dist/WorkflowBuilder-DEi_9YOb.js +555 -0
- package/dist/{WorkflowSearchRedirect-CaoOOpEl.js → WorkflowBuilder-iPqXgbYr.js} +19 -585
- package/dist/{WorkflowProposalCard-8Vr56kQa.js → WorkflowProposalCard-qrEeNyYG.js} +4 -405
- package/dist/WorkflowSearchRedirect-DwDNZ_q5.js +36 -0
- package/dist/{WorkflowShareDialog-6RRDe-_v.js → WorkflowShareDialog-B_gn968c.js} +6 -52
- package/dist/{WorkflowsList-B2j1Pehb.js → WorkflowsList-CA20dVIl.js} +11 -6
- package/dist/WorkflowsList-CcNDQge5.d.ts +6 -0
- package/dist/agent-form-Bfvm32j1.js +83 -0
- package/dist/{agentTools-DJ5o2ekg.js → agentTools-CkRJB7r5.js} +3 -2
- package/dist/{ai-usage-D5p7ZeRR.js → ai-usage-glCwHyQz.js} +1 -1
- package/dist/api-BrCHB5oA.js +224 -0
- package/dist/api-C8e0iV-Q.js +55 -0
- package/dist/button-DG7y_FVJ.js +48 -0
- package/dist/{canvasNoteTools-D3zUdEeg.js → canvasNoteTools-CQIc7Oxg.js} +2 -2
- package/dist/client-C9XSzSXr.js +1147 -0
- package/dist/{codec-client-DstqyzTN.js → codec-client-CJ95Rmqo.js} +1 -1
- package/dist/constants-B2lhWNoN.js +202 -0
- package/dist/constants-XdvUxeWK.d.ts +162 -0
- package/dist/dialog-Cjb4lB4D.js +105 -0
- package/dist/executions.d.ts +1 -1
- package/dist/executions.js +2 -1
- package/dist/feature-flags-DM5myWaD.js +296 -0
- package/dist/flags.d.ts +3 -0
- package/dist/flags.js +4 -0
- package/dist/{flow-builder-DEzT8TQu.js → flow-builder-y2Y1iG4Y.js} +70 -1731
- package/dist/{node-endpoint-configs-DHFGA5MQ.js → graphql-operation-types-kX-MMjgW.js} +1 -1194
- package/dist/{graphql-types-XcDLtFwU.js → graphql-types-CDKVWH2R.js} +2 -202
- package/dist/history.d.ts +1 -1
- package/dist/history.js +1 -1
- package/dist/index-72XFgvbn.d.ts +132 -0
- package/dist/index-BcdtKBM-.d.ts +56 -0
- package/dist/{index-B3BWqafN.d.ts → index-DjiFgEQl.d.ts} +5 -131
- package/dist/{index-DRf25-D3.d.ts → index-W2o__Nm6.d.ts} +32 -214
- package/dist/index.d.ts +13 -9
- package/dist/index.js +50 -5300
- package/dist/{node-detail-view-LNqeIqN-.js → node-detail-view-B91kXzIy.js} +11 -8
- package/dist/node-endpoint-configs-B5bjNMDW.js +37 -0
- package/dist/node-registry-refresh-Dy0uRg9f.js +84 -0
- package/dist/nodes.d.ts +1 -1
- package/dist/nodes.js +1 -1
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +2 -1
- package/dist/{quick-config-panel-D3ogsaFL.js → quick-config-panel-DggPl1vY.js} +2 -2
- package/dist/rolldown-runtime-hDbaFkMy.js +18 -0
- package/dist/run-display-BycKYSU0.js +46 -0
- package/dist/run-list-links-ByYC0OlV.js +98 -0
- package/dist/settings.d.ts +2 -1
- package/dist/settings.js +2 -1
- package/dist/shared-CMfn30xT.js +196 -0
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +2 -1
- package/dist/theme-provider-B3mIuz2d.js +11 -0
- package/dist/{useAgentDefinitions-NFtLytDR.js → useAgentDefinitions-Dpz0HDTq.js} +2 -1
- package/dist/useDurableParity-CzUQMFJ3.js +415 -0
- package/dist/useEntityTags-DhnsZcTl.js +236 -0
- package/dist/{useExecutions-DAzeWHS6.js → useExecutions-9aLuOt1c.js} +4 -3
- package/dist/{useObservability-COCZ0hUS.js → useObservability-CfEw3ZFD.js} +2 -2
- package/dist/{useRunHistory-CsYUbouO.js → useRunHistory-B1xJDwl1.js} +4 -3
- package/dist/{useWorkflows-w4Dx2fF2.js → useWorkflows-BlVVu8NM.js} +3 -2
- package/dist/utils-uaL5UVxb.js +65 -0
- package/dist/workflow-builder.d.ts +1 -1
- package/dist/workflow-builder.js +4 -2
- package/dist/workflow-host.d.ts +6 -0
- package/dist/workflow-host.js +9 -0
- package/dist/workflow-pages.d.ts +55 -0
- package/dist/workflow-pages.js +113 -0
- package/dist/workflows-list.d.ts +1 -1
- package/dist/workflows-list.js +1 -1
- package/package.json +13 -1
- package/dist/ExecutionDetailPage-MvsnOnE1.d.ts +0 -4
- package/dist/HistoryPage-BqUizaFv.d.ts +0 -3
- package/dist/NodesPage-C97-9K2Y.d.ts +0 -3
- package/dist/TemplatesPage-D0qnXzHh.d.ts +0 -4
- package/dist/WorkflowsList-0d5GSKdh.d.ts +0 -3
- package/dist/agent-form-BvMWhQ42.js +0 -496
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
|
|
3
|
+
import { _ as throwIfResultError, n as getFluidGridsClient, x as getFluidGridsConfig } from "./client-C9XSzSXr.js";
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
|
+
import { Icon } from "@iconify/react";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { toast } from "sonner";
|
|
8
|
+
import { gql } from "@apollo/client";
|
|
9
|
+
import { Badge, Card, CardContent, CardHeader, Input, Label, Textarea } from "@burdenoff/fe-libs/ui";
|
|
10
|
+
import { Button as Button$1 } from "@burdenoff/fe-libs/button";
|
|
11
|
+
import { buildActorFallback, useActorProfiles } from "@burdenoff/fe-libs/shared";
|
|
12
|
+
|
|
13
|
+
// src/modules/human-response/utils/recipient.ts
|
|
14
|
+
/**
|
|
15
|
+
* Who an ask went to, for display (BOFF-7651). An in-app ask stores its responders as user ids
|
|
16
|
+
* (the worker cannot resolve names); the UI resolves them to people before showing them.
|
|
17
|
+
*/
|
|
18
|
+
const USER_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19
|
+
/** The recipient label split into its parts, in order. */
|
|
20
|
+
function recipientParts(recipient) {
|
|
21
|
+
return (recipient ?? "").split(",").map((part) => part.trim()).filter(Boolean);
|
|
22
|
+
}
|
|
23
|
+
/** The user ids in an in-app ask's recipient label, in order and without repeats. */
|
|
24
|
+
function inAppResponderIds(recipient) {
|
|
25
|
+
return [...new Set(recipientParts(recipient).filter((part) => USER_ID.test(part)))];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/modules/human-response/hooks/useRecipientLabel.ts
|
|
29
|
+
/**
|
|
30
|
+
* The people an ask went to, by name (BOFF-7651). In-app asks carry user ids; they are resolved
|
|
31
|
+
* through the shared actor-profile cache, the same lookup the run page uses for "triggered by".
|
|
32
|
+
* Email addresses, Slack channels and anything else are shown as stored.
|
|
33
|
+
*/
|
|
34
|
+
function useRecipientLabel(request, fallback) {
|
|
35
|
+
const idsKey = request.channel === "IN_APP" ? inAppResponderIds(request.recipient).join(",") : "";
|
|
36
|
+
const inputs = useMemo(() => idsKey ? idsKey.split(",").map((actorId) => ({
|
|
37
|
+
actorId,
|
|
38
|
+
actorType: "user"
|
|
39
|
+
})) : [], [idsKey]);
|
|
40
|
+
const config = getFluidGridsConfig();
|
|
41
|
+
const profiles = useActorProfiles(inputs, {
|
|
42
|
+
workspaceId: config.workspaceId || void 0,
|
|
43
|
+
orgId: config.organizationId,
|
|
44
|
+
authToken: config.authToken
|
|
45
|
+
});
|
|
46
|
+
if (!request.recipient) return fallback;
|
|
47
|
+
if (!idsKey) return request.recipient;
|
|
48
|
+
const ids = new Set(idsKey.split(","));
|
|
49
|
+
return recipientParts(request.recipient).map((part) => ids.has(part) ? profiles[part]?.displayName ?? buildActorFallback({
|
|
50
|
+
actorId: part,
|
|
51
|
+
actorType: "user"
|
|
52
|
+
}).displayName : part).join(", ");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/modules/human-response/hooks/useHumanRequests.ts
|
|
56
|
+
/**
|
|
57
|
+
* Hooks for "Send and Wait for Response" (BOFF-7651): asks a workflow made of a person, the
|
|
58
|
+
* in-app inbox of asks still waiting, and answering an in-app ask. Mirrors the tool-approval
|
|
59
|
+
* hooks (`modules/tool-approval/hooks/useToolApprovals.ts`).
|
|
60
|
+
*/
|
|
61
|
+
const HUMAN_REQUEST_FIELDS = gql`
|
|
62
|
+
fragment WorkflowHumanRequestFields on WorkflowHumanRequest {
|
|
63
|
+
id
|
|
64
|
+
runId
|
|
65
|
+
nodeId
|
|
66
|
+
workflowKey
|
|
67
|
+
channel
|
|
68
|
+
recipient
|
|
69
|
+
responseType
|
|
70
|
+
title
|
|
71
|
+
message
|
|
72
|
+
approvalType
|
|
73
|
+
approveLabel
|
|
74
|
+
declineLabel
|
|
75
|
+
submitLabel
|
|
76
|
+
fields {
|
|
77
|
+
name
|
|
78
|
+
label
|
|
79
|
+
type
|
|
80
|
+
required
|
|
81
|
+
placeholder
|
|
82
|
+
options
|
|
83
|
+
}
|
|
84
|
+
status
|
|
85
|
+
decision
|
|
86
|
+
response
|
|
87
|
+
respondedBy
|
|
88
|
+
respondedVia
|
|
89
|
+
expiresAt
|
|
90
|
+
respondedAt
|
|
91
|
+
createdAt
|
|
92
|
+
canRespond
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
const GET_PENDING_HUMAN_REQUESTS = gql`
|
|
96
|
+
${HUMAN_REQUEST_FIELDS}
|
|
97
|
+
query GetPendingWorkflowHumanRequests($limit: Int, $offset: Int) {
|
|
98
|
+
pendingWorkflowHumanRequests(limit: $limit, offset: $offset) {
|
|
99
|
+
totalCount
|
|
100
|
+
items {
|
|
101
|
+
...WorkflowHumanRequestFields
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
const GET_RUN_HUMAN_REQUESTS = gql`
|
|
107
|
+
${HUMAN_REQUEST_FIELDS}
|
|
108
|
+
query GetWorkflowHumanRequests($runId: ID!) {
|
|
109
|
+
workflowHumanRequests(runId: $runId) {
|
|
110
|
+
...WorkflowHumanRequestFields
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
`;
|
|
114
|
+
const RESPOND_TO_HUMAN_REQUEST = gql`
|
|
115
|
+
${HUMAN_REQUEST_FIELDS}
|
|
116
|
+
mutation RespondToWorkflowHumanRequest(
|
|
117
|
+
$input: RespondToWorkflowHumanRequestInput!
|
|
118
|
+
) {
|
|
119
|
+
respondToWorkflowHumanRequest(input: $input) {
|
|
120
|
+
success
|
|
121
|
+
decision
|
|
122
|
+
error
|
|
123
|
+
request {
|
|
124
|
+
...WorkflowHumanRequestFields
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
/** Asks still waiting for a person in the workspace (polled, like the tool-approval inbox). */
|
|
130
|
+
function usePendingHumanRequests(options) {
|
|
131
|
+
const limit = options?.limit ?? 50;
|
|
132
|
+
const offset = options?.offset ?? 0;
|
|
133
|
+
const pollIntervalMs = options?.pollIntervalMs ?? 15e3;
|
|
134
|
+
const [items, setItems] = useState([]);
|
|
135
|
+
const [totalCount, setTotalCount] = useState(0);
|
|
136
|
+
const [loading, setLoading] = useState(true);
|
|
137
|
+
const [error, setError] = useState(null);
|
|
138
|
+
const generation = useRef(0);
|
|
139
|
+
const fetchData = useCallback(async () => {
|
|
140
|
+
const ticket = ++generation.current;
|
|
141
|
+
setError(null);
|
|
142
|
+
try {
|
|
143
|
+
const result = throwIfResultError(await getFluidGridsClient().query({
|
|
144
|
+
query: GET_PENDING_HUMAN_REQUESTS,
|
|
145
|
+
variables: {
|
|
146
|
+
limit,
|
|
147
|
+
offset
|
|
148
|
+
},
|
|
149
|
+
fetchPolicy: "network-only"
|
|
150
|
+
}));
|
|
151
|
+
if (ticket !== generation.current) return;
|
|
152
|
+
setItems(result.data?.pendingWorkflowHumanRequests?.items ?? []);
|
|
153
|
+
setTotalCount(result.data?.pendingWorkflowHumanRequests?.totalCount ?? 0);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
if (ticket !== generation.current) return;
|
|
156
|
+
setError(err);
|
|
157
|
+
} finally {
|
|
158
|
+
if (ticket === generation.current) setLoading(false);
|
|
159
|
+
}
|
|
160
|
+
}, [limit, offset]);
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
fetchData();
|
|
163
|
+
if (pollIntervalMs <= 0) return;
|
|
164
|
+
const interval = setInterval(fetchData, pollIntervalMs);
|
|
165
|
+
return () => clearInterval(interval);
|
|
166
|
+
}, [fetchData, pollIntervalMs]);
|
|
167
|
+
return {
|
|
168
|
+
items,
|
|
169
|
+
totalCount,
|
|
170
|
+
loading,
|
|
171
|
+
error,
|
|
172
|
+
refetch: fetchData
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** Every ask a run made — who was asked, how, until when, and the answer. */
|
|
176
|
+
function useRunHumanRequests(runId, options) {
|
|
177
|
+
const pollIntervalMs = options?.pollIntervalMs ?? 0;
|
|
178
|
+
const [items, setItems] = useState([]);
|
|
179
|
+
const [error, setError] = useState(null);
|
|
180
|
+
const [loaded, setLoaded] = useState(false);
|
|
181
|
+
const [loadedAt, setLoadedAt] = useState(null);
|
|
182
|
+
const currentRunId = useRef(runId);
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
currentRunId.current = runId;
|
|
185
|
+
setItems([]);
|
|
186
|
+
setError(null);
|
|
187
|
+
setLoaded(false);
|
|
188
|
+
setLoadedAt(null);
|
|
189
|
+
}, [runId]);
|
|
190
|
+
const generation = useRef(0);
|
|
191
|
+
const fetchData = useCallback(async () => {
|
|
192
|
+
if (!runId) return;
|
|
193
|
+
const ticket = ++generation.current;
|
|
194
|
+
const startedAt = Date.now();
|
|
195
|
+
setError(null);
|
|
196
|
+
try {
|
|
197
|
+
const result = throwIfResultError(await getFluidGridsClient().query({
|
|
198
|
+
query: GET_RUN_HUMAN_REQUESTS,
|
|
199
|
+
variables: { runId },
|
|
200
|
+
fetchPolicy: "network-only",
|
|
201
|
+
context: { queryDeduplication: false }
|
|
202
|
+
}));
|
|
203
|
+
if (ticket !== generation.current || currentRunId.current !== runId) return;
|
|
204
|
+
setItems(result.data?.workflowHumanRequests ?? []);
|
|
205
|
+
setLoaded(true);
|
|
206
|
+
setLoadedAt(startedAt);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
if (ticket !== generation.current || currentRunId.current !== runId) return;
|
|
209
|
+
setError(err);
|
|
210
|
+
}
|
|
211
|
+
}, [runId]);
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
fetchData();
|
|
214
|
+
if (pollIntervalMs <= 0) return;
|
|
215
|
+
const interval = setInterval(fetchData, pollIntervalMs);
|
|
216
|
+
return () => clearInterval(interval);
|
|
217
|
+
}, [fetchData, pollIntervalMs]);
|
|
218
|
+
return {
|
|
219
|
+
items,
|
|
220
|
+
error,
|
|
221
|
+
loaded,
|
|
222
|
+
loadedAt,
|
|
223
|
+
refetch: fetchData
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/** Answer an in-app ask. Resolves to the decision; throws with the service's reason. */
|
|
227
|
+
function useRespondToHumanRequest() {
|
|
228
|
+
const [loading, setLoading] = useState(false);
|
|
229
|
+
return {
|
|
230
|
+
respond: useCallback(async (id, answer) => {
|
|
231
|
+
setLoading(true);
|
|
232
|
+
try {
|
|
233
|
+
const payload = throwIfResultError(await getFluidGridsClient().mutate({
|
|
234
|
+
mutation: RESPOND_TO_HUMAN_REQUEST,
|
|
235
|
+
variables: { input: {
|
|
236
|
+
id,
|
|
237
|
+
...answer
|
|
238
|
+
} }
|
|
239
|
+
})).data?.respondToWorkflowHumanRequest;
|
|
240
|
+
if (!payload?.success) throw new Error(payload?.error || "The response was not recorded");
|
|
241
|
+
return payload.decision;
|
|
242
|
+
} finally {
|
|
243
|
+
setLoading(false);
|
|
244
|
+
}
|
|
245
|
+
}, []),
|
|
246
|
+
loading
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/modules/human-response/components/HumanRequestCard.tsx
|
|
251
|
+
/**
|
|
252
|
+
* One "Send and Wait for Response" ask in the in-app inbox (BOFF-7651): what was asked, of whom,
|
|
253
|
+
* through which channel and until when — and, when the caller may answer it in-app, the controls
|
|
254
|
+
* to do so (Approve / Decline, a text box, or the custom form).
|
|
255
|
+
*/
|
|
256
|
+
const CHANNEL_ICON = {
|
|
257
|
+
EMAIL: "mdi:email-outline",
|
|
258
|
+
SLACK: "mdi:slack",
|
|
259
|
+
IN_APP: "mdi:bell-outline"
|
|
260
|
+
};
|
|
261
|
+
function channelLabel(channel, tr) {
|
|
262
|
+
switch (channel) {
|
|
263
|
+
case "EMAIL": return tr("fluidgrids.humanResponse.channelEmail", "email");
|
|
264
|
+
case "SLACK": return tr("fluidgrids.humanResponse.channelSlack", "Slack");
|
|
265
|
+
default: return tr("fluidgrids.humanResponse.channelInApp", "in-app");
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function inputType(field) {
|
|
269
|
+
const type = (field.type || "text").toLowerCase();
|
|
270
|
+
return [
|
|
271
|
+
"email",
|
|
272
|
+
"number",
|
|
273
|
+
"date",
|
|
274
|
+
"tel",
|
|
275
|
+
"url"
|
|
276
|
+
].includes(type) ? type : "text";
|
|
277
|
+
}
|
|
278
|
+
function FormControls({ requestId, fields, values, onChange }) {
|
|
279
|
+
return /* @__PURE__ */ jsx("div", {
|
|
280
|
+
className: "space-y-3",
|
|
281
|
+
children: fields.map((field) => {
|
|
282
|
+
const id = `human-field-${requestId}-${field.name}`;
|
|
283
|
+
const label = `${field.label || field.name}${field.required ? " *" : ""}`;
|
|
284
|
+
const type = (field.type || "text").toLowerCase();
|
|
285
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
286
|
+
className: "space-y-1.5",
|
|
287
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
288
|
+
htmlFor: id,
|
|
289
|
+
children: label
|
|
290
|
+
}), type === "textarea" ? /* @__PURE__ */ jsx(Textarea, {
|
|
291
|
+
id,
|
|
292
|
+
value: values[field.name] ?? "",
|
|
293
|
+
placeholder: field.placeholder ?? void 0,
|
|
294
|
+
onChange: (event) => onChange(field.name, event.target.value)
|
|
295
|
+
}) : type === "dropdown" || type === "select" ? /* @__PURE__ */ jsxs("select", {
|
|
296
|
+
id,
|
|
297
|
+
className: "w-full rounded-md border border-border-secondary bg-background-primary px-3 py-2 text-sm text-text-primary",
|
|
298
|
+
value: values[field.name] ?? "",
|
|
299
|
+
onChange: (event) => onChange(field.name, event.target.value),
|
|
300
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
301
|
+
value: "",
|
|
302
|
+
children: "—"
|
|
303
|
+
}), (field.options ?? []).map((option) => /* @__PURE__ */ jsx("option", {
|
|
304
|
+
value: option,
|
|
305
|
+
children: option
|
|
306
|
+
}, option))]
|
|
307
|
+
}) : /* @__PURE__ */ jsx(Input, {
|
|
308
|
+
id,
|
|
309
|
+
type: inputType(field),
|
|
310
|
+
value: values[field.name] ?? "",
|
|
311
|
+
placeholder: field.placeholder ?? void 0,
|
|
312
|
+
onChange: (event) => onChange(field.name, event.target.value)
|
|
313
|
+
})]
|
|
314
|
+
}, field.name);
|
|
315
|
+
})
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function HumanRequestCard({ request, onAnswered }) {
|
|
319
|
+
const tr = useTr();
|
|
320
|
+
const { respond, loading } = useRespondToHumanRequest();
|
|
321
|
+
const [text, setText] = useState("");
|
|
322
|
+
const [values, setValues] = useState({});
|
|
323
|
+
const submit = async (answer) => {
|
|
324
|
+
try {
|
|
325
|
+
const decision = await respond(request.id, answer);
|
|
326
|
+
toast.success(decision === "declined" ? tr("fluidgrids.humanResponse.declinedToast", "Declined — the workflow is continuing") : tr("fluidgrids.humanResponse.answeredToast", "Response sent — the workflow is continuing"));
|
|
327
|
+
onAnswered();
|
|
328
|
+
} catch (err) {
|
|
329
|
+
toast.error(err instanceof Error ? err.message : tr("fluidgrids.humanResponse.failed", "Your response could not be recorded"));
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
const missingRequired = request.responseType === "CUSTOM_FORM" && request.fields.some((field) => field.required && !(values[field.name] ?? "").trim());
|
|
333
|
+
const emptyText = request.responseType === "FREE_TEXT" && !text.trim();
|
|
334
|
+
const recipient = useRecipientLabel(request, request.channel === "IN_APP" ? tr("fluidgrids.humanResponse.anyMember", "any workspace member") : "");
|
|
335
|
+
return /* @__PURE__ */ jsxs(Card, {
|
|
336
|
+
className: "border border-border-secondary bg-background-secondary",
|
|
337
|
+
"data-testid": "human-request-card",
|
|
338
|
+
"data-request-id": request.id,
|
|
339
|
+
children: [/* @__PURE__ */ jsxs(CardHeader, {
|
|
340
|
+
className: "pb-3",
|
|
341
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
342
|
+
className: "flex flex-wrap items-center justify-between gap-2",
|
|
343
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
344
|
+
className: "flex min-w-0 items-center gap-2",
|
|
345
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
346
|
+
icon: CHANNEL_ICON[request.channel] ?? "mdi:account-question",
|
|
347
|
+
width: 18,
|
|
348
|
+
className: "shrink-0 text-text-secondary"
|
|
349
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
350
|
+
className: "truncate text-sm font-semibold text-text-primary",
|
|
351
|
+
children: request.title
|
|
352
|
+
})]
|
|
353
|
+
}), /* @__PURE__ */ jsx(Badge, {
|
|
354
|
+
variant: "outline",
|
|
355
|
+
className: "font-mono text-[10px]",
|
|
356
|
+
children: request.workflowKey
|
|
357
|
+
})]
|
|
358
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
359
|
+
className: "text-xs text-text-secondary",
|
|
360
|
+
children: [tr("fluidgrids.humanResponse.askedVia", "Asked {{recipient}} via {{channel}}", {
|
|
361
|
+
recipient,
|
|
362
|
+
channel: channelLabel(request.channel, tr)
|
|
363
|
+
}), request.expiresAt && ` · ${tr("fluidgrids.humanResponse.until", "until {{time}}", { time: new Date(request.expiresAt).toLocaleString() })}`]
|
|
364
|
+
})]
|
|
365
|
+
}), /* @__PURE__ */ jsxs(CardContent, {
|
|
366
|
+
className: "space-y-3",
|
|
367
|
+
children: [
|
|
368
|
+
request.message && /* @__PURE__ */ jsx("p", {
|
|
369
|
+
className: "whitespace-pre-wrap break-words text-sm text-text-primary",
|
|
370
|
+
children: request.message
|
|
371
|
+
}),
|
|
372
|
+
!request.canRespond && /* @__PURE__ */ jsx("p", {
|
|
373
|
+
className: "text-xs text-text-secondary",
|
|
374
|
+
children: request.channel === "IN_APP" ? tr("fluidgrids.humanResponse.notForYou", "This request was sent to someone else.") : tr("fluidgrids.humanResponse.answerByLink", "Answered through the link in the {{channel}} message.", { channel: channelLabel(request.channel, tr) })
|
|
375
|
+
}),
|
|
376
|
+
request.canRespond && request.responseType === "FREE_TEXT" && /* @__PURE__ */ jsxs("div", {
|
|
377
|
+
className: "space-y-1.5",
|
|
378
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
379
|
+
htmlFor: `human-text-${request.id}`,
|
|
380
|
+
children: tr("fluidgrids.humanResponse.yourResponse", "Your response")
|
|
381
|
+
}), /* @__PURE__ */ jsx(Textarea, {
|
|
382
|
+
id: `human-text-${request.id}`,
|
|
383
|
+
value: text,
|
|
384
|
+
onChange: (event) => setText(event.target.value)
|
|
385
|
+
})]
|
|
386
|
+
}),
|
|
387
|
+
request.canRespond && request.responseType === "CUSTOM_FORM" && /* @__PURE__ */ jsx(FormControls, {
|
|
388
|
+
requestId: request.id,
|
|
389
|
+
fields: request.fields,
|
|
390
|
+
values,
|
|
391
|
+
onChange: (name, value) => setValues((current) => ({
|
|
392
|
+
...current,
|
|
393
|
+
[name]: value
|
|
394
|
+
}))
|
|
395
|
+
}),
|
|
396
|
+
request.canRespond && /* @__PURE__ */ jsx("div", {
|
|
397
|
+
className: "flex flex-wrap items-center gap-2 pt-1",
|
|
398
|
+
children: request.responseType === "APPROVAL" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Button$1, {
|
|
399
|
+
"data-agent": "confirm",
|
|
400
|
+
size: "sm",
|
|
401
|
+
disabled: loading,
|
|
402
|
+
onClick: () => submit({ action: "approve" }),
|
|
403
|
+
"data-testid": "human-request-approve",
|
|
404
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
405
|
+
icon: "mdi:check",
|
|
406
|
+
className: "mr-1.5",
|
|
407
|
+
width: 16
|
|
408
|
+
}), request.approveLabel]
|
|
409
|
+
}), request.approvalType === "double" && /* @__PURE__ */ jsxs(Button$1, {
|
|
410
|
+
"data-agent": "confirm",
|
|
411
|
+
size: "sm",
|
|
412
|
+
variant: "destructive",
|
|
413
|
+
disabled: loading,
|
|
414
|
+
onClick: () => submit({ action: "decline" }),
|
|
415
|
+
"data-testid": "human-request-decline",
|
|
416
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
417
|
+
icon: "mdi:close",
|
|
418
|
+
className: "mr-1.5",
|
|
419
|
+
width: 16
|
|
420
|
+
}), request.declineLabel]
|
|
421
|
+
})] }) : /* @__PURE__ */ jsxs(Button$1, {
|
|
422
|
+
"data-agent": "confirm",
|
|
423
|
+
size: "sm",
|
|
424
|
+
disabled: loading || missingRequired || emptyText,
|
|
425
|
+
onClick: () => submit(request.responseType === "FREE_TEXT" ? { text } : { values }),
|
|
426
|
+
"data-testid": "human-request-submit",
|
|
427
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
428
|
+
icon: "mdi:send",
|
|
429
|
+
className: "mr-1.5",
|
|
430
|
+
width: 16
|
|
431
|
+
}), request.submitLabel]
|
|
432
|
+
})
|
|
433
|
+
})
|
|
434
|
+
]
|
|
435
|
+
})]
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export { useRunHumanRequests as a, useRespondToHumanRequest as i, channelLabel as n, useRecipientLabel as o, usePendingHumanRequests as r, HumanRequestCard as t };
|