@burdenoff/fluidgrids-fe-sdk 2026.1030.3 → 2026.1031.2

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 (39) hide show
  1. package/dist/{ExecutionDetailPage-UJXTRVYM.js → ExecutionDetailPage-WOOORXXU.js} +10 -10
  2. package/dist/{ExecutionsListPage-ZSMTA5QN.js → ExecutionsListPage-AYDGFU6W.js} +7 -7
  3. package/dist/{WorkflowBuilder-NSBAOTQB.js → WorkflowBuilder-LJ7OX2WF.js} +9 -9
  4. package/dist/{WorkflowsList-VH7ZKQJE.js → WorkflowsList-BZXBTGNL.js} +8 -8
  5. package/dist/{chunk-QJ4P5Y47.js → chunk-475XP5J7.js} +6 -6
  6. package/dist/{chunk-5CRJD5YU.js → chunk-5R2JKC3S.js} +3 -3
  7. package/dist/{chunk-Z6THCS3L.js → chunk-6PGLJWIC.js} +119 -10
  8. package/dist/{chunk-NHQXWTOK.js → chunk-ADTEMSJI.js} +1 -1
  9. package/dist/{chunk-HWPPD2WE.js → chunk-BHD4CDLL.js} +1 -1
  10. package/dist/{chunk-HO7CZM3V.js → chunk-CIR7WF4W.js} +1 -1
  11. package/dist/{chunk-SU5W4OOE.js → chunk-DHR2YURL.js} +7 -7
  12. package/dist/{chunk-D3UWK7GY.js → chunk-FNZZ5JJL.js} +22 -16
  13. package/dist/{chunk-JUUYPCFY.js → chunk-G77IGC3Y.js} +4 -1
  14. package/dist/{chunk-C4LNSLQX.js → chunk-IGROOI7E.js} +1 -1
  15. package/dist/{chunk-TGGTM5RW.js → chunk-JFQGODKK.js} +1 -1
  16. package/dist/{chunk-6DNT5QP5.js → chunk-M6GUEJBJ.js} +163 -52
  17. package/dist/{chunk-IQFPFSFN.js → chunk-P2VAHDGO.js} +1 -1
  18. package/dist/{chunk-KK5VQ322.js → chunk-P3TGCUXP.js} +3 -3
  19. package/dist/{chunk-OTO46JVO.js → chunk-PKG6BCHB.js} +1 -1
  20. package/dist/{chunk-5EHMEGSO.js → chunk-PTROESUB.js} +4 -4
  21. package/dist/{chunk-FQBRK4R5.js → chunk-Q3HKE627.js} +3 -3
  22. package/dist/{chunk-WNC3UP2A.js → chunk-QKQOTEAB.js} +2 -2
  23. package/dist/{chunk-63H4FFY7.js → chunk-VJWJGSRS.js} +1 -1
  24. package/dist/{chunk-7RVPPQOG.js → chunk-VM73YKAA.js} +5 -5
  25. package/dist/{chunk-WMI2OSWG.js → chunk-WHPQRYZD.js} +4 -4
  26. package/dist/{chunk-VKM2BI46.js → chunk-XX6D7IND.js} +1 -1
  27. package/dist/{chunk-P4WCNDP7.js → chunk-YU7H4QO2.js} +776 -53
  28. package/dist/executions.js +11 -11
  29. package/dist/global-operations-RAFTISSY.js +2 -0
  30. package/dist/history.js +5 -5
  31. package/dist/index.d.ts +96 -4
  32. package/dist/index.js +34 -34
  33. package/dist/observability.js +5 -5
  34. package/dist/settings.js +6 -6
  35. package/dist/templates.js +9 -9
  36. package/dist/workflow-builder.js +10 -10
  37. package/dist/workflows-list.js +8 -8
  38. package/package.json +1 -1
  39. package/dist/global-operations-D2USVOPM.js +0 -2
@@ -1,12 +1,12 @@
1
- import { useImportWorkflowTemplate, FlowBuilderPage, useWorkflowTemplates, TemplateCard } from './chunk-6DNT5QP5.js';
2
- import { QuotaExhaustedDialog } from './chunk-C4LNSLQX.js';
3
- import { templatesControls } from './chunk-D3UWK7GY.js';
4
- import { useWorkflowDefinitions, useDuplicateWorkflow } from './chunk-OTO46JVO.js';
1
+ import { useImportWorkflowTemplate, FlowBuilderPage, useWorkflowTemplates, TEMPLATE_STORE_CATEGORIES, TemplateCard, useTemplateSubmissions, usePublishToStore } from './chunk-M6GUEJBJ.js';
2
+ import { QuotaExhaustedDialog } from './chunk-IGROOI7E.js';
3
+ import { templatesControls } from './chunk-FNZZ5JJL.js';
4
+ import { useWorkflowDefinitions, useDuplicateWorkflow, useLazyWorkflowDefinition } from './chunk-PKG6BCHB.js';
5
5
  import { useFluidGridsPageControls } from './chunk-PJAWNFJV.js';
6
6
  import { FluidGridsDocsLink } from './chunk-4VJ3MDOV.js';
7
- import { BILLING_PLANS_PATH, useFluidGridsAnalytics, emitTemplateUsed, isQuotaExhaustedFailure, SdkPermissionButton, FLUIDGRIDS_PERMISSIONS } from './chunk-Z6THCS3L.js';
7
+ import { BILLING_PLANS_PATH, useFluidGridsAnalytics, emitTemplateUsed, isQuotaExhaustedFailure, SdkPermissionButton, FLUIDGRIDS_PERMISSIONS } from './chunk-6PGLJWIC.js';
8
8
  import { useTr } from './chunk-UULVIT25.js';
