@burdenoff/fluidgrids-fe-sdk 2026.1111.10 → 2026.1111.12
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/{ExecutionDetailPage-6ddRdNUR.js → ExecutionDetailPage-CDkJeeu2.js} +9 -10
- package/dist/{FailureTriagePanel-CR1BzOrM.js → FailureTriagePanel-BHiduL7A.js} +1 -1
- package/dist/{FluidGridsProvider-BFdGQ1g_.js → FluidGridsProvider-CDZjKmfD.js} +14 -3
- package/dist/{HistoryPage-DFiyqbvw.js → HistoryPage-DnVGN_72.js} +3 -3
- package/dist/{InsightsPage-CXCNQqmV.js → InsightsPage-CGRpZtGu.js} +4 -4
- package/dist/{ManagedAiPolicyAuthority-G1Efbc49.js → ManagedAiPolicyAuthority-DbJBledZ.js} +6 -6
- package/dist/{MissingNodesInstallModal-C-nsV4W7.js → MissingNodesInstallModal-YI57ImRx.js} +2 -2
- package/dist/{NodesPage-C4KtAasX.js → NodesPage-ClvhUfbh.js} +2 -2
- package/dist/{PublishCustomNodeAction-C3fEmWnC.js → PublishCustomNodeAction-D2F7oN0S.js} +1 -1
- package/dist/{TemplateConfigurationModal-CDJFVAho.js → TemplateConfigurationModal-BimyvVH6.js} +2 -2
- package/dist/{TemplateDetailPanel-BU6SzQy0.js → TemplateDetailPanel-CkwB6vrW.js} +2 -2
- package/dist/{TemplatesPage-CXMJntN7.js → TemplatesPage-ZK6cMWur.js} +113 -504
- package/dist/{VersionDiffDialog-ExN-CN6f.js → VersionDiffDialog-Dq2xlPFI.js} +1 -1
- package/dist/{WorkflowProposalCard-DuXQI7UY.js → WorkflowProposalCard-8Vr56kQa.js} +1 -1
- package/dist/{WorkflowSearchRedirect-xe0qc4A3.js → WorkflowSearchRedirect-BHtdF64q.js} +10 -11
- package/dist/{WorkflowShareDialog-FTeBgyb-.js → WorkflowShareDialog-ByTSWwwR.js} +2 -2
- package/dist/{WorkflowsList-DUFwerL1.js → WorkflowsList-B9F2e8M6.js} +5 -6
- package/dist/{agent-form-CzG3vX_q.js → agent-form-BvMWhQ42.js} +1 -1
- package/dist/{agentTools-dDrEjdbB.js → agentTools-Dgrdql-W.js} +2 -2
- package/dist/{codec-client-DiFkc0yn.js → codec-client-DstqyzTN.js} +1 -1
- package/dist/executions.js +1 -1
- package/dist/{flow-builder-vz253WZe.js → flow-builder-CukxgO65.js} +2857 -2555
- package/dist/history.js +1 -1
- package/dist/index.js +23 -23
- package/dist/{node-detail-view-DlbE4ob6.js → node-detail-view-DRk8Ln5L.js} +6 -6
- package/dist/{node-endpoint-configs-DVo9nUOe.js → node-endpoint-configs-DHFGA5MQ.js} +17 -3
- package/dist/nodes.js +1 -1
- package/dist/observability.js +1 -1
- package/dist/{quick-config-panel-DxNYFw66.js → quick-config-panel-40_1utiL.js} +2 -2
- package/dist/settings.js +1 -1
- package/dist/templates.js +1 -1
- package/dist/{useAgentDefinitions-BVTONKpB.js → useAgentDefinitions-NFtLytDR.js} +1 -1
- package/dist/{useExecutions-BeY3w5oZ.js → useExecutions-BqoD7XWi.js} +2 -2
- package/dist/{useObservability-BniBi0zw.js → useObservability-Cs-5_NHh.js} +2 -2
- package/dist/{useRunHistory-C47nRjSE.js → useRunHistory-CEL-CIz-.js} +2 -2
- package/dist/{useWorkflows-BtZBdTmp.js → useWorkflows-Bwpvfdgv.js} +2 -2
- package/dist/workflow-builder.js +2 -2
- package/dist/workflows-list.js +1 -1
- package/package.json +1 -1
- package/dist/rolldown-runtime-hDbaFkMy.js +0 -18
- package/dist/template-input-applicator-C0p6hE4K.js +0 -84
|
@@ -1,382 +1,27 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import { Dn as isQuotaExhaustedFailure, E as MyWorkflowTemplateSubmissionsDocument, O as SubmitWorkflowTemplateDocument, Sn as BILLING_PLANS_PATH,
|
|
2
|
+
import { Bi as describeOperationError, Ui as throwIfResultError } from "./node-endpoint-configs-DHFGA5MQ.js";
|
|
3
|
+
import { Dn as isQuotaExhaustedFailure, E as MyWorkflowTemplateSubmissionsDocument, O as SubmitWorkflowTemplateDocument, Sn as BILLING_PLANS_PATH, hn as emitTemplateUsed, j as WithdrawWorkflowTemplateSubmissionDocument, ln as useFluidGridsAnalytics } from "./FluidGridsProvider-CDZjKmfD.js";
|
|
4
4
|
import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
|
|
5
|
-
import {
|
|
6
|
-
import { h as useWorkflowDefinitions, l as useLazyWorkflowDefinition, s as useDuplicateWorkflow } from "./useWorkflows-
|
|
7
|
-
import { k as QuotaExhaustedDialog } from "./WorkflowShareDialog-
|
|
5
|
+
import { I as useImportWorkflowTemplate, L as useWorkflowTemplates, N as useTemplateImportFlow, Y as useGlobalStoreClient, t as FlowBuilderPage, v as TemplateCard } from "./flow-builder-CukxgO65.js";
|
|
6
|
+
import { h as useWorkflowDefinitions, l as useLazyWorkflowDefinition, s as useDuplicateWorkflow } from "./useWorkflows-Bwpvfdgv.js";
|
|
7
|
+
import { k as QuotaExhaustedDialog } from "./WorkflowShareDialog-ByTSWwwR.js";
|
|
8
8
|
import { YF as WorkflowStatus, hI as FLUIDGRIDS_PERMISSIONS, vI as SdkPermissionButton } from "./graphql-types-XcDLtFwU.js";
|
|
9
9
|
import { n as useFluidGridsPageControls } from "./pageControlsBridge-0pX9s-3k.js";
|
|
10
10
|
import { r as templatesControls } from "./pageControlSpecs-DZJ9K37m.js";
|
|
11
11
|
import { t as FluidGridsDocsLink } from "./FluidGridsDocsLink-JxkBpOeb.js";
|
|
12
|
-
import {
|
|
13
|
-
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
12
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
14
13
|
import { useNavigate, useParams } from "react-router";
|
|
15
14
|
import { useQuery } from "@apollo/client/react";
|
|
16
|
-
import { gql } from "@apollo/client/core";
|
|
17
15
|
import { Icon } from "@iconify/react";
|
|
18
16
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
19
17
|
import { toast } from "sonner";
|
|
20
18
|
import { Badge, Card, CardHeader, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Skeleton, Textarea } from "@burdenoff/fe-libs/ui";
|
|
21
19
|
import { ErrorBoundary } from "@burdenoff/fe-libs/shared/components";
|
|
22
|
-
import { useEventBus } from "@burdenoff/fe-libs/shared/events";
|
|
23
20
|
import { Button as Button$1 } from "@burdenoff/fe-libs/button";
|
|
24
21
|
import { v4 } from "uuid";
|
|
25
22
|
import { ThemeProvider } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
26
23
|
import "@xyflow/react/dist/style.css";
|
|
27
24
|
|
|
28
|
-
// src/hooks/store/useTemplateDetail.ts
|
|
29
|
-
const GET_WORKFLOW_TEMPLATE_DETAIL = gql`
|
|
30
|
-
query GetWorkflowTemplateDetail($id: ID!) {
|
|
31
|
-
product(id: $id) {
|
|
32
|
-
id
|
|
33
|
-
name
|
|
34
|
-
slug
|
|
35
|
-
type
|
|
36
|
-
description
|
|
37
|
-
longDescription
|
|
38
|
-
icon
|
|
39
|
-
screenshots
|
|
40
|
-
videoUrls
|
|
41
|
-
rating
|
|
42
|
-
reviewCount
|
|
43
|
-
downloads
|
|
44
|
-
viewCount
|
|
45
|
-
price
|
|
46
|
-
pricingModel
|
|
47
|
-
currency
|
|
48
|
-
featured
|
|
49
|
-
content
|
|
50
|
-
manifest
|
|
51
|
-
manifestVersion
|
|
52
|
-
authorName
|
|
53
|
-
license
|
|
54
|
-
category
|
|
55
|
-
tags
|
|
56
|
-
compatibleProducts
|
|
57
|
-
publisher {
|
|
58
|
-
id
|
|
59
|
-
name
|
|
60
|
-
email
|
|
61
|
-
websiteUrl
|
|
62
|
-
logoUrl
|
|
63
|
-
isVerified
|
|
64
|
-
}
|
|
65
|
-
createdAt
|
|
66
|
-
updatedAt
|
|
67
|
-
publishedAt
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
`;
|
|
71
|
-
/**
|
|
72
|
-
* Hook to lazily fetch full details of a workflow template from the store.
|
|
73
|
-
*
|
|
74
|
-
* Unlike `useWorkflowTemplates` which fetches browse-level metadata,
|
|
75
|
-
* this hook fetches the full product detail including the `content` field
|
|
76
|
-
* which contains the workflow definitionJson.
|
|
77
|
-
*
|
|
78
|
-
* Uses a manual query (not useQuery) to only fetch when explicitly requested.
|
|
79
|
-
*/
|
|
80
|
-
function useTemplateDetail() {
|
|
81
|
-
const client = useGlobalStoreClient();
|
|
82
|
-
const [loading, setLoading] = useState(false);
|
|
83
|
-
const [error, setError] = useState(null);
|
|
84
|
-
const context = useContext(FluidGridsContext);
|
|
85
|
-
const workspaceId = context?.workspaceId ?? getFluidGridsConfig().workspaceId;
|
|
86
|
-
const organizationId = context?.organizationId;
|
|
87
|
-
const contextHeaders = useMemo(() => {
|
|
88
|
-
const headers = {};
|
|
89
|
-
if (organizationId) headers["x-organization-id"] = organizationId;
|
|
90
|
-
if (workspaceId) headers["x-workspace-id"] = workspaceId;
|
|
91
|
-
return headers;
|
|
92
|
-
}, [organizationId, workspaceId]);
|
|
93
|
-
return {
|
|
94
|
-
fetchTemplateDetail: useCallback(async (templateId) => {
|
|
95
|
-
setLoading(true);
|
|
96
|
-
setError(null);
|
|
97
|
-
try {
|
|
98
|
-
const result = await client.query({
|
|
99
|
-
query: GET_WORKFLOW_TEMPLATE_DETAIL,
|
|
100
|
-
variables: { id: templateId },
|
|
101
|
-
fetchPolicy: "network-only",
|
|
102
|
-
context: Object.keys(contextHeaders).length > 0 ? { headers: contextHeaders } : void 0
|
|
103
|
-
});
|
|
104
|
-
if (result.error) throw result.error;
|
|
105
|
-
return result.data?.product ?? null;
|
|
106
|
-
} catch (err) {
|
|
107
|
-
const fetchError = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to fetch template detail");
|
|
108
|
-
setError(fetchError);
|
|
109
|
-
console.error("[useTemplateDetail] Error:", err);
|
|
110
|
-
return null;
|
|
111
|
-
} finally {
|
|
112
|
-
setLoading(false);
|
|
113
|
-
}
|
|
114
|
-
}, [client, contextHeaders]),
|
|
115
|
-
loading,
|
|
116
|
-
error
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// src/hooks/store/useImportWorkflowTemplate.ts
|
|
121
|
-
const IMPORT_WORKFLOW_TEMPLATE = gql`
|
|
122
|
-
mutation ImportWorkflowTemplate($input: ImportWorkflowTemplateInput!) {
|
|
123
|
-
importWorkflowTemplate(input: $input) {
|
|
124
|
-
success
|
|
125
|
-
message
|
|
126
|
-
workflow {
|
|
127
|
-
id
|
|
128
|
-
workflowKey
|
|
129
|
-
version
|
|
130
|
-
name
|
|
131
|
-
description
|
|
132
|
-
status
|
|
133
|
-
definitionJson
|
|
134
|
-
tags
|
|
135
|
-
category
|
|
136
|
-
icon
|
|
137
|
-
createdAt
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
`;
|
|
142
|
-
const RECORD_DOWNLOAD = gql`
|
|
143
|
-
mutation RecordDownload($productId: String!) {
|
|
144
|
-
recordDownload(productId: $productId) {
|
|
145
|
-
id
|
|
146
|
-
downloadedAt
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
`;
|
|
150
|
-
const NODE_ENDPOINT_CONFIGS = gql`
|
|
151
|
-
query NodeEndpointConfigsForTemplateCheck {
|
|
152
|
-
nodeEndpointConfigs(pagination: { limit: 500, offset: 0 }) {
|
|
153
|
-
items {
|
|
154
|
-
nodeType
|
|
155
|
-
nodeVersion
|
|
156
|
-
publishType
|
|
157
|
-
status
|
|
158
|
-
}
|
|
159
|
-
total
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
`;
|
|
163
|
-
const WORKFLOW_DEFINITIONS = gql`
|
|
164
|
-
query WorkflowDefinitions($input: ListWorkflowDefinitionsInput!) {
|
|
165
|
-
workflowDefinitions(input: $input) {
|
|
166
|
-
items {
|
|
167
|
-
id
|
|
168
|
-
workflowKey
|
|
169
|
-
name
|
|
170
|
-
version
|
|
171
|
-
status
|
|
172
|
-
}
|
|
173
|
-
total
|
|
174
|
-
hasMore
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
`;
|
|
178
|
-
/**
|
|
179
|
-
* Hook to orchestrate importing a workflow template from the store.
|
|
180
|
-
*
|
|
181
|
-
* Frontend-orchestrated flow:
|
|
182
|
-
* 1. Fetch full template detail from global store (lazy load content)
|
|
183
|
-
* 2. Check which nodes are missing from the workspace
|
|
184
|
-
* 3. Auto-install free/builtin missing nodes (with user confirmation)
|
|
185
|
-
* 4. Apply user-provided template inputs to definitionJson
|
|
186
|
-
* 5. Create workflow via importWorkflowTemplate mutation (workspace gateway)
|
|
187
|
-
* 6. Record download in store (global gateway)
|
|
188
|
-
*/
|
|
189
|
-
function useImportWorkflowTemplate() {
|
|
190
|
-
const [importing, setImporting] = useState(false);
|
|
191
|
-
const [error, setError] = useState(null);
|
|
192
|
-
const { fetchTemplateDetail, loading: fetchingDetail } = useTemplateDetail();
|
|
193
|
-
const { installNode } = useInstallNode();
|
|
194
|
-
const globalClient = useGlobalStoreClient();
|
|
195
|
-
const context = useContext(FluidGridsContext);
|
|
196
|
-
const workspaceId = context?.workspaceId ?? getFluidGridsConfig().workspaceId;
|
|
197
|
-
const bus = useEventBus();
|
|
198
|
-
const getTemplateDetail = useCallback(async (templateId) => {
|
|
199
|
-
return fetchTemplateDetail(templateId);
|
|
200
|
-
}, [fetchTemplateDetail]);
|
|
201
|
-
/**
|
|
202
|
-
* Parse the template content from a StoreProduct.
|
|
203
|
-
* Returns null if the product doesn't have valid workflow template content.
|
|
204
|
-
*/
|
|
205
|
-
const parseTemplateContent = useCallback((template) => {
|
|
206
|
-
try {
|
|
207
|
-
const content = template.content;
|
|
208
|
-
if (!content?.definitionJson) {
|
|
209
|
-
console.warn("[useImportWorkflowTemplate] Template has no definitionJson in content:", template.id);
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
return content;
|
|
213
|
-
} catch {
|
|
214
|
-
console.error("[useImportWorkflowTemplate] Failed to parse template content:", template.id);
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
}, []);
|
|
218
|
-
return {
|
|
219
|
-
/** Fetch full template content from store (lazy load) */
|
|
220
|
-
getTemplateDetail,
|
|
221
|
-
/** Parse WorkflowTemplateContent from a StoreProduct */
|
|
222
|
-
parseTemplateContent,
|
|
223
|
-
/** Check which template nodes are missing from workspace */
|
|
224
|
-
checkMissingNodes: useCallback(async (requiredNodeTypes) => {
|
|
225
|
-
if (!requiredNodeTypes || requiredNodeTypes.length === 0) return {
|
|
226
|
-
installed: [],
|
|
227
|
-
missing: [],
|
|
228
|
-
missingPaid: [],
|
|
229
|
-
canImport: true
|
|
230
|
-
};
|
|
231
|
-
try {
|
|
232
|
-
const data = (await getFluidGridsClient().query({
|
|
233
|
-
query: NODE_ENDPOINT_CONFIGS,
|
|
234
|
-
fetchPolicy: "network-only"
|
|
235
|
-
})).data;
|
|
236
|
-
const installedNodeTypes = new Set((data?.nodeEndpointConfigs?.items || []).map((n) => n.nodeType));
|
|
237
|
-
const installed = [];
|
|
238
|
-
const missing = [];
|
|
239
|
-
const missingPaid = [];
|
|
240
|
-
for (const node of requiredNodeTypes) if (installedNodeTypes.has(node.nodeType) || node.publishType === "BUILTIN") installed.push(node);
|
|
241
|
-
else if (node.publishType === "STORE" && node.storeProductId) missingPaid.push(node);
|
|
242
|
-
else missing.push(node);
|
|
243
|
-
return {
|
|
244
|
-
installed,
|
|
245
|
-
missing,
|
|
246
|
-
missingPaid,
|
|
247
|
-
canImport: missingPaid.length === 0
|
|
248
|
-
};
|
|
249
|
-
} catch (err) {
|
|
250
|
-
console.error("[useImportWorkflowTemplate] Failed to check node dependencies:", err);
|
|
251
|
-
return {
|
|
252
|
-
installed: requiredNodeTypes,
|
|
253
|
-
missing: [],
|
|
254
|
-
missingPaid: [],
|
|
255
|
-
canImport: true
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
}, []),
|
|
259
|
-
/** Install missing free nodes */
|
|
260
|
-
installMissingNodes: useCallback(async (nodes) => {
|
|
261
|
-
const installed = [];
|
|
262
|
-
for (const node of nodes) try {
|
|
263
|
-
if ((await installNode(node)).success) installed.push(node.name);
|
|
264
|
-
} catch (err) {
|
|
265
|
-
console.warn(`[useImportWorkflowTemplate] Failed to install node: ${node.name}`, err);
|
|
266
|
-
}
|
|
267
|
-
return installed;
|
|
268
|
-
}, [installNode]),
|
|
269
|
-
/** Full import: apply inputs -> create workflow -> record analytics */
|
|
270
|
-
importTemplate: useCallback(async (template, options) => {
|
|
271
|
-
setImporting(true);
|
|
272
|
-
setError(null);
|
|
273
|
-
try {
|
|
274
|
-
const content = parseTemplateContent(template);
|
|
275
|
-
if (!content) throw new Error("Template does not contain valid workflow definition data");
|
|
276
|
-
let definitionJson = deepClone(content.definitionJson);
|
|
277
|
-
if (options?.templateInputs) definitionJson = applyTemplateInputs(definitionJson, options.templateInputs);
|
|
278
|
-
const config = getFluidGridsConfig();
|
|
279
|
-
const importContext = {
|
|
280
|
-
...options?.context || {},
|
|
281
|
-
...config.productId ? { productId: config.productId } : {},
|
|
282
|
-
...config.productName ? { productName: config.productName } : {}
|
|
283
|
-
};
|
|
284
|
-
const client = getFluidGridsClient();
|
|
285
|
-
const importResult = throwIfResultError(await client.mutate({
|
|
286
|
-
mutation: IMPORT_WORKFLOW_TEMPLATE,
|
|
287
|
-
variables: { input: {
|
|
288
|
-
definitionJson,
|
|
289
|
-
name: options?.name || content.defaultWorkflowName || template.name,
|
|
290
|
-
description: options?.description || template.description,
|
|
291
|
-
sourceTemplateId: template.id,
|
|
292
|
-
sourceTemplateName: template.name,
|
|
293
|
-
tags: content.defaultTags || template.tags || [],
|
|
294
|
-
category: content.defaultCategory || template.category || void 0,
|
|
295
|
-
icon: template.icon || void 0,
|
|
296
|
-
projectId: options?.projectId,
|
|
297
|
-
context: importContext
|
|
298
|
-
} },
|
|
299
|
-
refetchQueries: [WORKFLOW_DEFINITIONS]
|
|
300
|
-
})).data?.importWorkflowTemplate;
|
|
301
|
-
if (!importResult?.success || !importResult.workflow) throw new Error(importResult?.message || "Failed to create workflow from template");
|
|
302
|
-
try {
|
|
303
|
-
throwIfResultError(await globalClient.mutate({
|
|
304
|
-
mutation: RECORD_DOWNLOAD,
|
|
305
|
-
variables: { productId: template.id }
|
|
306
|
-
}));
|
|
307
|
-
} catch {
|
|
308
|
-
console.warn("[useImportWorkflowTemplate] Failed to record store download");
|
|
309
|
-
}
|
|
310
|
-
try {
|
|
311
|
-
throwIfResultError(await globalClient.mutate({
|
|
312
|
-
mutation: InstallWorkflowNodeDocument,
|
|
313
|
-
variables: { input: {
|
|
314
|
-
productId: template.id,
|
|
315
|
-
workspaceId,
|
|
316
|
-
organizationId: context?.tenantId ?? getFluidGridsConfig().tenantId,
|
|
317
|
-
version: template.manifestVersion || "1.0.0",
|
|
318
|
-
manifest: template.manifest,
|
|
319
|
-
metadata: {
|
|
320
|
-
type: "WORKFLOW",
|
|
321
|
-
sourceTemplateId: template.id,
|
|
322
|
-
sourceTemplateName: template.name,
|
|
323
|
-
importedWorkflowKey: importResult.workflow.workflowKey,
|
|
324
|
-
importedWorkflowVersion: importResult.workflow.version
|
|
325
|
-
}
|
|
326
|
-
} }
|
|
327
|
-
}));
|
|
328
|
-
} catch {
|
|
329
|
-
console.warn("[useImportWorkflowTemplate] Failed to record app installation");
|
|
330
|
-
}
|
|
331
|
-
emitWorkflowCreated(bus, {
|
|
332
|
-
workspaceId,
|
|
333
|
-
workflowId: importResult.workflow.id,
|
|
334
|
-
source: "template",
|
|
335
|
-
definition: definitionJson
|
|
336
|
-
});
|
|
337
|
-
emitTemplateUsed(bus, {
|
|
338
|
-
workspaceId,
|
|
339
|
-
templateId: template.id,
|
|
340
|
-
category: content.defaultCategory || template.category
|
|
341
|
-
});
|
|
342
|
-
return {
|
|
343
|
-
success: true,
|
|
344
|
-
workflow: importResult.workflow,
|
|
345
|
-
message: importResult.message || "Template imported successfully",
|
|
346
|
-
installedNodes: [],
|
|
347
|
-
requiredCredentials: content.requiredCredentialTypes || []
|
|
348
|
-
};
|
|
349
|
-
} catch (err) {
|
|
350
|
-
const importError = err instanceof Error ? err : /* @__PURE__ */ new Error("Template import failed");
|
|
351
|
-
setError(importError);
|
|
352
|
-
console.error("[useImportWorkflowTemplate] Import failed:", err);
|
|
353
|
-
return {
|
|
354
|
-
success: false,
|
|
355
|
-
workflow: null,
|
|
356
|
-
message: importError.message,
|
|
357
|
-
installedNodes: [],
|
|
358
|
-
requiredCredentials: [],
|
|
359
|
-
error: importError
|
|
360
|
-
};
|
|
361
|
-
} finally {
|
|
362
|
-
setImporting(false);
|
|
363
|
-
}
|
|
364
|
-
}, [
|
|
365
|
-
parseTemplateContent,
|
|
366
|
-
globalClient,
|
|
367
|
-
workspaceId,
|
|
368
|
-
context?.tenantId,
|
|
369
|
-
bus
|
|
370
|
-
]),
|
|
371
|
-
/** Whether we're fetching template detail */
|
|
372
|
-
fetchingDetail,
|
|
373
|
-
/** Whether we're importing */
|
|
374
|
-
importing,
|
|
375
|
-
/** Last error */
|
|
376
|
-
error
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
|
|
380
25
|
// src/hooks/store/usePublishToStore.ts
|
|
381
26
|
/** Categories a community template can be filed under (matches the store). */
|
|
382
27
|
const TEMPLATE_STORE_CATEGORIES = [
|
|
@@ -543,12 +188,11 @@ function TemplatePreview() {
|
|
|
543
188
|
const { productId } = useParams();
|
|
544
189
|
const navigate = useNavigate();
|
|
545
190
|
const tr = useTr();
|
|
546
|
-
const { getTemplateDetail, parseTemplateContent
|
|
191
|
+
const { getTemplateDetail, parseTemplateContent } = useImportWorkflowTemplate();
|
|
547
192
|
const [workflow, setWorkflow] = useState(null);
|
|
548
193
|
const [template, setTemplate] = useState(null);
|
|
549
194
|
const [loading, setLoading] = useState(true);
|
|
550
195
|
const [error, setError] = useState(null);
|
|
551
|
-
const [importing, setImporting] = useState(false);
|
|
552
196
|
const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);
|
|
553
197
|
const workflowsBase = window.location.pathname.replace(/\/templates(\/.*)?$/, "");
|
|
554
198
|
useEffect(() => {
|
|
@@ -612,122 +256,115 @@ function TemplatePreview() {
|
|
|
612
256
|
parseTemplateContent,
|
|
613
257
|
tr
|
|
614
258
|
]);
|
|
615
|
-
const
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
if (result.success && result.workflow) {
|
|
623
|
-
toast.success(tr("fluidgrids.pages.templatePreview.importSuccessToast", "Workflow created from template!"), { description: tr("fluidgrids.pages.templatePreview.importSuccessDescription", "\"{{name}}\" is ready to edit", { name: result.workflow.name }) });
|
|
624
|
-
navigate(`${workflowsBase}/workflow/${result.workflow.workflowKey}/${result.workflow.version}`);
|
|
625
|
-
} else if (isQuotaExhaustedFailure(result.error?.cause ?? result.error)) setShowQuotaExhausted(true);
|
|
626
|
-
else toast.error(tr("fluidgrids.pages.templatePreview.importErrorToast", "Failed to use template"), { description: result.message });
|
|
627
|
-
} catch (e) {
|
|
628
|
-
toast.dismiss("tpl-preview-import");
|
|
629
|
-
toast.error(tr("fluidgrids.pages.templatePreview.genericErrorToast", "Something went wrong"), { description: e instanceof Error ? e.message : tr("fluidgrids.pages.templatePreview.importErrorDescription", "Failed to import template") });
|
|
630
|
-
} finally {
|
|
631
|
-
setImporting(false);
|
|
632
|
-
}
|
|
259
|
+
const templateImport = useTemplateImportFlow({
|
|
260
|
+
onImported: (created) => navigate(`${workflowsBase}/workflow/${created.workflowKey}/${created.version}`),
|
|
261
|
+
onQuotaExhausted: () => setShowQuotaExhausted(true)
|
|
262
|
+
});
|
|
263
|
+
const importing = templateImport.busy;
|
|
264
|
+
const handleUse = () => {
|
|
265
|
+
if (template) templateImport.start(template);
|
|
633
266
|
};
|
|
634
267
|
const backToTemplates = () => navigate(`${workflowsBase}/templates`);
|
|
635
|
-
return /* @__PURE__ */ jsxs(ThemeProvider$1, { children: [
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
className: "flex h-14 shrink-0 items-center justify-between gap-3 border-b border-card-foreground/10 bg-gradient-to-r from-primary/40 to-transparent px-3",
|
|
268
|
+
return /* @__PURE__ */ jsxs(ThemeProvider$1, { children: [
|
|
269
|
+
/* @__PURE__ */ jsxs("div", {
|
|
270
|
+
className: "flex h-full w-full flex-col overflow-hidden",
|
|
639
271
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
640
|
-
className: "flex
|
|
641
|
-
children: [/* @__PURE__ */
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
className: "flex size-9 shrink-0 items-center justify-center rounded-md text-card-foreground transition-colors hover:bg-card-foreground/10",
|
|
646
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
647
|
-
icon: "ion:arrow-back-outline",
|
|
648
|
-
className: "size-5"
|
|
649
|
-
})
|
|
650
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
651
|
-
className: "min-w-0",
|
|
652
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
653
|
-
className: "flex items-center gap-2",
|
|
654
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
655
|
-
className: "truncate text-sm font-semibold text-card-foreground",
|
|
656
|
-
children: workflow?.name ?? template?.name ?? tr("fluidgrids.pages.templatePreview.fallbackHeaderTitle", "Template preview")
|
|
657
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
658
|
-
className: "shrink-0 rounded-full bg-card-foreground/10 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground",
|
|
659
|
-
children: tr("fluidgrids.pages.templatePreview.readOnlyBadge", "Preview · read-only")
|
|
660
|
-
})]
|
|
661
|
-
}), template?.description ? /* @__PURE__ */ jsx("span", {
|
|
662
|
-
className: "block truncate text-xs text-muted-foreground",
|
|
663
|
-
children: template.description
|
|
664
|
-
}) : null]
|
|
665
|
-
})]
|
|
666
|
-
}), /* @__PURE__ */ jsx(SdkPermissionButton, {
|
|
667
|
-
permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_CREATE,
|
|
668
|
-
deniedTooltip: tr("fluidgrids.templates.useDeniedTooltip", "You don't have permission to create workflows from templates"),
|
|
669
|
-
children: /* @__PURE__ */ jsxs(Button$1, {
|
|
670
|
-
onClick: handleUse,
|
|
671
|
-
disabled: importing || !template,
|
|
672
|
-
className: "shrink-0 gap-2",
|
|
673
|
-
children: [/* @__PURE__ */ jsx(Icon, {
|
|
674
|
-
icon: importing ? "svg-spinners:ring-resize" : "mdi:plus",
|
|
675
|
-
className: "size-4"
|
|
676
|
-
}), tr("fluidgrids.pages.templatePreview.useThisTemplate", "Use this template")]
|
|
677
|
-
})
|
|
678
|
-
})]
|
|
679
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
680
|
-
className: "relative min-h-0 w-full flex-1",
|
|
681
|
-
children: /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
682
|
-
fallback: () => /* @__PURE__ */ jsxs("div", {
|
|
683
|
-
className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center",
|
|
684
|
-
children: [/* @__PURE__ */ jsx("p", {
|
|
685
|
-
className: "text-sm text-muted-foreground",
|
|
686
|
-
children: tr("fluidgrids.pages.templatePreview.renderErrorMessage", "Something went wrong rendering this template preview.")
|
|
687
|
-
}), /* @__PURE__ */ jsx(Button$1, {
|
|
688
|
-
variant: "outline",
|
|
272
|
+
className: "flex h-14 shrink-0 items-center justify-between gap-3 border-b border-card-foreground/10 bg-gradient-to-r from-primary/40 to-transparent px-3",
|
|
273
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
274
|
+
className: "flex min-w-0 items-center gap-2",
|
|
275
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
276
|
+
type: "button",
|
|
689
277
|
onClick: backToTemplates,
|
|
690
|
-
|
|
278
|
+
title: tr("fluidgrids.pages.templatePreview.backTooltip", "Back to templates"),
|
|
279
|
+
className: "flex size-9 shrink-0 items-center justify-center rounded-md text-card-foreground transition-colors hover:bg-card-foreground/10",
|
|
280
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
281
|
+
icon: "ion:arrow-back-outline",
|
|
282
|
+
className: "size-5"
|
|
283
|
+
})
|
|
284
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
285
|
+
className: "min-w-0",
|
|
286
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
287
|
+
className: "flex items-center gap-2",
|
|
288
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
289
|
+
className: "truncate text-sm font-semibold text-card-foreground",
|
|
290
|
+
children: workflow?.name ?? template?.name ?? tr("fluidgrids.pages.templatePreview.fallbackHeaderTitle", "Template preview")
|
|
291
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
292
|
+
className: "shrink-0 rounded-full bg-card-foreground/10 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground",
|
|
293
|
+
children: tr("fluidgrids.pages.templatePreview.readOnlyBadge", "Preview · read-only")
|
|
294
|
+
})]
|
|
295
|
+
}), template?.description ? /* @__PURE__ */ jsx("span", {
|
|
296
|
+
className: "block truncate text-xs text-muted-foreground",
|
|
297
|
+
children: template.description
|
|
298
|
+
}) : null]
|
|
691
299
|
})]
|
|
692
|
-
}),
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
children:
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
300
|
+
}), /* @__PURE__ */ jsx(SdkPermissionButton, {
|
|
301
|
+
permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_CREATE,
|
|
302
|
+
deniedTooltip: tr("fluidgrids.templates.useDeniedTooltip", "You don't have permission to create workflows from templates"),
|
|
303
|
+
children: /* @__PURE__ */ jsxs(Button$1, {
|
|
304
|
+
onClick: handleUse,
|
|
305
|
+
disabled: importing || !template,
|
|
306
|
+
className: "shrink-0 gap-2",
|
|
307
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
308
|
+
icon: importing ? "svg-spinners:ring-resize" : "mdi:plus",
|
|
309
|
+
className: "size-4"
|
|
310
|
+
}), tr("fluidgrids.pages.templatePreview.useThisTemplate", "Use this template")]
|
|
311
|
+
})
|
|
312
|
+
})]
|
|
313
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
314
|
+
className: "relative min-h-0 w-full flex-1",
|
|
315
|
+
children: /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
316
|
+
fallback: () => /* @__PURE__ */ jsxs("div", {
|
|
317
|
+
className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center",
|
|
318
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
707
319
|
className: "text-sm text-muted-foreground",
|
|
708
|
-
children:
|
|
709
|
-
}),
|
|
710
|
-
/* @__PURE__ */ jsx(Button$1, {
|
|
320
|
+
children: tr("fluidgrids.pages.templatePreview.renderErrorMessage", "Something went wrong rendering this template preview.")
|
|
321
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
711
322
|
variant: "outline",
|
|
712
323
|
onClick: backToTemplates,
|
|
713
324
|
children: tr("fluidgrids.pages.templatePreview.backToTemplates", "Back to templates")
|
|
714
|
-
})
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
325
|
+
})]
|
|
326
|
+
}),
|
|
327
|
+
children: loading ? /* @__PURE__ */ jsxs("div", {
|
|
328
|
+
className: "flex h-full items-center justify-center gap-2 text-sm text-muted-foreground",
|
|
329
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
330
|
+
icon: "svg-spinners:ring-resize",
|
|
331
|
+
className: "size-4"
|
|
332
|
+
}), tr("fluidgrids.pages.templatePreview.loadingPreview", "Loading preview…")]
|
|
333
|
+
}) : error ? /* @__PURE__ */ jsxs("div", {
|
|
334
|
+
className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center",
|
|
335
|
+
children: [
|
|
336
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
337
|
+
icon: "mdi:alert-circle-outline",
|
|
338
|
+
className: "size-8 text-muted-foreground"
|
|
339
|
+
}),
|
|
340
|
+
/* @__PURE__ */ jsx("p", {
|
|
341
|
+
className: "text-sm text-muted-foreground",
|
|
342
|
+
children: error
|
|
343
|
+
}),
|
|
344
|
+
/* @__PURE__ */ jsx(Button$1, {
|
|
345
|
+
variant: "outline",
|
|
346
|
+
onClick: backToTemplates,
|
|
347
|
+
children: tr("fluidgrids.pages.templatePreview.backToTemplates", "Back to templates")
|
|
348
|
+
})
|
|
349
|
+
]
|
|
350
|
+
}) : workflow ? /* @__PURE__ */ jsx(FlowBuilderPage, {
|
|
351
|
+
workflow,
|
|
352
|
+
readOnly: true
|
|
353
|
+
}) : null
|
|
354
|
+
})
|
|
355
|
+
})]
|
|
356
|
+
}),
|
|
357
|
+
templateImport.dialog,
|
|
358
|
+
/* @__PURE__ */ jsx(QuotaExhaustedDialog, {
|
|
359
|
+
surface: "template-import",
|
|
360
|
+
open: showQuotaExhausted,
|
|
361
|
+
onOpenChange: setShowQuotaExhausted,
|
|
362
|
+
onUpgrade: () => {
|
|
363
|
+
setShowQuotaExhausted(false);
|
|
364
|
+
navigate(BILLING_PLANS_PATH);
|
|
365
|
+
}
|
|
366
|
+
})
|
|
367
|
+
] });
|
|
731
368
|
}
|
|
732
369
|
|
|
733
370
|
// src/modules/templates/utils/template-categories.ts
|
|
@@ -1544,7 +1181,6 @@ function TemplatesPage() {
|
|
|
1544
1181
|
setResubmitTarget(null);
|
|
1545
1182
|
setActiveSource("submissions");
|
|
1546
1183
|
}, [tr]);
|
|
1547
|
-
const { getTemplateDetail, parseTemplateContent, importTemplate } = useImportWorkflowTemplate();
|
|
1548
1184
|
const categorizedWorkflows = useMemo(() => {
|
|
1549
1185
|
return workflows.map((w) => ({
|
|
1550
1186
|
workflow: w,
|
|
@@ -1600,39 +1236,11 @@ function TemplatesPage() {
|
|
|
1600
1236
|
bus,
|
|
1601
1237
|
workspaceId
|
|
1602
1238
|
]);
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
if (!fullTemplate) {
|
|
1609
|
-
toast.error(tr("fluidgrids.templates.templatesPage.storeLoadDetailErrorToast", "Failed to load template details"));
|
|
1610
|
-
return;
|
|
1611
|
-
}
|
|
1612
|
-
if (!parseTemplateContent(fullTemplate)) {
|
|
1613
|
-
toast.error(tr("fluidgrids.templates.templatesPage.storeInvalidContentToast", "Template does not contain valid workflow data"));
|
|
1614
|
-
return;
|
|
1615
|
-
}
|
|
1616
|
-
toast.loading(tr("fluidgrids.templates.templatesPage.storeImportingToast", "Importing template..."), { id: "store-template-import" });
|
|
1617
|
-
const result = await importTemplate(fullTemplate);
|
|
1618
|
-
toast.dismiss("store-template-import");
|
|
1619
|
-
if (result.success && result.workflow) {
|
|
1620
|
-
toast.success(tr("fluidgrids.templates.templatesPage.storeImportSuccessToast", "Workflow created from store template!"), { description: `"${result.workflow.name}" is ready to edit` });
|
|
1621
|
-
navigate(`../workflow/${result.workflow.workflowKey}/${result.workflow.version}`);
|
|
1622
|
-
} else if (isQuotaExhaustedFailure(result.error?.cause ?? result.error)) setShowQuotaExhausted(true);
|
|
1623
|
-
else toast.error(tr("fluidgrids.templates.templatesPage.storeImportErrorToast", "Failed to import template"), { description: result.message });
|
|
1624
|
-
} catch (err) {
|
|
1625
|
-
toast.dismiss("store-template-load");
|
|
1626
|
-
toast.dismiss("store-template-import");
|
|
1627
|
-
toast.error(tr("fluidgrids.templates.templatesPage.storeGenericErrorToast", "Something went wrong"), { description: err instanceof Error ? err.message : tr("fluidgrids.templates.templatesPage.storeProcessErrorDescription", "Failed to process template") });
|
|
1628
|
-
}
|
|
1629
|
-
}, [
|
|
1630
|
-
getTemplateDetail,
|
|
1631
|
-
parseTemplateContent,
|
|
1632
|
-
importTemplate,
|
|
1633
|
-
navigate,
|
|
1634
|
-
tr
|
|
1635
|
-
]);
|
|
1239
|
+
const templateImport = useTemplateImportFlow({
|
|
1240
|
+
onImported: (created) => navigate(`../workflow/${created.workflowKey}/${created.version}`),
|
|
1241
|
+
onQuotaExhausted: () => setShowQuotaExhausted(true)
|
|
1242
|
+
});
|
|
1243
|
+
const handleUseStoreTemplate = templateImport.start;
|
|
1636
1244
|
const assistantControls = useMemo(() => templatesControls({
|
|
1637
1245
|
page: tr("fluidgrids.templates.templatesPage.heading", "Templates"),
|
|
1638
1246
|
search: setSearch,
|
|
@@ -1857,6 +1465,7 @@ function TemplatesPage() {
|
|
|
1857
1465
|
resubmit: resubmitTarget,
|
|
1858
1466
|
onSubmitted: handleSubmitted
|
|
1859
1467
|
}),
|
|
1468
|
+
templateImport.dialog,
|
|
1860
1469
|
/* @__PURE__ */ jsx(QuotaExhaustedDialog, {
|
|
1861
1470
|
surface: "template-import",
|
|
1862
1471
|
open: showQuotaExhausted,
|