@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.
Files changed (41) hide show
  1. package/dist/{ExecutionDetailPage-6ddRdNUR.js → ExecutionDetailPage-CDkJeeu2.js} +9 -10
  2. package/dist/{FailureTriagePanel-CR1BzOrM.js → FailureTriagePanel-BHiduL7A.js} +1 -1
  3. package/dist/{FluidGridsProvider-BFdGQ1g_.js → FluidGridsProvider-CDZjKmfD.js} +14 -3
  4. package/dist/{HistoryPage-DFiyqbvw.js → HistoryPage-DnVGN_72.js} +3 -3
  5. package/dist/{InsightsPage-CXCNQqmV.js → InsightsPage-CGRpZtGu.js} +4 -4
  6. package/dist/{ManagedAiPolicyAuthority-G1Efbc49.js → ManagedAiPolicyAuthority-DbJBledZ.js} +6 -6
  7. package/dist/{MissingNodesInstallModal-C-nsV4W7.js → MissingNodesInstallModal-YI57ImRx.js} +2 -2
  8. package/dist/{NodesPage-C4KtAasX.js → NodesPage-ClvhUfbh.js} +2 -2
  9. package/dist/{PublishCustomNodeAction-C3fEmWnC.js → PublishCustomNodeAction-D2F7oN0S.js} +1 -1
  10. package/dist/{TemplateConfigurationModal-CDJFVAho.js → TemplateConfigurationModal-BimyvVH6.js} +2 -2
  11. package/dist/{TemplateDetailPanel-BU6SzQy0.js → TemplateDetailPanel-CkwB6vrW.js} +2 -2
  12. package/dist/{TemplatesPage-CXMJntN7.js → TemplatesPage-ZK6cMWur.js} +113 -504
  13. package/dist/{VersionDiffDialog-ExN-CN6f.js → VersionDiffDialog-Dq2xlPFI.js} +1 -1
  14. package/dist/{WorkflowProposalCard-DuXQI7UY.js → WorkflowProposalCard-8Vr56kQa.js} +1 -1
  15. package/dist/{WorkflowSearchRedirect-xe0qc4A3.js → WorkflowSearchRedirect-BHtdF64q.js} +10 -11
  16. package/dist/{WorkflowShareDialog-FTeBgyb-.js → WorkflowShareDialog-ByTSWwwR.js} +2 -2
  17. package/dist/{WorkflowsList-DUFwerL1.js → WorkflowsList-B9F2e8M6.js} +5 -6
  18. package/dist/{agent-form-CzG3vX_q.js → agent-form-BvMWhQ42.js} +1 -1
  19. package/dist/{agentTools-dDrEjdbB.js → agentTools-Dgrdql-W.js} +2 -2
  20. package/dist/{codec-client-DiFkc0yn.js → codec-client-DstqyzTN.js} +1 -1
  21. package/dist/executions.js +1 -1
  22. package/dist/{flow-builder-vz253WZe.js → flow-builder-CukxgO65.js} +2857 -2555
  23. package/dist/history.js +1 -1
  24. package/dist/index.js +23 -23
  25. package/dist/{node-detail-view-DlbE4ob6.js → node-detail-view-DRk8Ln5L.js} +6 -6
  26. package/dist/{node-endpoint-configs-DVo9nUOe.js → node-endpoint-configs-DHFGA5MQ.js} +17 -3
  27. package/dist/nodes.js +1 -1
  28. package/dist/observability.js +1 -1
  29. package/dist/{quick-config-panel-DxNYFw66.js → quick-config-panel-40_1utiL.js} +2 -2
  30. package/dist/settings.js +1 -1
  31. package/dist/templates.js +1 -1
  32. package/dist/{useAgentDefinitions-BVTONKpB.js → useAgentDefinitions-NFtLytDR.js} +1 -1
  33. package/dist/{useExecutions-BeY3w5oZ.js → useExecutions-BqoD7XWi.js} +2 -2
  34. package/dist/{useObservability-BniBi0zw.js → useObservability-Cs-5_NHh.js} +2 -2
  35. package/dist/{useRunHistory-C47nRjSE.js → useRunHistory-CEL-CIz-.js} +2 -2
  36. package/dist/{useWorkflows-BtZBdTmp.js → useWorkflows-Bwpvfdgv.js} +2 -2
  37. package/dist/workflow-builder.js +2 -2
  38. package/dist/workflows-list.js +1 -1
  39. package/package.json +1 -1
  40. package/dist/rolldown-runtime-hDbaFkMy.js +0 -18
  41. package/dist/template-input-applicator-C0p6hE4K.js +0 -84