9
- import { useState, useEffect, useMemo, useCallback } from 'react';
9
+ import { useState, useEffect, useCallback, useMemo } from 'react';
10
10
  import { useParams, useNavigate } from 'react-router';
11
11
  import { Icon } from '@iconify/react';
12
12
  import { toast } from 'sonner';
@@ -15,7 +15,7 @@ import { ErrorBoundary } from '@burdenoff/fe-libs/shared/components';
15
15
  import { ThemeProvider as ThemeProvider$1 } from '@burdenoff/fe-libs/shared/providers/shell';
16
16
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
17
17
  import '@xyflow/react/dist/style.css';
18
- import { Input, Skeleton, Card, CardHeader, Badge } from '@burdenoff/fe-libs/ui';
18
+ import { Input, Skeleton, Card, CardHeader, Badge, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Label, Textarea, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DialogFooter } from '@burdenoff/fe-libs/ui';
19
19
  import { v4 } from 'uuid';
20
20
 
21
21
  function ThemeProvider({ children }) {
@@ -289,6 +289,579 @@ function TemplatePreview() {
289
289
  )
290
290
  ] });
291
291
  }
292
+
293
+ // src/modules/templates/utils/template-categories.ts
294
+ var TEMPLATE_CATEGORY_LABELS = {
295
+ automation: "Automation",
296
+ integration: "Integration",
297
+ "data-processing": "Data Processing",
298
+ "ai-ml": "AI/ML",
299
+ communication: "Communication",
300
+ devops: "DevOps",
301
+ marketing: "Marketing",
302
+ sales: "Sales",
303
+ support: "Support",
304
+ productivity: "Productivity",
305
+ finance: "Finance"
306
+ };
307
+ var TEMPLATE_CATEGORY_ICONS = {
308
+ automation: "mdi:robot",
309
+ integration: "mdi:connection",
310
+ "data-processing": "mdi:database-cog",
311
+ "ai-ml": "mdi:brain",
312
+ communication: "mdi:message-text",
313
+ devops: "mdi:server",
314
+ marketing: "mdi:bullhorn",
315
+ sales: "mdi:handshake",
316
+ support: "mdi:lifebuoy",
317
+ productivity: "mdi:checkbox-marked-circle-outline",
318
+ finance: "mdi:currency-usd"
319
+ };
320
+ function parseTemplateTags(value) {
321
+ const out = [];
322
+ for (const raw of value.split(",")) {
323
+ const tag = raw.trim().toLowerCase();
324
+ if (tag && !out.includes(tag)) out.push(tag);
325
+ }
326
+ return out.slice(0, 10);
327
+ }
328
+ var DIFFICULTIES = ["easy", "medium", "hard"];
329
+ function initialCategory(workflow, resubmit) {
330
+ const candidate = (resubmit?.category ?? workflow?.category ?? "").toLowerCase().replace(/\s+/g, "-");
331
+ return TEMPLATE_STORE_CATEGORIES.includes(candidate) ? candidate : "automation";
332
+ }
333
+ function PublishTemplateDialog({
334
+ open,
335
+ onOpenChange,
336
+ workflow,
337
+ resubmit,
338
+ onSubmitted
339
+ }) {
340
+ const tr = useTr();
341
+ const { publishToStore, publishing } = usePublishToStore();
342
+ const { getWorkflow } = useLazyWorkflowDefinition();
343
+ const [name, setName] = useState("");
344
+ const [description, setDescription] = useState("");
345
+ const [longDescription, setLongDescription] = useState("");
346
+ const [category, setCategory] = useState("automation");
347
+ const [tags, setTags] = useState("");
348
+ const [authorName, setAuthorName] = useState("");
349
+ const [difficulty, setDifficulty] = useState("medium");
350
+ const [notes, setNotes] = useState("");
351
+ const [error, setError] = useState(null);
352
+ useEffect(() => {
353
+ if (!open) return;
354
+ setName(resubmit?.name ?? workflow?.name ?? "");
355
+ setDescription(resubmit?.description ?? workflow?.description ?? "");
356
+ setLongDescription("");
357
+ setCategory(initialCategory(workflow, resubmit));
358
+ setTags((resubmit?.tags ?? workflow?.tags ?? []).join(", "));
359
+ setAuthorName(resubmit?.authorName ?? "");
360
+ setDifficulty("medium");
361
+ setNotes("");
362
+ setError(null);
363
+ }, [open, workflow, resubmit]);
364
+ const submit = async () => {
365
+ setError(null);
366
+ if (!workflow) return;
367
+ if (!name.trim()) {
368
+ setError(
369
+ tr(
370
+ "fluidgrids.templates.publish.nameRequired",
371
+ "Enter a template name."
372
+ )
373
+ );
374
+ return;
375
+ }
376
+ if (!description.trim()) {
377
+ setError(
378
+ tr(
379
+ "fluidgrids.templates.publish.descriptionRequired",
380
+ "Enter a one-line summary."
381
+ )
382
+ );
383
+ return;
384
+ }
385
+ const current = (workflow.version ? await getWorkflow(workflow.workflowKey, void 0, workflow.version) : null) ?? await getWorkflow(workflow.workflowKey);
386
+ if (!current?.definitionJson) {
387
+ setError(
388
+ tr(
389
+ "fluidgrids.templates.publish.definitionMissing",
390
+ "Could not read this workflow's definition. Save it and try again."
391
+ )
392
+ );
393
+ return;
394
+ }
395
+ const result = await publishToStore({
396
+ workflow: current,
397
+ productId: resubmit?.id,
398
+ name,
399
+ description,
400
+ longDescription: longDescription || void 0,
401
+ category,
402
+ tags: parseTemplateTags(tags),
403
+ authorName: authorName || void 0,
404
+ difficultyLevel: difficulty,
405
+ notes: notes || void 0
406
+ });
407
+ if (result.success && result.submission) {
408
+ onSubmitted?.(result.submission);
409
+ onOpenChange(false);
410
+ } else {
411
+ setError(result.message);
412
+ }
413
+ };
414
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
415
+ DialogContent,
416
+ {
417
+ className: "sm:max-w-lg max-h-[90vh] overflow-y-auto",
418
+ "data-testid": "publish-template-dialog",
419
+ children: [
420
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
421
+ /* @__PURE__ */ jsx(DialogTitle, { children: resubmit ? tr(
422
+ "fluidgrids.templates.publish.resubmitTitle",
423
+ "Resubmit template"
424
+ ) : tr(
425
+ "fluidgrids.templates.publish.title",
426
+ "Publish to FluidGrids Store"
427
+ ) }),
428
+ /* @__PURE__ */ jsx(DialogDescription, { children: tr(
429
+ "fluidgrids.templates.publish.description",
430
+ "Share this workflow as a free community template. Saved connections, pinned test data and secrets are removed, and a store reviewer checks it before it is listed."
431
+ ) })
432
+ ] }),
433
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
434
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
435
+ /* @__PURE__ */ jsx(Label, { htmlFor: "publish-template-name", children: tr("fluidgrids.templates.publish.nameLabel", "Template name") }),
436
+ /* @__PURE__ */ jsx(
437
+ Input,
438
+ {
439
+ id: "publish-template-name",
440
+ value: name,
441
+ maxLength: 120,
442
+ onChange: (e) => setName(e.target.value)
443
+ }
444
+ )
445
+ ] }),
446
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
447
+ /* @__PURE__ */ jsx(Label, { htmlFor: "publish-template-summary", children: tr("fluidgrids.templates.publish.summaryLabel", "Summary") }),
448
+ /* @__PURE__ */ jsx(
449
+ Input,
450
+ {
451
+ id: "publish-template-summary",
452
+ value: description,
453
+ maxLength: 500,
454
+ placeholder: tr(
455
+ "fluidgrids.templates.publish.summaryPlaceholder",
456
+ "What does this workflow do?"
457
+ ),
458
+ onChange: (e) => setDescription(e.target.value)
459
+ }
460
+ )
461
+ ] }),
462
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
463
+ /* @__PURE__ */ jsx(Label, { htmlFor: "publish-template-details", children: tr(
464
+ "fluidgrids.templates.publish.detailsLabel",
465
+ "Details (optional, markdown)"
466
+ ) }),
467
+ /* @__PURE__ */ jsx(
468
+ Textarea,
469
+ {
470
+ id: "publish-template-details",
471
+ rows: 3,
472
+ value: longDescription,
473
+ maxLength: 1e4,
474
+ onChange: (e) => setLongDescription(e.target.value)
475
+ }
476
+ )
477
+ ] }),
478
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [
479
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
480
+ /* @__PURE__ */ jsx(Label, { htmlFor: "publish-template-category", children: tr("fluidgrids.templates.publish.categoryLabel", "Category") }),
481
+ /* @__PURE__ */ jsxs(
482
+ Select,
483
+ {
484
+ value: category,
485
+ onValueChange: (value) => setCategory(value),
486
+ children: [
487
+ /* @__PURE__ */ jsx(SelectTrigger, { id: "publish-template-category", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
488
+ /* @__PURE__ */ jsx(SelectContent, { children: TEMPLATE_STORE_CATEGORIES.map((value) => /* @__PURE__ */ jsx(SelectItem, { value, children: tr(
489
+ `fluidgrids.templates.categories.${value}`,
490
+ TEMPLATE_CATEGORY_LABELS[value]
491
+ ) }, value)) })
492
+ ]
493
+ }
494
+ )
495
+ ] }),
496
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
497
+ /* @__PURE__ */ jsx(Label, { htmlFor: "publish-template-difficulty", children: tr(
498
+ "fluidgrids.templates.publish.difficultyLabel",
499
+ "Difficulty"
500
+ ) }),
501
+ /* @__PURE__ */ jsxs(
502
+ Select,
503
+ {
504
+ value: difficulty,
505
+ onValueChange: (value) => setDifficulty(value),
506
+ children: [
507
+ /* @__PURE__ */ jsx(SelectTrigger, { id: "publish-template-difficulty", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
508
+ /* @__PURE__ */ jsx(SelectContent, { children: DIFFICULTIES.map((value) => /* @__PURE__ */ jsx(SelectItem, { value, children: tr(
509
+ `fluidgrids.templates.difficulty.${value}`,
510
+ value.charAt(0).toUpperCase() + value.slice(1)
511
+ ) }, value)) })
512
+ ]
513
+ }
514
+ )
515
+ ] })
516
+ ] }),
517
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
518
+ /* @__PURE__ */ jsx(Label, { htmlFor: "publish-template-tags", children: tr(
519
+ "fluidgrids.templates.publish.tagsLabel",
520
+ "Tags (comma separated, up to 10)"
521
+ ) }),
522
+ /* @__PURE__ */ jsx(
523
+ Input,
524
+ {
525
+ id: "publish-template-tags",
526
+ value: tags,
527
+ placeholder: "slack, alerts, crm",
528
+ onChange: (e) => setTags(e.target.value)
529
+ }
530
+ )
531
+ ] }),
532
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
533
+ /* @__PURE__ */ jsx(Label, { htmlFor: "publish-template-author", children: tr("fluidgrids.templates.publish.authorLabel", "Author name") }),
534
+ /* @__PURE__ */ jsx(
535
+ Input,
536
+ {
537
+ id: "publish-template-author",
538
+ value: authorName,
539
+ maxLength: 120,
540
+ placeholder: tr(
541
+ "fluidgrids.templates.publish.authorPlaceholder",
542
+ "Shown on the template. Required for your first one."
543
+ ),
544
+ onChange: (e) => setAuthorName(e.target.value)
545
+ }
546
+ )
547
+ ] }),
548
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
549
+ /* @__PURE__ */ jsx(Label, { htmlFor: "publish-template-notes", children: tr(
550
+ "fluidgrids.templates.publish.notesLabel",
551
+ "Note for the reviewer (optional)"
552
+ ) }),
553
+ /* @__PURE__ */ jsx(
554
+ Textarea,
555
+ {
556
+ id: "publish-template-notes",
557
+ rows: 2,
558
+ value: notes,
559
+ maxLength: 2e3,
560
+ onChange: (e) => setNotes(e.target.value)
561
+ }
562
+ )
563
+ ] }),
564
+ error && /* @__PURE__ */ jsx(
565
+ "p",
566
+ {
567
+ role: "alert",
568
+ className: "text-sm text-[var(--color-status-error-text)]",
569
+ "data-testid": "publish-template-error",
570
+ children: error
571
+ }
572
+ )
573
+ ] }),
574
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
575
+ /* @__PURE__ */ jsx(
576
+ Button,
577
+ {
578
+ variant: "outline",
579
+ onClick: () => onOpenChange(false),
580
+ disabled: publishing,
581
+ children: tr("fluidgrids.templates.publish.cancel", "Cancel")
582
+ }
583
+ ),
584
+ /* @__PURE__ */ jsxs(
585
+ Button,
586
+ {
587
+ onClick: () => void submit(),
588
+ disabled: publishing || !workflow,
589
+ "data-testid": "publish-template-submit",
590
+ children: [
591
+ publishing ? /* @__PURE__ */ jsx(
592
+ Icon,
593
+ {
594
+ icon: "mdi:loading",
595
+ width: 14,
596
+ className: "mr-1.5 animate-spin"
597
+ }
598
+ ) : /* @__PURE__ */ jsx(Icon, { icon: "mdi:store-plus", width: 14, className: "mr-1.5" }),
599
+ tr("fluidgrids.templates.publish.submit", "Submit for review")
600
+ ]
601
+ }
602
+ )
603
+ ] })
604
+ ]
605
+ }
606
+ ) });
607
+ }
608
+ var STATUS_STYLES = {
609
+ ["PENDING_REVIEW" /* PendingReview */]: {
610
+ key: "pendingReview",
611
+ label: "In review",
612
+ icon: "mdi:clock-outline",
613
+ className: "bg-[var(--color-status-info-bg-subtle)] text-[var(--color-status-info-text)] border-[var(--color-status-info-border)]"
614
+ },
615
+ ["PUBLISHED" /* Published */]: {
616
+ key: "published",
617
+ label: "Published",
618
+ icon: "mdi:check-circle",
619
+ className: "bg-[var(--color-status-success-bg-subtle)] text-[var(--color-status-success-text)] border-[var(--color-status-success-border)]"
620
+ },
621
+ ["REJECTED" /* Rejected */]: {
622
+ key: "rejected",
623
+ label: "Changes requested",
624
+ icon: "mdi:alert-circle",
625
+ className: "bg-[var(--color-status-error-bg-subtle)] text-[var(--color-status-error-text)] border-[var(--color-status-error-border)]"
626
+ },
627
+ ["UNLISTED" /* Unlisted */]: {
628
+ key: "unlisted",
629
+ label: "Withdrawn",
630
+ icon: "mdi:eye-off",
631
+ className: "bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)] border-[var(--color-status-warning-border)]"
632
+ },
633
+ ["DRAFT" /* Draft */]: {
634
+ key: "draft",
635
+ label: "Draft",
636
+ icon: "mdi:pencil",
637
+ className: "bg-background-tertiary text-text-secondary border-border-secondary"
638
+ }
639
+ };
640
+ var RESUBMITTABLE = /* @__PURE__ */ new Set([
641
+ "DRAFT" /* Draft */,
642
+ "REJECTED" /* Rejected */,
643
+ "UNLISTED" /* Unlisted */
644
+ ]);
645
+ function categoryLabel(category) {
646
+ if (!category) return null;
647
+ return TEMPLATE_CATEGORY_LABELS[category] ?? category;
648
+ }
649
+ function MySubmissionsPanel({ onResubmit }) {
650
+ const tr = useTr();
651
+ const { submissions, loading, error, refetch, withdraw, withdrawingId } = useTemplateSubmissions(true);
652
+ const [confirmingId, setConfirmingId] = useState(null);
653
+ const handleWithdraw = async (submission) => {
654
+ if (confirmingId !== submission.id) {
655
+ setConfirmingId(submission.id);
656
+ return;
657
+ }
658
+ setConfirmingId(null);
659
+ const result = await withdraw(submission.id);
660
+ if (result.success) {
661
+ toast.success(
662
+ result.removed ? tr(
663
+ "fluidgrids.templates.submissions.withdrawDeletedToast",
664
+ "Template withdrawn"
665
+ ) : tr(
666
+ "fluidgrids.templates.submissions.withdrawUnlistedToast",
667
+ "Template taken off the store"
668
+ )
669
+ );
670
+ } else {
671
+ toast.error(result.message);
672
+ }
673
+ };
674
+ if (loading && submissions.length === 0) {
675
+ return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-24 rounded-lg bg-card-foreground/10" }, i)) });
676
+ }
677
+ if (error) {
678
+ return /* @__PURE__ */ jsxs("div", { className: "p-4 bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)] rounded-lg", children: [
679
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-status-error-text)]", children: error.message }),
680
+ /* @__PURE__ */ jsx(
681
+ Button,
682
+ {
683
+ variant: "outline",
684
+ size: "sm",
685
+ className: "mt-3",
686
+ onClick: () => void refetch(),
687
+ children: tr("fluidgrids.templates.templatesPage.errorStateRetry", "Retry")
688
+ }
689
+ )
690
+ ] });
691
+ }
692
+ if (submissions.length === 0) {
693
+ return /* @__PURE__ */ jsxs(
694
+ "div",
695
+ {
696
+ className: "flex flex-col items-center justify-center py-20 text-center",
697
+ "data-testid": "my-submissions-empty",
698
+ children: [
699
+ /* @__PURE__ */ jsx("div", { className: "rounded-full bg-background-secondary p-4 mb-4", children: /* @__PURE__ */ jsx(
700
+ Icon,
701
+ {
702
+ icon: "mdi:store-plus",
703
+ width: 40,
704
+ className: "text-text-secondary"
705
+ }
706
+ ) }),
707
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-text-primary mb-2", children: tr(
708
+ "fluidgrids.templates.submissions.emptyTitle",
709
+ "You have not published any templates yet"
710
+ ) }),
711
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary max-w-md", children: tr(
712
+ "fluidgrids.templates.submissions.emptyHint",
713
+ "Open My Templates and choose Publish on a workflow to share it in the FluidGrids Store."
714
+ ) })
715
+ ]
716
+ }
717
+ );
718
+ }
719
+ return /* @__PURE__ */ jsx("div", { className: "space-y-3", "data-testid": "my-submissions-list", children: submissions.map((submission) => {
720
+ const style = STATUS_STYLES[submission.status];
721
+ const category = categoryLabel(submission.category);
722
+ const busy = withdrawingId === submission.id;
723
+ return /* @__PURE__ */ jsx(
724
+ Card,
725
+ {
726
+ className: "border border-border-secondary bg-background-secondary",
727
+ "data-testid": "my-submission-row",
728
+ children: /* @__PURE__ */ jsxs(CardHeader, { className: "gap-3", children: [
729
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3", children: [
730
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 min-w-0", children: [
731
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg bg-background-tertiary p-2.5 shrink-0", children: /* @__PURE__ */ jsx(
732
+ Icon,
733
+ {
734
+ icon: submission.icon ?? "mdi:workflow",
735
+ width: 22,
736
+ className: "text-text-primary"
737
+ }
738
+ ) }),
739
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
740
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold text-text-primary leading-tight break-words", children: submission.name }),
741
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary mt-0.5 break-words", children: submission.description }),
742
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 mt-2 text-xs text-text-secondary", children: [
743
+ /* @__PURE__ */ jsxs("span", { children: [
744
+ "v",
745
+ submission.version
746
+ ] }),
747
+ category && /* @__PURE__ */ jsxs("span", { children: [
748
+ "\xB7 ",
749
+ category
750
+ ] }),
751
+ /* @__PURE__ */ jsxs("span", { children: [
752
+ "\xB7",
753
+ " ",
754
+ tr(
755
+ "fluidgrids.templates.submissions.nodeCount",
756
+ "{{count}} node types",
757
+ { count: submission.nodeCount }
758
+ )
759
+ ] }),
760
+ submission.status === "PUBLISHED" /* Published */ && /* @__PURE__ */ jsxs("span", { children: [
761
+ "\xB7",
762
+ " ",
763
+ tr(
764
+ "fluidgrids.templates.submissions.downloads",
765
+ "{{count}} imports",
766
+ { count: submission.downloads }
767
+ )
768
+ ] }),
769
+ submission.sourceWorkflowKey && /* @__PURE__ */ jsxs("span", { className: "truncate", children: [
770
+ "\xB7",
771
+ " ",
772
+ tr(
773
+ "fluidgrids.templates.submissions.source",
774
+ "from {{key}}",
775
+ { key: submission.sourceWorkflowKey }
776
+ )
777
+ ] })
778
+ ] })
779
+ ] })
780
+ ] }),
781
+ style && /* @__PURE__ */ jsxs(
782
+ Badge,
783
+ {
784
+ variant: "outline",
785
+ className: `shrink-0 w-fit ${style.className}`,
786
+ "data-testid": "my-submission-status",
787
+ children: [
788
+ /* @__PURE__ */ jsx(Icon, { icon: style.icon, width: 14, className: "mr-1" }),
789
+ tr(
790
+ `fluidgrids.templates.submissions.status.${style.key}`,
791
+ style.label
792
+ )
793
+ ]
794
+ }
795
+ )
796
+ ] }),
797
+ submission.status === "REJECTED" /* Rejected */ && submission.rejectedReason && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-[var(--color-status-error-border)] bg-[var(--color-status-error-bg-subtle)] p-3 text-sm text-[var(--color-status-error-text)]", children: [
798
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: tr(
799
+ "fluidgrids.templates.submissions.reviewerFeedback",
800
+ "Reviewer feedback:"
801
+ ) }),
802
+ " ",
803
+ submission.rejectedReason
804
+ ] }),
805
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
806
+ RESUBMITTABLE.has(submission.status) && submission.sourceWorkflowKey && /* @__PURE__ */ jsxs(
807
+ Button,
808
+ {
809
+ size: "sm",
810
+ onClick: () => onResubmit(submission),
811
+ disabled: busy,
812
+ children: [
813
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:upload", width: 14, className: "mr-1.5" }),
814
+ tr(
815
+ "fluidgrids.templates.submissions.resubmit",
816
+ "Resubmit"
817
+ )
818
+ ]
819
+ }
820
+ ),
821
+ /* @__PURE__ */ jsxs(
822
+ Button,
823
+ {
824
+ size: "sm",
825
+ variant: "outline",
826
+ onClick: () => void handleWithdraw(submission),
827
+ disabled: busy,
828
+ "data-testid": "my-submission-withdraw",
829
+ children: [
830
+ busy ? /* @__PURE__ */ jsx(
831
+ Icon,
832
+ {
833
+ icon: "mdi:loading",
834
+ width: 14,
835
+ className: "mr-1.5 animate-spin"
836
+ }
837
+ ) : /* @__PURE__ */ jsx(
838
+ Icon,
839
+ {
840
+ icon: "mdi:close-circle-outline",
841
+ width: 14,
842
+ className: "mr-1.5"
843
+ }
844
+ ),
845
+ confirmingId === submission.id ? tr(
846
+ "fluidgrids.templates.submissions.withdrawConfirm",
847
+ "Click again to confirm"
848
+ ) : submission.status === "PUBLISHED" /* Published */ ? tr(
849
+ "fluidgrids.templates.submissions.unlist",
850
+ "Take off store"
851
+ ) : tr(
852
+ "fluidgrids.templates.submissions.withdraw",
853
+ "Withdraw"
854
+ )
855
+ ]
856
+ }
857
+ )
858
+ ] })
859
+ ] })
860
+ },
861
+ submission.id
862
+ );
863
+ }) });
864
+ }
292
865
  var CATEGORIES = [
293
866
  { value: "all", label: "All", icon: "mdi:view-grid" },
294
867
  {
@@ -387,10 +960,11 @@ function WorkspaceTemplateCard({
387
960
  workflow,
388
961
  category,
389
962
  onUseTemplate,
963
+ onPublish,
390
964
  isLoading
391
965
  }) {
392
966
  const tr = useTr();
393
- const categoryLabel = CATEGORIES.find((c) => c.value === category)?.label ?? tr("fluidgrids.templates.templatesPage.cardDefaultCategory", "Workflow");
967
+ const categoryLabel2 = CATEGORIES.find((c) => c.value === category)?.label ?? tr("fluidgrids.templates.templatesPage.cardDefaultCategory", "Workflow");
394
968
  const categoryColor = CATEGORY_COLORS[category] ?? "bg-bg-sunken text-text-muted border-border-strong";
395
969
  return /* @__PURE__ */ jsxs(Card, { className: "group border border-border-secondary bg-background-secondary hover:border-border-primary transition-all duration-200 flex flex-col", children: [
396
970
  /* @__PURE__ */ jsxs(CardHeader, { className: "flex-1 pb-3", children: [
@@ -403,7 +977,7 @@ function WorkspaceTemplateCard({
403
977
  className: "text-text-primary"
404
978
  }
405
979
  ) }),
406
- /* @__PURE__ */ jsx(Badge, { variant: "outline", className: categoryColor, children: categoryLabel })
980
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: categoryColor, children: categoryLabel2 })
407
981
  ] }),
408
982
  /* @__PURE__ */ jsx("h3", { className: "font-semibold text-text-primary text-base leading-tight mb-1.5 line-clamp-2", children: workflow.name }),
409
983
  /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary line-clamp-2 min-h-[2.5rem]", children: workflow.description || tr(
@@ -416,46 +990,66 @@ function WorkspaceTemplateCard({
416
990
  "v",
417
991
  workflow.version
418
992
  ] }),
419
- /* @__PURE__ */ jsx(
420
- SdkPermissionButton,
421
- {
422
- permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_CREATE,
423
- deniedTooltip: tr(
424
- "fluidgrids.templates.useDeniedTooltip",
425
- "You don't have permission to create workflows from templates"
426
- ),
427
- children: /* @__PURE__ */ jsx(
428
- Button,
429
- {
430
- variant: "default",
431
- size: "sm",
432
- onClick: () => onUseTemplate(workflow),
433
- disabled: isLoading,
434
- className: "text-primary-foreground",
435
- children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
436
- /* @__PURE__ */ jsx(
437
- Icon,
438
- {
439
- icon: "mdi:loading",
440
- width: 14,
441
- className: "mr-1.5 animate-spin"
442
- }
443
- ),
444
- tr(
445
- "fluidgrids.templates.templatesPage.cardCreating",
446
- "Creating..."
447
- )
448
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
449
- /* @__PURE__ */ jsx(Icon, { icon: "mdi:plus", width: 14, className: "mr-1.5" }),
450
- tr(
451
- "fluidgrids.templates.templatesPage.cardUseTemplate",
452
- "Use Template"
453
- )
454
- ] })
455
- }
456
- )
457
- }
458
- )
993
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
994
+ /* @__PURE__ */ jsxs(
995
+ Button,
996
+ {
997
+ variant: "outline",
998
+ size: "sm",
999
+ onClick: () => onPublish(workflow),
1000
+ disabled: isLoading,
1001
+ title: tr(
1002
+ "fluidgrids.templates.templatesPage.cardPublishHint",
1003
+ "Publish this workflow to the FluidGrids Store"
1004
+ ),
1005
+ "data-testid": "workspace-template-publish",
1006
+ children: [
1007
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:store-plus", width: 14, className: "mr-1.5" }),
1008
+ tr("fluidgrids.templates.templatesPage.cardPublish", "Publish")
1009
+ ]
1010
+ }
1011
+ ),
1012
+ /* @__PURE__ */ jsx(
1013
+ SdkPermissionButton,
1014
+ {
1015
+ permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_CREATE,
1016
+ deniedTooltip: tr(
1017
+ "fluidgrids.templates.useDeniedTooltip",
1018
+ "You don't have permission to create workflows from templates"
1019
+ ),
1020
+ children: /* @__PURE__ */ jsx(
1021
+ Button,
1022
+ {
1023
+ variant: "default",
1024
+ size: "sm",
1025
+ onClick: () => onUseTemplate(workflow),
1026
+ disabled: isLoading,
1027
+ className: "text-primary-foreground",
1028
+ children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
1029
+ /* @__PURE__ */ jsx(
1030
+ Icon,
1031
+ {
1032
+ icon: "mdi:loading",
1033
+ width: 14,
1034
+ className: "mr-1.5 animate-spin"
1035
+ }
1036
+ ),
1037
+ tr(
1038
+ "fluidgrids.templates.templatesPage.cardCreating",
1039
+ "Creating..."
1040
+ )
1041
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1042
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:plus", width: 14, className: "mr-1.5" }),
1043
+ tr(
1044
+ "fluidgrids.templates.templatesPage.cardUseTemplate",
1045
+ "Use Template"
1046
+ )
1047
+ ] })
1048
+ }
1049
+ )
1050
+ }
1051
+ )
1052
+ ] })
459
1053
  ] }) })
