@burdenoff/fluidgrids-fe-sdk 2026.1111.16 → 2026.1111.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +41 -0
  2. package/dist/AiStudioPage-BHVEU4Wp.js +774 -0
  3. package/dist/ApprovalInboxPage-dbBDSJ-f.js +558 -0
  4. package/dist/DataSinkDashboardPage-1h2QsMzH.js +270 -0
  5. package/dist/DataTablesPage-BZy2z0LB.js +1690 -0
  6. package/dist/EntityTagPicker-BqRNsLCQ.js +135 -0
  7. package/dist/{ExecutionDetailPage-D-0_POAf.js → ExecutionDetailPage-BdcZmLT4.js} +799 -4420
  8. package/dist/ExecutionDetailPage-DvQ667OE.d.ts +10 -0
  9. package/dist/ExecutionsListPage-BCUJoAIE.js +2742 -0
  10. package/dist/{FailureTriagePanel-BHiduL7A.js → FailureTriagePanel-B9hDQymi.js} +4 -97
  11. package/dist/{FluidGridsProvider-DmvFADFa.js → FluidGridsProvider-dvZSNtSW.js} +10 -350
  12. package/dist/HistoryPage-BLoRpJ1R.d.ts +6 -0
  13. package/dist/{HistoryPage-BbUgxx1I.js → HistoryPage-U0tqupBN.js} +6 -4
  14. package/dist/HumanRequestCard-D-ooVPDn.js +439 -0
  15. package/dist/{InsightsPage-s6f5bU-8.js → InsightsPage-CALo16eB.js} +17 -328
  16. package/dist/ManagedAiPolicyAuthority-BxwJ3ptd.js +40 -0
  17. package/dist/{ManagedAiPolicyAuthority-CGux_DaV.d.ts → ManagedAiPolicyAuthority-DM3t3wNr.d.ts} +1 -5
  18. package/dist/ManagedAiPolicyAuthorityContext-CNkxIOZ1.js +10 -0
  19. package/dist/{MissingNodesInstallModal-B-9gHy2K.js → MissingNodesInstallModal-CMybsI8h.js} +3 -2
  20. package/dist/{NodesPage-ClvhUfbh.js → NodesPage-Bf5PhfBK.js} +7 -3
  21. package/dist/NodesPage-DEbnz9iU.d.ts +6 -0
  22. package/dist/{PublishCustomNodeAction-D2F7oN0S.js → PublishCustomNodeAction-5XXnSA3H.js} +3 -45
  23. package/dist/QuotaExhaustedDialog-7IFSaLyi.js +54 -0
  24. package/dist/RunnerStatusPill-Ctg4TJcP.js +187 -0
  25. package/dist/RunnersPage-CLCOCclc.js +467 -0
  26. package/dist/{ManagedAiPolicyAuthority-DsSaz-vg.js → SettingsPage-CJLZvv4J.js} +743 -773
  27. package/dist/SettingsPage-DMHQFj5-.d.ts +9 -0
  28. package/dist/TagFilterSelect-DCBOH6Ou.js +41 -0
  29. package/dist/TaskQueuesPage-9VgSnLdL.js +951 -0
  30. package/dist/TemplateCard-DsC0KNH1.js +1416 -0
  31. package/dist/{TemplateConfigurationModal-D_Mvgxqa.js → TemplateConfigurationModal-BhGWIzpm.js} +3 -2
  32. package/dist/{TemplateDetailPanel-DfFfUcOe.js → TemplateDetailPanel-D4cUVr-m.js} +3 -3
  33. package/dist/TemplatePreview-FayF6jxh.js +231 -0
  34. package/dist/TemplatesPage-BG3H-6v6.d.ts +10 -0
  35. package/dist/{TemplatesPage-Cku13bav.js → TemplatesPage-CsDNT54N.js} +11 -228
  36. package/dist/VariablesPage-67WR_lVE.js +949 -0
  37. package/dist/{VersionDiffDialog-Dq2xlPFI.js → VersionDiffDialog-DSN0mU0f.js} +2 -1
  38. package/dist/WorkflowBuilder-CPnXEL5J.js +555 -0
  39. package/dist/{WorkflowSearchRedirect-CaoOOpEl.js → WorkflowBuilder-YXRysvZc.js} +19 -585
  40. package/dist/{WorkflowProposalCard-8Vr56kQa.js → WorkflowProposalCard-qrEeNyYG.js} +4 -405
  41. package/dist/WorkflowSearchRedirect-DwDNZ_q5.js +36 -0
  42. package/dist/WorkflowShareDialog-mbIwRQUY.js +168 -0
  43. package/dist/WorkflowsList-CcNDQge5.d.ts +6 -0
  44. package/dist/{WorkflowsList-B2j1Pehb.js → WorkflowsList-DiiGyZkT.js} +12 -6
  45. package/dist/agent-form-Bfvm32j1.js +83 -0
  46. package/dist/{agentTools-DJ5o2ekg.js → agentTools-8s_25Q_F.js} +3 -2
  47. package/dist/{ai-usage-D5p7ZeRR.js → ai-usage-glCwHyQz.js} +1 -1
  48. package/dist/api-BrCHB5oA.js +224 -0
  49. package/dist/api-C8e0iV-Q.js +55 -0
  50. package/dist/button-DG7y_FVJ.js +48 -0
  51. package/dist/{canvasNoteTools-D3zUdEeg.js → canvasNoteTools-B6pYARgU.js} +2 -2
  52. package/dist/client-C9XSzSXr.js +1147 -0
  53. package/dist/code-editor-field-9-GdVE_8.js +111 -0
  54. package/dist/{codec-client-DstqyzTN.js → codec-client-CJ95Rmqo.js} +1 -1
  55. package/dist/constants-B2lhWNoN.js +202 -0
  56. package/dist/constants-XdvUxeWK.d.ts +162 -0
  57. package/dist/dialog-Cjb4lB4D.js +105 -0
  58. package/dist/executions.d.ts +1 -1
  59. package/dist/executions.js +2 -1
  60. package/dist/feature-flags-DM5myWaD.js +296 -0
  61. package/dist/flags.d.ts +3 -0
  62. package/dist/flags.js +4 -0
  63. package/dist/{flow-builder-DEzT8TQu.js → flow-builder-CZDKRc6f.js} +662 -10716
  64. package/dist/{node-endpoint-configs-DHFGA5MQ.js → graphql-operation-types-kX-MMjgW.js} +1 -1194
  65. package/dist/{graphql-types-XcDLtFwU.js → graphql-types-CDKVWH2R.js} +2 -202
  66. package/dist/history.d.ts +1 -1
  67. package/dist/history.js +1 -1
  68. package/dist/{index-DRf25-D3.d.ts → index-6JR2vD9J.d.ts} +33 -215
  69. package/dist/index-72XFgvbn.d.ts +132 -0
  70. package/dist/index-BcdtKBM-.d.ts +56 -0
  71. package/dist/{index-B3BWqafN.d.ts → index-DjiFgEQl.d.ts} +5 -131
  72. package/dist/index.d.ts +13 -9
  73. package/dist/index.js +52 -5300
  74. package/dist/{node-detail-view-LNqeIqN-.js → node-detail-view-CV4ZRwqh.js} +13 -8
  75. package/dist/node-editor-tab-body-DU-dh35X.js +174 -0
  76. package/dist/node-endpoint-configs-B5bjNMDW.js +37 -0
  77. package/dist/node-registry-refresh-Dy0uRg9f.js +84 -0
  78. package/dist/nodes.d.ts +1 -1
  79. package/dist/nodes.js +1 -1
  80. package/dist/observability.d.ts +1 -1
  81. package/dist/observability.js +2 -1
  82. package/dist/popover-BJHvrFGR.js +472 -0
  83. package/dist/{quick-config-panel-D3ogsaFL.js → quick-config-panel-ws3pLvbt.js} +3 -2
  84. package/dist/rolldown-runtime-hDbaFkMy.js +18 -0
  85. package/dist/run-display-BycKYSU0.js +46 -0
  86. package/dist/run-list-links-ByYC0OlV.js +98 -0
  87. package/dist/schedule-panel-C0mfGQSD.js +7542 -0
  88. package/dist/settings.d.ts +2 -1
  89. package/dist/settings.js +2 -1
  90. package/dist/shared-CMfn30xT.js +196 -0
  91. package/dist/sidebar-node-editor-CR1PiZO-.js +177 -0
  92. package/dist/templates.d.ts +1 -1
  93. package/dist/templates.js +2 -1
  94. package/dist/theme-provider-B3mIuz2d.js +11 -0
  95. package/dist/{useAgentDefinitions-NFtLytDR.js → useAgentDefinitions-B0dam3_o.js} +3 -397
  96. package/dist/useConnections-Cs7LCWEM.js +401 -0
  97. package/dist/useDurableParity-CzUQMFJ3.js +415 -0
  98. package/dist/useEntityTags-DhnsZcTl.js +236 -0
  99. package/dist/{useExecutions-DAzeWHS6.js → useExecutions-9aLuOt1c.js} +4 -3
  100. package/dist/{useObservability-COCZ0hUS.js → useObservability-CfEw3ZFD.js} +2 -2
  101. package/dist/{useRunHistory-CsYUbouO.js → useRunHistory-B1xJDwl1.js} +4 -3
  102. package/dist/{WorkflowShareDialog-6RRDe-_v.js → useWorkflowCodeExport-Bu2QYJzA.js} +7 -210
  103. package/dist/{useWorkflows-w4Dx2fF2.js → useWorkflows-BlVVu8NM.js} +3 -2
  104. package/dist/utils-uaL5UVxb.js +65 -0
  105. package/dist/workflow-builder.d.ts +1 -1
  106. package/dist/workflow-builder.js +4 -2
  107. package/dist/workflow-host.d.ts +6 -0
  108. package/dist/workflow-host.js +9 -0
  109. package/dist/workflow-pages.d.ts +55 -0
  110. package/dist/workflow-pages.js +113 -0
  111. package/dist/workflows-list.d.ts +1 -1
  112. package/dist/workflows-list.js +1 -1
  113. package/package.json +13 -1
  114. package/dist/ExecutionDetailPage-MvsnOnE1.d.ts +0 -4
  115. package/dist/HistoryPage-BqUizaFv.d.ts +0 -3
  116. package/dist/NodesPage-C97-9K2Y.d.ts +0 -3
  117. package/dist/TemplatesPage-D0qnXzHh.d.ts +0 -4
  118. package/dist/WorkflowsList-0d5GSKdh.d.ts +0 -3
  119. 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 };