@@ -1,382 +1,27 @@
1
1
  "use client";
2
- import { Ji as getFluidGridsConfig, Vi as describeOperationError, Wi as throwIfResultError, ki as getFluidGridsClient } from "./node-endpoint-configs-DVo9nUOe.js";
3
- import { Dn as isQuotaExhaustedFailure, E as MyWorkflowTemplateSubmissionsDocument, O as SubmitWorkflowTemplateDocument, Sn as BILLING_PLANS_PATH, T as InstallWorkflowNodeDocument, _n as emitWorkflowCreated, hn as emitTemplateUsed, j as WithdrawWorkflowTemplateSubmissionDocument, ln as useFluidGridsAnalytics, t as FluidGridsContext } from "./FluidGridsProvider-BFdGQ1g_.js";
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 { F as useWorkflowTemplates, I as useInstallNode, J as useGlobalStoreClient, b as TemplateCard, t as FlowBuilderPage } from "./flow-builder-vz253WZe.js";
6
- import { h as useWorkflowDefinitions, l as useLazyWorkflowDefinition, s as useDuplicateWorkflow } from "./useWorkflows-BtZBdTmp.js";
7
- import { k as QuotaExhaustedDialog } from "./WorkflowShareDialog-FTeBgyb-.js";
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 { n as deepClone, t as applyTemplateInputs } from "./template-input-applicator-C0p6hE4K.js";
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, importTemplate } = useImportWorkflowTemplate();
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 handleUse = async () => {
616
- if (!template) return;
617
- setImporting(true);
618
- toast.loading(tr("fluidgrids.pages.templatePreview.importingToast", "Importing template..."), { id: "tpl-preview-import" });
619
- try {
620
- const result = await importTemplate(template);
621
- toast.dismiss("tpl-preview-import");
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: [/* @__PURE__ */ jsxs("div", {
636
- className: "flex h-full w-full flex-col overflow-hidden",
637
- children: [/* @__PURE__ */ jsxs("div", {
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 min-w-0 items-center gap-2",
641
- children: [/* @__PURE__ */ jsx("button", {
642
- type: "button",
643
- onClick: backToTemplates,
644
- title: tr("fluidgrids.pages.templatePreview.backTooltip", "Back to templates"),
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
- children: tr("fluidgrids.pages.templatePreview.backToTemplates", "Back to templates")
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
- children: loading ? /* @__PURE__ */ jsxs("div", {
694
- className: "flex h-full items-center justify-center gap-2 text-sm text-muted-foreground",
695
- children: [/* @__PURE__ */ jsx(Icon, {
696
- icon: "svg-spinners:ring-resize",
697
- className: "size-4"
698
- }), tr("fluidgrids.pages.templatePreview.loadingPreview", "Loading preview…")]
699
- }) : error ? /* @__PURE__ */ jsxs("div", {
700
- className: "flex h-full flex-col items-center justify-center gap-3 p-8 text-center",
701
- children: [
702
- /* @__PURE__ */ jsx(Icon, {
703
- icon: "mdi:alert-circle-outline",
704
- className: "size-8 text-muted-foreground"
705
- }),
706
- /* @__PURE__ */ jsx("p", {
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: error
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
- }) : workflow ? /* @__PURE__ */ jsx(FlowBuilderPage, {
717
- workflow,
718
- readOnly: true
719
- }) : null
720
- })
721
- })]
722
- }), /* @__PURE__ */ jsx(QuotaExhaustedDialog, {
723
- surface: "template-import",
724
- open: showQuotaExhausted,
725
- onOpenChange: setShowQuotaExhausted,
726
- onUpgrade: () => {
727
- setShowQuotaExhausted(false);
728
- navigate(BILLING_PLANS_PATH);
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 handleUseStoreTemplate = useCallback(async (template) => {
1604
- try {
1605
- toast.loading(tr("fluidgrids.templates.templatesPage.storeLoadingToast", "Loading template..."), { id: "store-template-load" });
1606
- const fullTemplate = await getTemplateDetail(template.id);
1607
- toast.dismiss("store-template-load");
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,