460
1054
  ] });
461
1055
  }
@@ -467,6 +1061,9 @@ function TemplatesPage() {
467
1061
  const [duplicatingId, setDuplicatingId] = useState(null);
468
1062
  const [activeSource, setActiveSource] = useState("store");
469
1063
  const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);
1064
+ const [storeCategory, setStoreCategory] = useState("all");
1065
+ const [publishSource, setPublishSource] = useState(null);
1066
+ const [resubmitTarget, setResubmitTarget] = useState(null);
470
1067
  const { workflows, loading, error, refetch } = useWorkflowDefinitions({
471
1068
  status: "ACTIVE" /* Active */,
472
1069
  limit: 100
@@ -484,7 +1081,47 @@ function TemplatesPage() {
484
1081
  } = useWorkflowTemplates(
485
1082
  activeSource === "store" ? search : void 0,
486
1083
  20,
487
- activeSource === "store"
1084
+ activeSource === "store",
1085
+ storeCategory === "all" ? void 0 : storeCategory
1086
+ );
1087
+ const handlePublish = useCallback((workflow) => {
1088
+ setResubmitTarget(null);
1089
+ setPublishSource(workflow);
1090
+ }, []);
1091
+ const handleResubmit = useCallback(
1092
+ (submission) => {
1093
+ if (!submission.sourceWorkflowKey) return;
1094
+ setResubmitTarget(submission);
1095
+ setPublishSource({
1096
+ workflowKey: submission.sourceWorkflowKey,
1097
+ version: submission.sourceWorkflowVersion ?? void 0,
1098
+ name: submission.name,
1099
+ description: submission.description,
1100
+ tags: submission.tags,
1101
+ category: submission.category
1102
+ });
1103
+ },
1104
+ []
1105
+ );
1106
+ const handleSubmitted = useCallback(
1107
+ (submission) => {
1108
+ toast.success(
1109
+ tr(
1110
+ "fluidgrids.templates.templatesPage.publishSuccessToast",
1111
+ "Template submitted for review"
1112
+ ),
1113
+ {
1114
+ description: tr(
1115
+ "fluidgrids.templates.templatesPage.publishSuccessDescription",
1116
+ "{{name}} (v{{version}}) is listed in the store once a reviewer approves it.",
1117
+ { name: submission.name, version: submission.version }
1118
+ )
1119
+ }
1120
+ );
1121
+ setResubmitTarget(null);
1122
+ setActiveSource("submissions");
1123
+ },
1124
+ [tr]
488
1125
  );
489
1126
  const { getTemplateDetail, parseTemplateContent, importTemplate } = useImportWorkflowTemplate();
490
1127
  const categorizedWorkflows = useMemo(() => {
@@ -669,6 +1306,10 @@ function TemplatesPage() {
669
1306
  "fluidgrids.templates.templatesPage.tabMyTemplates",
670
1307
  "My Templates"
671
1308
  ),
1309
+ submissionsLabel: tr(
1310
+ "fluidgrids.templates.templatesPage.tabMySubmissions",
1311
+ "My Submissions"
1312
+ ),
672
1313
  value: activeSource,
673
1314
  set: setActiveSource
674
1315
  },
@@ -679,9 +1320,22 @@ function TemplatesPage() {
679
1320
  neutral: "all",
680
1321
  value: activeCategory,
681
1322
  set: setActiveCategory
1323
+ },
1324
+ storeCategory: {
1325
+ label: "Category",
1326
+ options: [
1327
+ { value: "all", label: "All" },
1328
+ ...TEMPLATE_STORE_CATEGORIES.map((value) => ({
1329
+ value,
1330
+ label: TEMPLATE_CATEGORY_LABELS[value]
1331
+ }))
1332
+ ],
1333
+ neutral: "all",
1334
+ value: storeCategory,
1335
+ set: setStoreCategory
682
1336
  }
683
1337
  }),
684
- [tr, activeSource, activeCategory]
1338
+ [tr, activeSource, activeCategory, storeCategory]
685
1339
  );
686
1340
  useFluidGridsPageControls(assistantControls);
687
1341
  return /* @__PURE__ */ jsxs("div", { className: "p-4 sm:p-6 bg-background-primary min-h-screen", children: [
@@ -747,11 +1401,26 @@ function TemplatesPage() {
747
1401
  )
748
1402
  ]
749
1403
  }
1404
+ ),
1405
+ /* @__PURE__ */ jsxs(
1406
+ "button",
1407
+ {
1408
+ onClick: () => setActiveSource("submissions"),
1409
+ className: `px-4 py-2 rounded-md text-sm font-medium transition-colors ${activeSource === "submissions" ? "bg-background-primary text-text-primary shadow-sm" : "text-text-secondary hover:text-text-primary"}`,
1410
+ "data-testid": "templates-tab-submissions",
1411
+ children: [
1412
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:upload", className: "inline mr-1.5", width: 16 }),
1413
+ tr(
1414
+ "fluidgrids.templates.templatesPage.tabMySubmissions",
1415
+ "My Submissions"
1416
+ )
1417
+ ]
1418
+ }
750
1419
  )
751
1420
  ]
752
1421
  }
753
1422
  ),
754
- /* @__PURE__ */ jsxs(
1423
+ activeSource !== "submissions" && /* @__PURE__ */ jsxs(
755
1424
  "div",
756
1425
  {
757
1426
  className: "relative max-w-md mb-6",
@@ -830,14 +1499,52 @@ function TemplatesPage() {
830
1499
  workflow,
831
1500
  category,
832
1501
  onUseTemplate: handleUseWorkspaceTemplate,
1502
+ onPublish: handlePublish,
833
1503
  isLoading: duplicatingId === workflow.id
834
1504
  },
835
1505
  workflow.id
836
1506
  )) })
837
1507
  ] }),
838
1508
  activeSource === "store" && /* @__PURE__ */ jsxs(Fragment, { children: [
1509
+ /* @__PURE__ */ jsx(
1510
+ "div",
1511
+ {
1512
+ className: "flex flex-wrap gap-2 mb-6",
1513
+ "data-testid": "store-template-categories",
1514
+ children: ["all", ...TEMPLATE_STORE_CATEGORIES].map((value) => {
1515
+ const isActive = storeCategory === value;
1516
+ return /* @__PURE__ */ jsxs(
1517
+ "button",
1518
+ {
1519
+ onClick: () => setStoreCategory(value),
1520
+ "aria-pressed": isActive,
1521
+ className: `inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium transition-colors ${isActive ? "bg-background-tertiary text-text-primary border border-border-primary" : "bg-background-secondary text-text-secondary border border-border-secondary hover:text-text-primary hover:border-border-primary"}`,
1522
+ children: [
1523
+ /* @__PURE__ */ jsx(
1524
+ Icon,
1525
+ {
1526
+ icon: value === "all" ? "mdi:view-grid" : TEMPLATE_CATEGORY_ICONS[value],
1527
+ width: 16
1528
+ }
1529
+ ),
1530
+ value === "all" ? tr("fluidgrids.templates.categories.all", "All") : tr(
1531
+ `fluidgrids.templates.categories.${value}`,
1532
+ TEMPLATE_CATEGORY_LABELS[value]
1533
+ )
1534
+ ]
1535
+ },
1536
+ value
1537
+ );
1538
+ })
1539
+ }
1540
+ ),
839
1541
  storeError && /* @__PURE__ */ jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsx(ErrorState, { message: storeError.message, onRetry: storeRefetch }) }),
840
- storeLoading && storeTemplates.length === 0 ? /* @__PURE__ */ jsx(TemplateSkeleton, {}) : storeTemplates.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { hasSearch: search.trim().length > 0 }) : /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1542
+ storeLoading && storeTemplates.length === 0 ? /* @__PURE__ */ jsx(TemplateSkeleton, {}) : storeTemplates.length === 0 ? /* @__PURE__ */ jsx(
1543
+ EmptyState,
1544
+ {
1545
+ hasSearch: search.trim().length > 0 || storeCategory !== "all"
1546
+ }
1547
+ ) : /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
841
1548
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4", children: storeTemplates.map((template) => /* @__PURE__ */ jsx(
842
1549
  TemplateCard,
843
1550
  {
@@ -875,6 +1582,22 @@ function TemplatesPage() {
875
1582
  ) })
876
1583
  ] })
877
1584
  ] }),
1585
+ activeSource === "submissions" && /* @__PURE__ */ jsx(MySubmissionsPanel, { onResubmit: handleResubmit }),
1586
+ /* @__PURE__ */ jsx(
1587
+ PublishTemplateDialog,
1588
+ {
1589
+ open: publishSource !== null,
1590
+ onOpenChange: (open) => {
1591
+ if (!open) {
1592
+ setPublishSource(null);
1593
+ setResubmitTarget(null);
1594
+ }
1595
+ },
1596
+ workflow: publishSource,
1597
+ resubmit: resubmitTarget,
1598
+ onSubmitted: handleSubmitted
1599
+ }
1600
+ ),
878
1601
  /* @__PURE__ */ jsx(
879
1602
  QuotaExhaustedDialog,
880
1603
  {