@burdenoff/microfe-bigconsole 2026.730.5 → 2026.730.6

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.
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardToolbar.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/DashboardToolbar.tsx"],"sourcesContent":["/**\n * DashboardToolbar Component\n *\n * Toolbar for dashboard actions and controls.\n * Uses fe-libs shared components for consistent styling.\n * Phase 2: Added SavedViewsDropdown and Share button.\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport {\n Plus,\n Clipboard,\n Eye,\n Pencil,\n Save,\n Download,\n Minus,\n RotateCcw,\n Share2,\n PanelLeftClose,\n PanelLeft,\n Image as ImageIcon,\n FileText,\n} from 'lucide-react';\nimport { useDashboardStore, useWidgetStore } from '../../store';\nimport { SavedViewsDropdown } from './SavedViewsDropdown';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DashboardToolbarProps {\n /** Dashboard ID for saved views */\n dashboardId?: string;\n /** Dashboard title */\n title?: string;\n /** Callback to add widget */\n onAddWidget: () => void;\n /** Callback to toggle widget palette */\n onTogglePalette?: () => void;\n /** Whether palette is open */\n isPaletteOpen?: boolean;\n /** Callback to paste widget */\n onPaste?: () => void;\n /** Callback to save dashboard */\n onSave?: () => void;\n /** Whether save is in progress */\n isSaving?: boolean;\n /** Callback to export dashboard (JSON console definition) */\n onExport?: () => void;\n /** Callback to export the board as a PNG image */\n onExportImage?: () => void;\n /** Callback to export the board as a PDF */\n onExportPdf?: () => void;\n /** Whether an image/PDF export is in progress */\n isExportingImage?: boolean;\n /** Callback to open share dialog */\n onShare?: () => void;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardToolbar: FC<DashboardToolbarProps> = memo(function DashboardToolbar({\n dashboardId,\n title,\n onAddWidget,\n onTogglePalette,\n isPaletteOpen = false,\n onPaste,\n onSave,\n isSaving = false,\n onExport,\n onExportImage,\n onExportPdf,\n isExportingImage = false,\n onShare,\n}) {\n const isEditMode = useDashboardStore((state) => state.viewMode === 'edit');\n const toggleViewMode = useDashboardStore((state) => state.toggleViewMode);\n const zoomLevel = useDashboardStore((state) => state.zoomLevel);\n const setZoomLevel = useDashboardStore((state) => state.setZoomLevel);\n\n const clipboardWidgetId = useWidgetStore((state) => state.clipboardWidgetId);\n const hasClipboard = !!clipboardWidgetId;\n\n // Handle paste - use provided callback or fallback\n const handlePaste = useCallback(() => {\n if (hasClipboard && onPaste) {\n onPaste();\n }\n }, [hasClipboard, onPaste]);\n\n return (\n <TooltipProvider>\n <div className=\"flex flex-wrap items-center justify-between gap-x-4 gap-y-2 px-4 py-2 bg-bg-surface border-b\">\n {/* Left section */}\n <div className=\"flex min-w-0 items-center gap-4\">\n {title && (\n <h1 className=\"min-w-0 truncate text-lg font-semibold\" title={title}>\n {title}\n </h1>\n )}\n {/* Saved Views Dropdown */}\n {dashboardId && <SavedViewsDropdown dashboardId={dashboardId} />}\n </div>\n\n {/* Center section - Zoom controls */}\n <div className=\"flex flex-shrink-0 items-center gap-1\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setZoomLevel(Math.max(50, zoomLevel - 10))}\n disabled={zoomLevel <= 50}\n >\n <Minus className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Zoom out</TooltipContent>\n </Tooltip>\n\n <span className=\"text-sm text-text-secondary w-12 text-center\">{zoomLevel}%</span>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setZoomLevel(Math.min(200, zoomLevel + 10))}\n disabled={zoomLevel >= 200}\n >\n <Plus className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Zoom in</TooltipContent>\n </Tooltip>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setZoomLevel(100)}>\n <RotateCcw className=\"size-3.5 mr-1\" />\n Reset\n </Button>\n </TooltipTrigger>\n <TooltipContent>Reset zoom to 100%</TooltipContent>\n </Tooltip>\n </div>\n\n {/* Right section - Actions */}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {isEditMode && (\n <>\n {/* Widget Palette Toggle */}\n {onTogglePalette && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant={isPaletteOpen ? 'secondary' : 'outline'} size=\"sm\" onClick={onTogglePalette}>\n {isPaletteOpen ? <PanelLeftClose className=\"size-4\" /> : <PanelLeft className=\"size-4\" />}\n Widgets\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {isPaletteOpen ? 'Hide widget palette' : 'Show widget palette for drag & drop'}\n </TooltipContent>\n </Tooltip>\n )}\n\n {/* Paste */}\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" onClick={handlePaste} disabled={!hasClipboard}>\n <Clipboard className=\"size-4\" />\n Paste\n </Button>\n </TooltipTrigger>\n <TooltipContent>Paste widget from clipboard</TooltipContent>\n </Tooltip>\n\n {/* Add Widget (Dialog) */}\n <Button size=\"sm\" onClick={onAddWidget}>\n <Plus className=\"size-4\" />\n Add Widget\n </Button>\n </>\n )}\n\n {/* Edit/View Mode Toggle */}\n <Button variant={isEditMode ? 'secondary' : 'outline'} size=\"sm\" onClick={toggleViewMode}>\n {isEditMode ? (\n <>\n <Eye className=\"size-4\" />\n Preview\n </>\n ) : (\n <>\n <Pencil className=\"size-4\" />\n Edit\n </>\n )}\n </Button>\n\n {/* Save */}\n {onSave && (\n <Button variant=\"outline\" size=\"sm\" onClick={onSave} disabled={isSaving}>\n {isSaving ? (\n <>\n <Spinner className=\"size-4\" />\n Saving...\n </>\n ) : (\n <>\n <Save className=\"size-4\" />\n Save\n </>\n )}\n </Button>\n )}\n\n {/* Share */}\n {onShare && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onShare}>\n <Share2 className=\"size-4\" />\n Share\n </Button>\n </TooltipTrigger>\n <TooltipContent>Share dashboard with others</TooltipContent>\n </Tooltip>\n )}\n\n {/* Export — JSON definition */}\n {onExport && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onExport}>\n <Download className=\"size-4\" />\n Export\n </Button>\n </TooltipTrigger>\n <TooltipContent>Export dashboard to JSON</TooltipContent>\n </Tooltip>\n )}\n\n {/* Export — board PNG */}\n {onExportImage && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"icon-sm\" onClick={onExportImage} disabled={isExportingImage}>\n {isExportingImage ? <Spinner className=\"size-4\" /> : <ImageIcon className=\"size-4\" />}\n </Button>\n </TooltipTrigger>\n <TooltipContent>Export board as PNG image</TooltipContent>\n </Tooltip>\n )}\n\n {/* Export — board PDF */}\n {onExportPdf && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"icon-sm\" onClick={onExportPdf} disabled={isExportingImage}>\n <FileText className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Export board as PDF</TooltipContent>\n </Tooltip>\n )}\n </div>\n </div>\n </TooltipProvider>\n );\n});\n\nexport default DashboardToolbar;\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardToolbar.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/DashboardToolbar.tsx"],"sourcesContent":["/**\n * DashboardToolbar Component\n *\n * Toolbar for dashboard actions and controls.\n * Uses fe-libs shared components for consistent styling.\n * Phase 2: Added SavedViewsDropdown and Share button.\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport {\n Clipboard,\n Download,\n Eye,\n FileText,\n Image as ImageIcon,\n Minus,\n PanelLeft,\n PanelLeftClose,\n Pencil,\n Plus,\n RotateCcw,\n Save,\n Share2,\n Store,\n} from 'lucide-react';\nimport { useDashboardStore, useWidgetStore } from '../../store';\nimport { SavedViewsDropdown } from './SavedViewsDropdown';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DashboardToolbarProps {\n /** Dashboard ID for saved views */\n dashboardId?: string;\n /** Dashboard title */\n title?: string;\n /** Callback to add widget */\n onAddWidget: () => void;\n /** Callback to toggle widget palette */\n onTogglePalette?: () => void;\n /** Whether palette is open */\n isPaletteOpen?: boolean;\n /** Callback to paste widget */\n onPaste?: () => void;\n /** Callback to save dashboard */\n onSave?: () => void;\n /** Whether save is in progress */\n isSaving?: boolean;\n /** Callback to export dashboard (JSON console definition) */\n onExport?: () => void;\n /** Publish this dashboard as a reusable template. */\n onPublishTemplate?: () => void;\n /** Callback to export the board as a PNG image */\n onExportImage?: () => void;\n /** Callback to export the board as a PDF */\n onExportPdf?: () => void;\n /** Whether an image/PDF export is in progress */\n isExportingImage?: boolean;\n /** Callback to open share dialog */\n onShare?: () => void;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardToolbar: FC<DashboardToolbarProps> = memo(function DashboardToolbar({\n dashboardId,\n title,\n onAddWidget,\n onTogglePalette,\n isPaletteOpen = false,\n onPaste,\n onSave,\n isSaving = false,\n onExport,\n onPublishTemplate,\n onExportImage,\n onExportPdf,\n isExportingImage = false,\n onShare,\n}) {\n const isEditMode = useDashboardStore((state) => state.viewMode === 'edit');\n const toggleViewMode = useDashboardStore((state) => state.toggleViewMode);\n const zoomLevel = useDashboardStore((state) => state.zoomLevel);\n const setZoomLevel = useDashboardStore((state) => state.setZoomLevel);\n\n const clipboardWidgetId = useWidgetStore((state) => state.clipboardWidgetId);\n const hasClipboard = !!clipboardWidgetId;\n\n // Handle paste - use provided callback or fallback\n const handlePaste = useCallback(() => {\n if (hasClipboard && onPaste) {\n onPaste();\n }\n }, [hasClipboard, onPaste]);\n\n return (\n <TooltipProvider>\n <div className=\"flex flex-wrap items-center justify-between gap-x-4 gap-y-2 px-4 py-2 bg-bg-surface border-b\">\n {/* Left section */}\n <div className=\"flex min-w-0 items-center gap-4\">\n {title && (\n <h1 className=\"min-w-0 truncate text-lg font-semibold\" title={title}>\n {title}\n </h1>\n )}\n {/* Saved Views Dropdown */}\n {dashboardId && <SavedViewsDropdown dashboardId={dashboardId} />}\n </div>\n\n {/* Center section - Zoom controls */}\n <div className=\"flex flex-shrink-0 items-center gap-1\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setZoomLevel(Math.max(50, zoomLevel - 10))}\n disabled={zoomLevel <= 50}\n >\n <Minus className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Zoom out</TooltipContent>\n </Tooltip>\n\n <span className=\"text-sm text-text-secondary w-12 text-center\">{zoomLevel}%</span>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setZoomLevel(Math.min(200, zoomLevel + 10))}\n disabled={zoomLevel >= 200}\n >\n <Plus className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Zoom in</TooltipContent>\n </Tooltip>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setZoomLevel(100)}>\n <RotateCcw className=\"size-3.5 mr-1\" />\n Reset\n </Button>\n </TooltipTrigger>\n <TooltipContent>Reset zoom to 100%</TooltipContent>\n </Tooltip>\n </div>\n\n {/* Right section - Actions */}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {isEditMode && (\n <>\n {/* Widget Palette Toggle */}\n {onTogglePalette && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant={isPaletteOpen ? 'secondary' : 'outline'} size=\"sm\" onClick={onTogglePalette}>\n {isPaletteOpen ? <PanelLeftClose className=\"size-4\" /> : <PanelLeft className=\"size-4\" />}\n Widgets\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n {isPaletteOpen ? 'Hide widget palette' : 'Show widget palette for drag & drop'}\n </TooltipContent>\n </Tooltip>\n )}\n\n {/* Paste */}\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\" onClick={handlePaste} disabled={!hasClipboard}>\n <Clipboard className=\"size-4\" />\n Paste\n </Button>\n </TooltipTrigger>\n <TooltipContent>Paste widget from clipboard</TooltipContent>\n </Tooltip>\n\n {/* Add Widget (Dialog) */}\n <Button size=\"sm\" onClick={onAddWidget}>\n <Plus className=\"size-4\" />\n Add Widget\n </Button>\n </>\n )}\n\n {/* Edit/View Mode Toggle */}\n <Button variant={isEditMode ? 'secondary' : 'outline'} size=\"sm\" onClick={toggleViewMode}>\n {isEditMode ? (\n <>\n <Eye className=\"size-4\" />\n Preview\n </>\n ) : (\n <>\n <Pencil className=\"size-4\" />\n Edit\n </>\n )}\n </Button>\n\n {/* Save */}\n {onSave && (\n <Button variant=\"outline\" size=\"sm\" onClick={onSave} disabled={isSaving}>\n {isSaving ? (\n <>\n <Spinner className=\"size-4\" />\n Saving...\n </>\n ) : (\n <>\n <Save className=\"size-4\" />\n Save\n </>\n )}\n </Button>\n )}\n\n {/* Share */}\n {onShare && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onShare}>\n <Share2 className=\"size-4\" />\n Share\n </Button>\n </TooltipTrigger>\n <TooltipContent>Share dashboard with others</TooltipContent>\n </Tooltip>\n )}\n\n {/* Publish as a store template */}\n {onPublishTemplate && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onPublishTemplate}>\n <Store className=\"size-4\" />\n <span className=\"hidden sm:inline\">Publish</span>\n </Button>\n </TooltipTrigger>\n <TooltipContent>Publish this dashboard as a reusable template</TooltipContent>\n </Tooltip>\n )}\n\n {/* Export — JSON definition */}\n {onExport && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" onClick={onExport}>\n <Download className=\"size-4\" />\n Export\n </Button>\n </TooltipTrigger>\n <TooltipContent>Export dashboard to JSON</TooltipContent>\n </Tooltip>\n )}\n\n {/* Export — board PNG */}\n {onExportImage && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"icon-sm\" onClick={onExportImage} disabled={isExportingImage}>\n {isExportingImage ? <Spinner className=\"size-4\" /> : <ImageIcon className=\"size-4\" />}\n </Button>\n </TooltipTrigger>\n <TooltipContent>Export board as PNG image</TooltipContent>\n </Tooltip>\n )}\n\n {/* Export — board PDF */}\n {onExportPdf && (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"outline\" size=\"icon-sm\" onClick={onExportPdf} disabled={isExportingImage}>\n <FileText className=\"size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Export board as PDF</TooltipContent>\n </Tooltip>\n )}\n </div>\n </div>\n </TooltipProvider>\n );\n});\n\nexport default DashboardToolbar;\n"],"mappings":"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"}
@@ -0,0 +1,165 @@
1
+ import e from "../../hooks/useStoreDashboardTemplates.js";
2
+ import { memo as t, useCallback as n, useEffect as r, useState as i } from "react";
3
+ import { Button as a, Dialog as o, DialogContent as s, DialogDescription as c, DialogFooter as l, DialogHeader as u, DialogTitle as d, Input as f, Spinner as p, Textarea as m } from "@burdenoff/fe-libs/ui";
4
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
5
+ //#region src/bigconsole/components/dashboard/PublishToStoreDialog.tsx
6
+ function _(e) {
7
+ return e.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean).join("-").slice(0, 60);
8
+ }
9
+ var v = t(function({ isOpen: t, dashboardId: v, dashboardName: y, dashboardDescription: b, onClose: x, onPublished: S }) {
10
+ let { publishToStore: C } = e({ autoLoad: !1 }), [w, T] = i(y), [E, D] = i(_(y)), [O, k] = i(b ?? ""), [A, j] = i(!1), [M, N] = i(null), [P, F] = i(null);
11
+ r(() => {
12
+ t && (T(y), D(_(y)), k(b ?? ""), N(null), F(null));
13
+ }, [
14
+ t,
15
+ y,
16
+ b
17
+ ]);
18
+ let I = n(async () => {
19
+ if (!w.trim()) {
20
+ N("A template name is required");
21
+ return;
22
+ }
23
+ if (!E.trim()) {
24
+ N("A slug is required");
25
+ return;
26
+ }
27
+ if (!O.trim()) {
28
+ N("A description is required — it is what people see in the gallery");
29
+ return;
30
+ }
31
+ N(null), j(!0);
32
+ try {
33
+ let e = await C({
34
+ dashboardId: v,
35
+ name: w.trim(),
36
+ slug: E.trim(),
37
+ description: O.trim()
38
+ });
39
+ if (!e.success) {
40
+ N(e.errorMessage ?? "Failed to publish to the store");
41
+ return;
42
+ }
43
+ F(e), S?.(e);
44
+ } finally {
45
+ j(!1);
46
+ }
47
+ }, [
48
+ w,
49
+ E,
50
+ O,
51
+ v,
52
+ C,
53
+ S
54
+ ]), L = {
55
+ display: "block",
56
+ marginBottom: "var(--space-2xs)",
57
+ fontSize: "var(--font-size-sm)"
58
+ };
59
+ return /* @__PURE__ */ h(o, {
60
+ open: t,
61
+ onOpenChange: (e) => !e && !A && x(),
62
+ children: /* @__PURE__ */ g(s, {
63
+ style: { maxWidth: "520px" },
64
+ children: [
65
+ /* @__PURE__ */ g(u, { children: [/* @__PURE__ */ h(d, { children: "Publish as Template" }), /* @__PURE__ */ h(c, { children: "Share this dashboard as a reusable template. Your data sources are replaced with named slots, so nothing from this workspace is published." })] }),
66
+ P ? /* @__PURE__ */ g("div", {
67
+ style: { padding: "var(--space-md) 0" },
68
+ children: [
69
+ /* @__PURE__ */ h("p", {
70
+ style: { fontWeight: "var(--font-weight-medium)" },
71
+ children: "Draft template created"
72
+ }),
73
+ /* @__PURE__ */ g("p", {
74
+ style: {
75
+ fontSize: "var(--font-size-sm)",
76
+ color: "var(--color-text-muted)"
77
+ },
78
+ children: [
79
+ P.sinkCount,
80
+ " data source",
81
+ P.sinkCount === 1 ? "" : "s",
82
+ " turned into slots. Finish in the Developer Portal: review the listing, then submit it for approval. It appears in the gallery once approved."
83
+ ]
84
+ }),
85
+ P.unknownSinkIds.length > 0 && /* @__PURE__ */ g("p", {
86
+ style: {
87
+ fontSize: "var(--font-size-sm)",
88
+ color: "var(--color-text-warning)"
89
+ },
90
+ children: [
91
+ P.unknownSinkIds.length,
92
+ " data source",
93
+ P.unknownSinkIds.length === 1 ? "" : "s",
94
+ " could not be named — those slots use generic labels."
95
+ ]
96
+ })
97
+ ]
98
+ }) : /* @__PURE__ */ g("div", {
99
+ style: {
100
+ display: "flex",
101
+ flexDirection: "column",
102
+ gap: "var(--space-sm)"
103
+ },
104
+ children: [
105
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
106
+ htmlFor: "tpl-name",
107
+ style: L,
108
+ children: "Template name"
109
+ }), /* @__PURE__ */ h(f, {
110
+ id: "tpl-name",
111
+ value: w,
112
+ disabled: A,
113
+ onChange: (e) => {
114
+ T(e.target.value), D(_(e.target.value));
115
+ }
116
+ })] }),
117
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
118
+ htmlFor: "tpl-slug",
119
+ style: L,
120
+ children: "Slug"
121
+ }), /* @__PURE__ */ h(f, {
122
+ id: "tpl-slug",
123
+ value: E,
124
+ disabled: A,
125
+ onChange: (e) => D(_(e.target.value))
126
+ })] }),
127
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
128
+ htmlFor: "tpl-desc",
129
+ style: L,
130
+ children: "Description"
131
+ }), /* @__PURE__ */ h(m, {
132
+ id: "tpl-desc",
133
+ rows: 3,
134
+ value: O,
135
+ disabled: A,
136
+ placeholder: "What is this dashboard for, and what data does it need?",
137
+ onChange: (e) => k(e.target.value)
138
+ })] }),
139
+ M && /* @__PURE__ */ h("p", {
140
+ style: {
141
+ color: "var(--color-text-danger)",
142
+ fontSize: "var(--font-size-sm)"
143
+ },
144
+ children: M
145
+ })
146
+ ]
147
+ }),
148
+ /* @__PURE__ */ g(l, { children: [/* @__PURE__ */ h(a, {
149
+ variant: "outline",
150
+ onClick: x,
151
+ disabled: A,
152
+ children: P ? "Done" : "Cancel"
153
+ }), !P && /* @__PURE__ */ h(a, {
154
+ onClick: I,
155
+ disabled: A,
156
+ children: A ? /* @__PURE__ */ h(p, {}) : "Publish"
157
+ })] })
158
+ ]
159
+ })
160
+ });
161
+ });
162
+ //#endregion
163
+ export { v as default };
164
+
165
+ //# sourceMappingURL=PublishToStoreDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PublishToStoreDialog.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/PublishToStoreDialog.tsx"],"sourcesContent":["/**\n * PublishToStoreDialog\n *\n * Turns the open dashboard into a store template.\n *\n * WHY THIS EXISTS:\n * A workflow template's {nodes, edges} can plausibly be hand-written into the\n * developer portal. A dashboard export — every widget with its layout\n * coordinates, bindings and filters — cannot. Without this, authoring a\n * dashboard template means hand-crafting a large JSON document, which nobody\n * will do, and the gallery stays empty.\n *\n * The backend tokenises the board (replacing this workspace's data-sink\n * references with named slots), then a DRAFT Dashboard Template application is\n * created in the developer portal from the result. It deliberately stops there\n * rather than writing a store product directly: devportal already owns\n * versioning, review and publisher identity for dashboard templates, and\n * forking that pipeline would orphan the listing.\n *\n * @module bigconsole/components/dashboard/PublishToStoreDialog\n */\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n Spinner,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { type FC, memo, useCallback, useEffect, useState } from 'react';\n\nimport { useStoreDashboardTemplates, type PublishToStoreResult } from '../../hooks/useStoreDashboardTemplates';\n\nexport interface PublishToStoreDialogProps {\n isOpen: boolean;\n dashboardId: string;\n dashboardName: string;\n dashboardDescription?: string | null;\n onClose: () => void;\n onPublished?: (result: PublishToStoreResult) => void;\n}\n\n/** Store slugs are lowercase, hyphenated and unique. */\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter(Boolean)\n .join('-')\n .slice(0, 60);\n}\n\nexport const PublishToStoreDialog: FC<PublishToStoreDialogProps> = memo(function PublishToStoreDialog({\n isOpen,\n dashboardId,\n dashboardName,\n dashboardDescription,\n onClose,\n onPublished,\n}) {\n const { publishToStore } = useStoreDashboardTemplates({ autoLoad: false });\n\n const [name, setName] = useState(dashboardName);\n const [slug, setSlug] = useState(slugify(dashboardName));\n const [description, setDescription] = useState(dashboardDescription ?? '');\n const [publishing, setPublishing] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [result, setResult] = useState<PublishToStoreResult | null>(null);\n\n useEffect(() => {\n if (!isOpen) return;\n setName(dashboardName);\n setSlug(slugify(dashboardName));\n setDescription(dashboardDescription ?? '');\n setError(null);\n setResult(null);\n }, [isOpen, dashboardName, dashboardDescription]);\n\n const handlePublish = useCallback(async () => {\n if (!name.trim()) {\n setError('A template name is required');\n return;\n }\n if (!slug.trim()) {\n setError('A slug is required');\n return;\n }\n if (!description.trim()) {\n setError('A description is required — it is what people see in the gallery');\n return;\n }\n\n setError(null);\n setPublishing(true);\n try {\n const published = await publishToStore({\n dashboardId,\n name: name.trim(),\n slug: slug.trim(),\n description: description.trim(),\n });\n if (!published.success) {\n setError(published.errorMessage ?? 'Failed to publish to the store');\n return;\n }\n setResult(published);\n onPublished?.(published);\n } finally {\n setPublishing(false);\n }\n }, [name, slug, description, dashboardId, publishToStore, onPublished]);\n\n const label = { display: 'block', marginBottom: 'var(--space-2xs)', fontSize: 'var(--font-size-sm)' };\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && !publishing && onClose()}>\n <DialogContent style={{ maxWidth: '520px' }}>\n <DialogHeader>\n <DialogTitle>Publish as Template</DialogTitle>\n <DialogDescription>\n Share this dashboard as a reusable template. Your data sources are replaced with named slots, so nothing\n from this workspace is published.\n </DialogDescription>\n </DialogHeader>\n\n {result ? (\n <div style={{ padding: 'var(--space-md) 0' }}>\n <p style={{ fontWeight: 'var(--font-weight-medium)' }}>Draft template created</p>\n <p style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)' }}>\n {result.sinkCount} data source{result.sinkCount === 1 ? '' : 's'} turned into slots. Finish in the\n Developer Portal: review the listing, then submit it for approval. It appears in the gallery once\n approved.\n </p>\n {result.unknownSinkIds.length > 0 && (\n <p style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-warning)' }}>\n {result.unknownSinkIds.length} data source\n {result.unknownSinkIds.length === 1 ? '' : 's'} could not be named — those slots use generic labels.\n </p>\n )}\n </div>\n ) : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-sm)' }}>\n <div>\n <label htmlFor=\"tpl-name\" style={label}>\n Template name\n </label>\n <Input\n id=\"tpl-name\"\n value={name}\n disabled={publishing}\n onChange={(e) => {\n setName(e.target.value);\n setSlug(slugify(e.target.value));\n }}\n />\n </div>\n\n <div>\n <label htmlFor=\"tpl-slug\" style={label}>\n Slug\n </label>\n <Input\n id=\"tpl-slug\"\n value={slug}\n disabled={publishing}\n onChange={(e) => setSlug(slugify(e.target.value))}\n />\n </div>\n\n <div>\n <label htmlFor=\"tpl-desc\" style={label}>\n Description\n </label>\n <Textarea\n id=\"tpl-desc\"\n rows={3}\n value={description}\n disabled={publishing}\n placeholder=\"What is this dashboard for, and what data does it need?\"\n onChange={(e) => setDescription(e.target.value)}\n />\n </div>\n\n {error && <p style={{ color: 'var(--color-text-danger)', fontSize: 'var(--font-size-sm)' }}>{error}</p>}\n </div>\n )}\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={onClose} disabled={publishing}>\n {result ? 'Done' : 'Cancel'}\n </Button>\n {!result && (\n <Button onClick={handlePublish} disabled={publishing}>\n {publishing ? <Spinner /> : 'Publish'}\n </Button>\n )}\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default PublishToStoreDialog;\n"],"mappings":"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"}
@@ -23,3 +23,4 @@ import "./ExportDashboardDialog.js";
23
23
  import "./ImportDashboardDialog.js";
24
24
  import "./TemplatePicker/index.js";
25
25
  import "./DrilldownBreadcrumb.js";
26
+ import "./PublishToStoreDialog.js";
@@ -20,8 +20,26 @@ var t = e`
20
20
  }
21
21
  }
22
22
  }
23
+ `, n = e`
24
+ query BuildDashboardTemplateContent(
25
+ $dashboardId: ID!
26
+ $defaultDashboardName: String
27
+ $defaultCategory: String
28
+ $defaultTags: [String!]
29
+ ) {
30
+ buildDashboardTemplateContent(
31
+ dashboardId: $dashboardId
32
+ defaultDashboardName: $defaultDashboardName
33
+ defaultCategory: $defaultCategory
34
+ defaultTags: $defaultTags
35
+ ) {
36
+ content
37
+ sinkCount
38
+ unknownSinkIds
39
+ }
40
+ }
23
41
  `;
24
42
  //#endregion
25
- export { t as IMPORT_DASHBOARD_TEMPLATE };
43
+ export { n as BUILD_DASHBOARD_TEMPLATE_CONTENT, t as IMPORT_DASHBOARD_TEMPLATE };
26
44
 
27
45
  //# sourceMappingURL=storeTemplates.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storeTemplates.js","names":[],"sources":["../../../src/bigconsole/graphql/storeTemplates.ts"],"sourcesContent":["/**\n * GraphQL documents for store-distributed dashboard templates.\n *\n * The two catalog reads are generated from\n * `src/operations/global/store/queries/*.graphql` in the usual way.\n *\n * `IMPORT_DASHBOARD_TEMPLATE` is defined inline as an interim measure.\n * `importDashboardTemplate` is new in wspace-bigconsole-svc, so it is not yet\n * present in `graphql/wspace.graphql` — that file is only ever refreshed by\n * `bun run schema:pull` against a live gateway (hand-editing it is a repo\n * guardrail), and the gateway supergraph only carries the field once the\n * service has deployed and Hive has reconciled.\n *\n * It cannot simply be dropped into `src/operations/wspace/**` yet either:\n * `src/generated` is gitignored and CI regenerates it, so an operation file\n * referencing a field the checked-in schema lacks fails codegen and breaks the\n * build. Once `schema:pull` has run, move this document into\n * `src/operations/wspace/dashboard/mutations/importDashboardTemplate.graphql`\n * and import the generated `ImportDashboardTemplateDocument` instead — the\n * shape is unchanged.\n *\n * @module bigconsole/graphql/storeTemplates\n */\nimport { gql } from '@apollo/client';\n\nexport {\n BrowseDashboardTemplatesDocument as BROWSE_DASHBOARD_TEMPLATES,\n GetDashboardTemplateDetailDocument as GET_DASHBOARD_TEMPLATE_DETAIL,\n} from '../../generated/global-operations';\n\nexport const IMPORT_DASHBOARD_TEMPLATE = gql`\n mutation ImportDashboardTemplate($input: ImportDashboardTemplateInput!) {\n importDashboardTemplate(input: $input) {\n success\n errorMessage\n unresolvedSlots\n resolvedSlots\n widgetsNeedingData\n warnings\n stats {\n pagesCreated\n widgetsCreated\n }\n dashboard {\n id\n name\n }\n }\n }\n`;\n"],"mappings":";;;AA8BA,IAAa,IAA4B,CAAG"}
1
+ {"version":3,"file":"storeTemplates.js","names":[],"sources":["../../../src/bigconsole/graphql/storeTemplates.ts"],"sourcesContent":["/**\n * GraphQL documents for store-distributed dashboard templates.\n *\n * The two catalog reads are generated from\n * `src/operations/global/store/queries/*.graphql` in the usual way.\n *\n * `IMPORT_DASHBOARD_TEMPLATE` is defined inline as an interim measure.\n * `importDashboardTemplate` is new in wspace-bigconsole-svc, so it is not yet\n * present in `graphql/wspace.graphql` — that file is only ever refreshed by\n * `bun run schema:pull` against a live gateway (hand-editing it is a repo\n * guardrail), and the gateway supergraph only carries the field once the\n * service has deployed and Hive has reconciled.\n *\n * It cannot simply be dropped into `src/operations/wspace/**` yet either:\n * `src/generated` is gitignored and CI regenerates it, so an operation file\n * referencing a field the checked-in schema lacks fails codegen and breaks the\n * build. Once `schema:pull` has run, move this document into\n * `src/operations/wspace/dashboard/mutations/importDashboardTemplate.graphql`\n * and import the generated `ImportDashboardTemplateDocument` instead — the\n * shape is unchanged.\n *\n * @module bigconsole/graphql/storeTemplates\n */\nimport { gql } from '@apollo/client';\n\nexport {\n BrowseDashboardTemplatesDocument as BROWSE_DASHBOARD_TEMPLATES,\n GetDashboardTemplateDetailDocument as GET_DASHBOARD_TEMPLATE_DETAIL,\n CreateDashboardTemplateAppDocument as CREATE_TEMPLATE_APP,\n CreateDashboardTemplateVersionDocument as CREATE_TEMPLATE_VERSION,\n} from '../../generated/global-operations';\n\nexport const IMPORT_DASHBOARD_TEMPLATE = gql`\n mutation ImportDashboardTemplate($input: ImportDashboardTemplateInput!) {\n importDashboardTemplate(input: $input) {\n success\n errorMessage\n unresolvedSlots\n resolvedSlots\n widgetsNeedingData\n warnings\n stats {\n pagesCreated\n widgetsCreated\n }\n dashboard {\n id\n name\n }\n }\n }\n`;\n\n/**\n * Same interim treatment as IMPORT_DASHBOARD_TEMPLATE, and for the same reason:\n * buildDashboardTemplateContent is new in wspace-bigconsole-svc and is not yet\n * in the checked-in wspace schema.\n */\nexport const BUILD_DASHBOARD_TEMPLATE_CONTENT = gql`\n query BuildDashboardTemplateContent(\n $dashboardId: ID!\n $defaultDashboardName: String\n $defaultCategory: String\n $defaultTags: [String!]\n ) {\n buildDashboardTemplateContent(\n dashboardId: $dashboardId\n defaultDashboardName: $defaultDashboardName\n defaultCategory: $defaultCategory\n defaultTags: $defaultTags\n ) {\n content\n sinkCount\n unknownSinkIds\n }\n }\n`;\n"],"mappings":";;;AAgCA,IAAa,IAA4B,CAAG;;;;;;;;;;;;;;;;;;;GA0B/B,IAAmC,CAAG"}
@@ -1,10 +1,10 @@
1
- import { BrowseDashboardTemplatesDocument as e, GetDashboardTemplateDetailDocument as t } from "../../generated/global-operations.js";
2
- import { IMPORT_DASHBOARD_TEMPLATE as n } from "../graphql/storeTemplates.js";
3
- import { useCallback as r, useEffect as i, useMemo as a, useState as o } from "react";
4
- import { useApolloClient as s } from "@apollo/client/react";
5
- import { useGatewayClient as c } from "@burdenoff/fe-libs/shared/graphql";
1
+ import { BrowseDashboardTemplatesDocument as e, CreateDashboardTemplateAppDocument as t, CreateDashboardTemplateVersionDocument as n, GetDashboardTemplateDetailDocument as r } from "../../generated/global-operations.js";
2
+ import { BUILD_DASHBOARD_TEMPLATE_CONTENT as i, IMPORT_DASHBOARD_TEMPLATE as a } from "../graphql/storeTemplates.js";
3
+ import { useCallback as o, useEffect as s, useMemo as c, useState as l } from "react";
4
+ import { useApolloClient as u } from "@apollo/client/react";
5
+ import { useGatewayClient as d } from "@burdenoff/fe-libs/shared/graphql";
6
6
  //#region src/bigconsole/hooks/useStoreDashboardTemplates.ts
7
- function l(e) {
7
+ function f(e) {
8
8
  let t = e.pricingModel ?? "FREE";
9
9
  return {
10
10
  id: e.id,
@@ -22,20 +22,20 @@ function l(e) {
22
22
  requiresPurchase: t !== "FREE" && (e.price ?? 0) > 0
23
23
  };
24
24
  }
25
- function u(u = {}) {
26
- let { search: d, category: f, autoLoad: p = !0 } = u, m = c("global"), h = s(), [g, _] = o([]), [v, y] = o(0), [b, x] = o(!1), [S, C] = o(null), w = r(async (t) => {
27
- if (m) {
28
- x(!0), C(null);
25
+ function p(p = {}) {
26
+ let { search: m, category: h, autoLoad: g = !0 } = p, _ = d("global"), v = u(), [y, b] = l([]), [x, S] = l(0), [C, w] = l(!1), [T, E] = l(null), D = o(async (t) => {
27
+ if (_) {
28
+ w(!0), E(null);
29
29
  try {
30
- let n = await m.query({
30
+ let n = await _.query({
31
31
  query: e,
32
32
  variables: { input: {
33
33
  filter: {
34
34
  type: "DASHBOARD",
35
35
  status: "PUBLISHED",
36
36
  compatibleWith: "bigconsole",
37
- ...t ?? d ? { search: t ?? d } : {},
38
- ...f && f !== "ALL" ? { category: f } : {}
37
+ ...t ?? m ? { search: t ?? m } : {},
38
+ ...h && h !== "ALL" ? { category: h } : {}
39
39
  },
40
40
  sortBy: "RELEVANCE",
41
41
  pagination: {
@@ -45,47 +45,47 @@ function u(u = {}) {
45
45
  } },
46
46
  fetchPolicy: "network-only"
47
47
  });
48
- _((n.data?.browseStore?.products ?? []).map(l)), y(n.data?.browseStore?.totalCount ?? 0);
48
+ b((n.data?.browseStore?.products ?? []).map(f)), S(n.data?.browseStore?.totalCount ?? 0);
49
49
  } catch (e) {
50
- C(e instanceof Error ? e.message : "Failed to load templates from the store"), _([]), y(0);
50
+ E(e instanceof Error ? e.message : "Failed to load templates from the store"), b([]), S(0);
51
51
  } finally {
52
- x(!1);
52
+ w(!1);
53
53
  }
54
54
  }
55
55
  }, [
56
+ _,
56
57
  m,
57
- d,
58
- f
58
+ h
59
59
  ]);
60
- i(() => {
61
- p && w();
62
- }, [p, w]);
63
- let T = r(async (e) => {
64
- if (!m) return null;
60
+ s(() => {
61
+ g && D();
62
+ }, [g, D]);
63
+ let O = o(async (e) => {
64
+ if (!_) return null;
65
65
  try {
66
- let n = (await m.query({
67
- query: t,
66
+ let t = (await _.query({
67
+ query: r,
68
68
  variables: { id: e },
69
69
  fetchPolicy: "network-only"
70
70
  })).data?.product?.content;
71
- if (!n || typeof n != "object") return null;
72
- let r = n;
73
- return r.definition ? {
74
- definition: r.definition,
75
- requiredDataSinks: r.requiredDataSinks ?? [],
76
- requiredWidgetTypes: r.requiredWidgetTypes ?? [],
77
- requiredInputs: r.requiredInputs ?? [],
78
- defaultDashboardName: r.defaultDashboardName,
79
- defaultCategory: r.defaultCategory ?? null,
80
- defaultTags: r.defaultTags ?? [],
81
- difficultyLevel: r.difficultyLevel ?? "medium"
71
+ if (!t || typeof t != "object") return null;
72
+ let n = t;
73
+ return n.definition ? {
74
+ definition: n.definition,
75
+ requiredDataSinks: n.requiredDataSinks ?? [],
76
+ requiredWidgetTypes: n.requiredWidgetTypes ?? [],
77
+ requiredInputs: n.requiredInputs ?? [],
78
+ defaultDashboardName: n.defaultDashboardName,
79
+ defaultCategory: n.defaultCategory ?? null,
80
+ defaultTags: n.defaultTags ?? [],
81
+ difficultyLevel: n.difficultyLevel ?? "medium"
82
82
  } : null;
83
83
  } catch {
84
84
  return null;
85
85
  }
86
- }, [m]), E = r(async (e) => {
87
- let t = (await h.mutate({
88
- mutation: n,
86
+ }, [_]), k = o(async (e) => {
87
+ let t = (await v.mutate({
88
+ mutation: a,
89
89
  variables: { input: {
90
90
  content: e.content,
91
91
  sourceTemplateId: e.templateId,
@@ -112,26 +112,94 @@ function u(u = {}) {
112
112
  widgetsNeedingData: 0,
113
113
  warnings: []
114
114
  };
115
- }, [h]);
116
- return a(() => ({
117
- templates: g,
118
- loading: b,
119
- error: S,
120
- totalCount: v,
121
- refetch: w,
122
- fetchTemplateContent: T,
123
- installTemplate: E
115
+ }, [v]), A = o(async (e) => {
116
+ let r = (e) => ({
117
+ success: !1,
118
+ errorMessage: e,
119
+ sinkCount: 0,
120
+ unknownSinkIds: []
121
+ });
122
+ if (!_) return r("Store is unavailable right now.");
123
+ let a, o = 0, s = [];
124
+ try {
125
+ let t = (await v.query({
126
+ query: i,
127
+ variables: {
128
+ dashboardId: e.dashboardId,
129
+ defaultDashboardName: e.name,
130
+ defaultCategory: e.category ?? null,
131
+ defaultTags: e.tags ?? []
132
+ },
133
+ fetchPolicy: "network-only"
134
+ })).data?.buildDashboardTemplateContent;
135
+ if (!t?.content) return r("Could not build a template from this dashboard.");
136
+ a = t.content, o = t.sinkCount, s = t.unknownSinkIds ?? [];
137
+ } catch (e) {
138
+ return r(e instanceof Error ? e.message : "Failed to build the template.");
139
+ }
140
+ try {
141
+ let i = (await _.mutate({
142
+ mutation: t,
143
+ variables: { input: {
144
+ appType: "DASHBOARD",
145
+ name: e.name,
146
+ slug: e.slug,
147
+ shortDescription: e.description.slice(0, 200),
148
+ description: e.description,
149
+ ...e.category ? { category: e.category } : {},
150
+ tags: e.tags ?? [],
151
+ compatibleProducts: ["bigconsole"]
152
+ } }
153
+ })).data?.createApplication;
154
+ return i ? (await _.mutate({
155
+ mutation: n,
156
+ variables: {
157
+ applicationId: i.id,
158
+ input: {
159
+ version: "1.0.0",
160
+ manifest: a
161
+ }
162
+ }
163
+ }), {
164
+ success: !0,
165
+ applicationId: i.id,
166
+ status: i.status,
167
+ sinkCount: o,
168
+ unknownSinkIds: s
169
+ }) : {
170
+ ...r("The developer portal did not create the app."),
171
+ sinkCount: o,
172
+ unknownSinkIds: s
173
+ };
174
+ } catch (e) {
175
+ return {
176
+ ...r(e instanceof Error ? e.message : "Failed to publish to the developer portal."),
177
+ sinkCount: o,
178
+ unknownSinkIds: s
179
+ };
180
+ }
181
+ }, [_, v]);
182
+ return c(() => ({
183
+ templates: y,
184
+ loading: C,
185
+ error: T,
186
+ totalCount: x,
187
+ refetch: D,
188
+ fetchTemplateContent: O,
189
+ installTemplate: k,
190
+ publishToStore: A
124
191
  }), [
125
- g,
126
- b,
127
- S,
128
- v,
129
- w,
192
+ y,
193
+ C,
130
194
  T,
131
- E
195
+ x,
196
+ D,
197
+ O,
198
+ k,
199
+ A
132
200
  ]);
133
201
  }
134
202
  //#endregion
135
- export { u as default };
203
+ export { p as default };
136
204
 
137
205
  //# sourceMappingURL=useStoreDashboardTemplates.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useStoreDashboardTemplates.js","names":[],"sources":["../../../src/bigconsole/hooks/useStoreDashboardTemplates.ts"],"sourcesContent":["/**\n * Store-distributed dashboard templates.\n *\n * Dashboard templates live in the global store as `StoreProduct` rows of type\n * `DASHBOARD` whose `content` column holds a `DashboardTemplateContent`\n * document. This mirrors how FluidGrids sources its workflow templates\n * (`browseStore(type: WORKFLOW)` → `content.definitionJson` →\n * `importWorkflowTemplate`).\n *\n * Two gateways are involved: the catalog is read from the **global** gateway,\n * and the install mutation runs against the **workspace** gateway, which is\n * where the dashboard is created.\n *\n * @module bigconsole/hooks/useStoreDashboardTemplates\n */\nimport { useApolloClient } from '@apollo/client/react';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport {\n BROWSE_DASHBOARD_TEMPLATES,\n GET_DASHBOARD_TEMPLATE_DETAIL,\n IMPORT_DASHBOARD_TEMPLATE,\n} from '../graphql/storeTemplates';\n\n/** A column a widget expects from the sink bound to its slot. */\nexport interface TemplateExpectedField {\n name: string;\n role?: string;\n}\n\n/** A data-binding slot the template needs filled. */\nexport interface TemplateRequiredDataSink {\n slot: string;\n label: string;\n description?: string | null;\n expectedFields: TemplateExpectedField[];\n widgetTitles: string[];\n}\n\nexport interface TemplateRequiredWidgetType {\n widgetType: string;\n widgetTypeKey?: string | null;\n publishType: 'BUILTIN' | 'STORE' | 'CUSTOM';\n displayName?: string;\n requiresPurchase?: boolean;\n}\n\nexport interface TemplateRequiredInput {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'select' | 'secret';\n label: string;\n placeholder?: string;\n required: boolean;\n defaultValue?: string | number | boolean | null;\n options?: { label: string; value: string }[];\n helpText?: string;\n}\n\nexport interface DashboardTemplateContent {\n definition: unknown;\n requiredDataSinks: TemplateRequiredDataSink[];\n requiredWidgetTypes: TemplateRequiredWidgetType[];\n requiredInputs: TemplateRequiredInput[];\n defaultDashboardName?: string;\n defaultCategory?: string | null;\n defaultTags?: string[];\n difficultyLevel?: 'easy' | 'medium' | 'hard';\n}\n\n/** A store product presented as a template card. */\nexport interface StoreDashboardTemplate {\n id: string;\n name: string;\n slug: string;\n description?: string | null;\n icon?: string | null;\n category?: string | null;\n tags: string[];\n featured: boolean;\n downloads: number;\n rating?: number | null;\n pricingModel: string;\n price: number;\n /** True for anything the workspace has to buy before it can be installed. */\n requiresPurchase: boolean;\n}\n\ninterface RawStoreProduct {\n id: string;\n name: string;\n slug: string;\n description?: string | null;\n icon?: string | null;\n category?: string | null;\n tags?: string[] | null;\n featured?: boolean | null;\n downloads?: number | null;\n rating?: number | null;\n pricingModel?: string | null;\n price?: number | null;\n}\n\nexport interface InstallTemplateArgs {\n templateId: string;\n templateName: string;\n content: DashboardTemplateContent;\n name?: string;\n visibility?: string;\n dataSinkBindings?: { slot: string; dataSinkId: string }[];\n inputValues?: Record<string, unknown>;\n}\n\nexport interface InstallTemplateResult {\n success: boolean;\n dashboardId?: string;\n errorMessage?: string;\n unresolvedSlots: string[];\n resolvedSlots: string[];\n widgetsNeedingData: number;\n warnings: string[];\n}\n\nfunction normalizeProduct(raw: RawStoreProduct): StoreDashboardTemplate {\n const pricingModel = raw.pricingModel ?? 'FREE';\n return {\n id: raw.id,\n name: raw.name,\n slug: raw.slug,\n description: raw.description ?? null,\n icon: raw.icon ?? null,\n category: raw.category ?? null,\n tags: raw.tags ?? [],\n featured: raw.featured ?? false,\n downloads: raw.downloads ?? 0,\n rating: raw.rating ?? null,\n pricingModel,\n price: raw.price ?? 0,\n requiresPurchase: pricingModel !== 'FREE' && (raw.price ?? 0) > 0,\n };\n}\n\nexport interface UseStoreDashboardTemplatesResult {\n templates: StoreDashboardTemplate[];\n loading: boolean;\n error: string | null;\n totalCount: number;\n refetch: (search?: string) => Promise<void>;\n /** Fetch the full template document. Kept lazy: content is large. */\n fetchTemplateContent: (templateId: string) => Promise<DashboardTemplateContent | null>;\n installTemplate: (args: InstallTemplateArgs) => Promise<InstallTemplateResult>;\n}\n\nexport function useStoreDashboardTemplates(\n options: { search?: string; category?: string; autoLoad?: boolean } = {}\n): UseStoreDashboardTemplatesResult {\n const { search, category, autoLoad = true } = options;\n\n const globalClient = useGatewayClient('global');\n const workspaceClient = useApolloClient();\n\n const [templates, setTemplates] = useState<StoreDashboardTemplate[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const refetch = useCallback(\n async (overrideSearch?: string): Promise<void> => {\n if (!globalClient) return;\n setLoading(true);\n setError(null);\n try {\n const result = await globalClient.query<{\n browseStore?: { products?: RawStoreProduct[]; totalCount?: number };\n }>({\n query: BROWSE_DASHBOARD_TEMPLATES,\n variables: {\n input: {\n filter: {\n type: 'DASHBOARD',\n status: 'PUBLISHED',\n // Without this a template published from another product's\n // devportal listing would show up in BigConsole's gallery.\n compatibleWith: 'bigconsole',\n ...((overrideSearch ?? search) ? { search: overrideSearch ?? search } : {}),\n ...(category && category !== 'ALL' ? { category } : {}),\n },\n sortBy: 'RELEVANCE',\n pagination: { limit: 50, offset: 0 },\n },\n },\n fetchPolicy: 'network-only',\n });\n\n setTemplates((result.data?.browseStore?.products ?? []).map(normalizeProduct));\n setTotalCount(result.data?.browseStore?.totalCount ?? 0);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load templates from the store');\n setTemplates([]);\n setTotalCount(0);\n } finally {\n setLoading(false);\n }\n },\n [globalClient, search, category]\n );\n\n useEffect(() => {\n if (autoLoad) void refetch();\n }, [autoLoad, refetch]);\n\n const fetchTemplateContent = useCallback(\n async (templateId: string): Promise<DashboardTemplateContent | null> => {\n if (!globalClient) return null;\n try {\n const result = await globalClient.query<{ product?: { content?: unknown } }>({\n query: GET_DASHBOARD_TEMPLATE_DETAIL,\n variables: { id: templateId },\n fetchPolicy: 'network-only',\n });\n const content = result.data?.product?.content;\n if (!content || typeof content !== 'object') return null;\n const doc = content as Partial<DashboardTemplateContent>;\n if (!doc.definition) return null;\n return {\n definition: doc.definition,\n requiredDataSinks: doc.requiredDataSinks ?? [],\n requiredWidgetTypes: doc.requiredWidgetTypes ?? [],\n requiredInputs: doc.requiredInputs ?? [],\n defaultDashboardName: doc.defaultDashboardName,\n defaultCategory: doc.defaultCategory ?? null,\n defaultTags: doc.defaultTags ?? [],\n difficultyLevel: doc.difficultyLevel ?? 'medium',\n };\n } catch {\n return null;\n }\n },\n [globalClient]\n );\n\n const installTemplate = useCallback(\n async (args: InstallTemplateArgs): Promise<InstallTemplateResult> => {\n const result = await workspaceClient.mutate<{\n importDashboardTemplate?: InstallTemplateResult & { dashboard?: { id: string } | null };\n }>({\n mutation: IMPORT_DASHBOARD_TEMPLATE,\n variables: {\n input: {\n content: args.content,\n sourceTemplateId: args.templateId,\n sourceTemplateName: args.templateName,\n ...(args.name ? { name: args.name } : {}),\n ...(args.visibility ? { visibility: args.visibility } : {}),\n ...(args.dataSinkBindings?.length ? { dataSinkBindings: args.dataSinkBindings } : {}),\n ...(args.inputValues && Object.keys(args.inputValues).length ? { inputValues: args.inputValues } : {}),\n },\n },\n });\n\n const payload = result.data?.importDashboardTemplate;\n if (!payload) {\n return {\n success: false,\n errorMessage: 'No response from the server',\n unresolvedSlots: [],\n resolvedSlots: [],\n widgetsNeedingData: 0,\n warnings: [],\n };\n }\n\n return {\n success: payload.success,\n dashboardId: payload.dashboard?.id,\n errorMessage: payload.errorMessage,\n unresolvedSlots: payload.unresolvedSlots ?? [],\n resolvedSlots: payload.resolvedSlots ?? [],\n widgetsNeedingData: payload.widgetsNeedingData ?? 0,\n warnings: payload.warnings ?? [],\n };\n },\n [workspaceClient]\n );\n\n return useMemo(\n () => ({\n templates,\n loading,\n error,\n totalCount,\n refetch,\n fetchTemplateContent,\n installTemplate,\n }),\n [templates, loading, error, totalCount, refetch, fetchTemplateContent, installTemplate]\n );\n}\n\nexport default useStoreDashboardTemplates;\n"],"mappings":"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"}
1
+ {"version":3,"file":"useStoreDashboardTemplates.js","names":[],"sources":["../../../src/bigconsole/hooks/useStoreDashboardTemplates.ts"],"sourcesContent":["/**\n * Store-distributed dashboard templates.\n *\n * Dashboard templates live in the global store as `StoreProduct` rows of type\n * `DASHBOARD` whose `content` column holds a `DashboardTemplateContent`\n * document. This mirrors how FluidGrids sources its workflow templates\n * (`browseStore(type: WORKFLOW)` → `content.definitionJson` →\n * `importWorkflowTemplate`).\n *\n * Two gateways are involved: the catalog is read from the **global** gateway,\n * and the install mutation runs against the **workspace** gateway, which is\n * where the dashboard is created.\n *\n * @module bigconsole/hooks/useStoreDashboardTemplates\n */\nimport { useApolloClient } from '@apollo/client/react';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport {\n BROWSE_DASHBOARD_TEMPLATES,\n BUILD_DASHBOARD_TEMPLATE_CONTENT,\n CREATE_TEMPLATE_APP,\n CREATE_TEMPLATE_VERSION,\n GET_DASHBOARD_TEMPLATE_DETAIL,\n IMPORT_DASHBOARD_TEMPLATE,\n} from '../graphql/storeTemplates';\n\n/** A column a widget expects from the sink bound to its slot. */\nexport interface TemplateExpectedField {\n name: string;\n role?: string;\n}\n\n/** A data-binding slot the template needs filled. */\nexport interface TemplateRequiredDataSink {\n slot: string;\n label: string;\n description?: string | null;\n expectedFields: TemplateExpectedField[];\n widgetTitles: string[];\n}\n\nexport interface TemplateRequiredWidgetType {\n widgetType: string;\n widgetTypeKey?: string | null;\n publishType: 'BUILTIN' | 'STORE' | 'CUSTOM';\n displayName?: string;\n requiresPurchase?: boolean;\n}\n\nexport interface TemplateRequiredInput {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'select' | 'secret';\n label: string;\n placeholder?: string;\n required: boolean;\n defaultValue?: string | number | boolean | null;\n options?: { label: string; value: string }[];\n helpText?: string;\n}\n\nexport interface DashboardTemplateContent {\n definition: unknown;\n requiredDataSinks: TemplateRequiredDataSink[];\n requiredWidgetTypes: TemplateRequiredWidgetType[];\n requiredInputs: TemplateRequiredInput[];\n defaultDashboardName?: string;\n defaultCategory?: string | null;\n defaultTags?: string[];\n difficultyLevel?: 'easy' | 'medium' | 'hard';\n}\n\n/** A store product presented as a template card. */\nexport interface StoreDashboardTemplate {\n id: string;\n name: string;\n slug: string;\n description?: string | null;\n icon?: string | null;\n category?: string | null;\n tags: string[];\n featured: boolean;\n downloads: number;\n rating?: number | null;\n pricingModel: string;\n price: number;\n /** True for anything the workspace has to buy before it can be installed. */\n requiresPurchase: boolean;\n}\n\ninterface RawStoreProduct {\n id: string;\n name: string;\n slug: string;\n description?: string | null;\n icon?: string | null;\n category?: string | null;\n tags?: string[] | null;\n featured?: boolean | null;\n downloads?: number | null;\n rating?: number | null;\n pricingModel?: string | null;\n price?: number | null;\n}\n\nexport interface InstallTemplateArgs {\n templateId: string;\n templateName: string;\n content: DashboardTemplateContent;\n name?: string;\n visibility?: string;\n dataSinkBindings?: { slot: string; dataSinkId: string }[];\n inputValues?: Record<string, unknown>;\n}\n\nexport interface InstallTemplateResult {\n success: boolean;\n dashboardId?: string;\n errorMessage?: string;\n unresolvedSlots: string[];\n resolvedSlots: string[];\n widgetsNeedingData: number;\n warnings: string[];\n}\n\nfunction normalizeProduct(raw: RawStoreProduct): StoreDashboardTemplate {\n const pricingModel = raw.pricingModel ?? 'FREE';\n return {\n id: raw.id,\n name: raw.name,\n slug: raw.slug,\n description: raw.description ?? null,\n icon: raw.icon ?? null,\n category: raw.category ?? null,\n tags: raw.tags ?? [],\n featured: raw.featured ?? false,\n downloads: raw.downloads ?? 0,\n rating: raw.rating ?? null,\n pricingModel,\n price: raw.price ?? 0,\n requiresPurchase: pricingModel !== 'FREE' && (raw.price ?? 0) > 0,\n };\n}\n\nexport interface PublishToStoreArgs {\n dashboardId: string;\n name: string;\n slug: string;\n description: string;\n category?: string;\n tags?: string[];\n}\n\nexport interface PublishToStoreResult {\n success: boolean;\n /** The DRAFT devportal application created for this template. */\n applicationId?: string;\n status?: string;\n errorMessage?: string;\n /** Distinct data sinks the dashboard referenced, all tokenised into slots. */\n sinkCount: number;\n /** Sinks that could not be resolved; their slots fall back to positional names. */\n unknownSinkIds: string[];\n}\n\nexport interface UseStoreDashboardTemplatesResult {\n templates: StoreDashboardTemplate[];\n loading: boolean;\n error: string | null;\n totalCount: number;\n refetch: (search?: string) => Promise<void>;\n /** Fetch the full template document. Kept lazy: content is large. */\n fetchTemplateContent: (templateId: string) => Promise<DashboardTemplateContent | null>;\n installTemplate: (args: InstallTemplateArgs) => Promise<InstallTemplateResult>;\n publishToStore: (args: PublishToStoreArgs) => Promise<PublishToStoreResult>;\n}\n\nexport function useStoreDashboardTemplates(\n options: { search?: string; category?: string; autoLoad?: boolean } = {}\n): UseStoreDashboardTemplatesResult {\n const { search, category, autoLoad = true } = options;\n\n const globalClient = useGatewayClient('global');\n const workspaceClient = useApolloClient();\n\n const [templates, setTemplates] = useState<StoreDashboardTemplate[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const refetch = useCallback(\n async (overrideSearch?: string): Promise<void> => {\n if (!globalClient) return;\n setLoading(true);\n setError(null);\n try {\n const result = await globalClient.query<{\n browseStore?: { products?: RawStoreProduct[]; totalCount?: number };\n }>({\n query: BROWSE_DASHBOARD_TEMPLATES,\n variables: {\n input: {\n filter: {\n type: 'DASHBOARD',\n status: 'PUBLISHED',\n // Without this a template published from another product's\n // devportal listing would show up in BigConsole's gallery.\n compatibleWith: 'bigconsole',\n ...((overrideSearch ?? search) ? { search: overrideSearch ?? search } : {}),\n ...(category && category !== 'ALL' ? { category } : {}),\n },\n sortBy: 'RELEVANCE',\n pagination: { limit: 50, offset: 0 },\n },\n },\n fetchPolicy: 'network-only',\n });\n\n setTemplates((result.data?.browseStore?.products ?? []).map(normalizeProduct));\n setTotalCount(result.data?.browseStore?.totalCount ?? 0);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load templates from the store');\n setTemplates([]);\n setTotalCount(0);\n } finally {\n setLoading(false);\n }\n },\n [globalClient, search, category]\n );\n\n useEffect(() => {\n if (autoLoad) void refetch();\n }, [autoLoad, refetch]);\n\n const fetchTemplateContent = useCallback(\n async (templateId: string): Promise<DashboardTemplateContent | null> => {\n if (!globalClient) return null;\n try {\n const result = await globalClient.query<{ product?: { content?: unknown } }>({\n query: GET_DASHBOARD_TEMPLATE_DETAIL,\n variables: { id: templateId },\n fetchPolicy: 'network-only',\n });\n const content = result.data?.product?.content;\n if (!content || typeof content !== 'object') return null;\n const doc = content as Partial<DashboardTemplateContent>;\n if (!doc.definition) return null;\n return {\n definition: doc.definition,\n requiredDataSinks: doc.requiredDataSinks ?? [],\n requiredWidgetTypes: doc.requiredWidgetTypes ?? [],\n requiredInputs: doc.requiredInputs ?? [],\n defaultDashboardName: doc.defaultDashboardName,\n defaultCategory: doc.defaultCategory ?? null,\n defaultTags: doc.defaultTags ?? [],\n difficultyLevel: doc.difficultyLevel ?? 'medium',\n };\n } catch {\n return null;\n }\n },\n [globalClient]\n );\n\n const installTemplate = useCallback(\n async (args: InstallTemplateArgs): Promise<InstallTemplateResult> => {\n const result = await workspaceClient.mutate<{\n importDashboardTemplate?: InstallTemplateResult & { dashboard?: { id: string } | null };\n }>({\n mutation: IMPORT_DASHBOARD_TEMPLATE,\n variables: {\n input: {\n content: args.content,\n sourceTemplateId: args.templateId,\n sourceTemplateName: args.templateName,\n ...(args.name ? { name: args.name } : {}),\n ...(args.visibility ? { visibility: args.visibility } : {}),\n ...(args.dataSinkBindings?.length ? { dataSinkBindings: args.dataSinkBindings } : {}),\n ...(args.inputValues && Object.keys(args.inputValues).length ? { inputValues: args.inputValues } : {}),\n },\n },\n });\n\n const payload = result.data?.importDashboardTemplate;\n if (!payload) {\n return {\n success: false,\n errorMessage: 'No response from the server',\n unresolvedSlots: [],\n resolvedSlots: [],\n widgetsNeedingData: 0,\n warnings: [],\n };\n }\n\n return {\n success: payload.success,\n dashboardId: payload.dashboard?.id,\n errorMessage: payload.errorMessage,\n unresolvedSlots: payload.unresolvedSlots ?? [],\n resolvedSlots: payload.resolvedSlots ?? [],\n widgetsNeedingData: payload.widgetsNeedingData ?? 0,\n warnings: payload.warnings ?? [],\n };\n },\n [workspaceClient]\n );\n\n /**\n * Turn a live dashboard into a Dashboard Template application in the\n * developer portal.\n *\n * It deliberately does NOT create a StoreProduct directly. devportal already\n * owns dashboard-template publishing end to end — versioning, submit/review,\n * publisher identity, and the `metadata.sourceApplicationId` the store uses\n * for ownership on later updates. Writing a product straight to the store\n * would fork that pipeline and orphan the listing: a later devportal publish\n * of the same slug would hit the store's slug-collision guard and refuse.\n *\n * So this fills in the one thing devportal cannot do — produce the template\n * document from a live dashboard — and hands off to the existing flow. The\n * app is left in DRAFT so the author reviews the listing and submits it\n * themselves.\n *\n * Two hops: the workspace gateway tokenises the dashboard (it owns the data\n * sinks), the global gateway owns devportal. Brokering through the client\n * keeps both directions free of cross-layer M2M, exactly like install.\n */\n const publishToStore = useCallback(\n async (args: PublishToStoreArgs): Promise<PublishToStoreResult> => {\n const failure = (errorMessage: string): PublishToStoreResult => ({\n success: false,\n errorMessage,\n sinkCount: 0,\n unknownSinkIds: [],\n });\n\n if (!globalClient) return failure('Store is unavailable right now.');\n\n let content: unknown;\n let sinkCount = 0;\n let unknownSinkIds: string[] = [];\n try {\n const built = await workspaceClient.query<{\n buildDashboardTemplateContent?: {\n content: unknown;\n sinkCount: number;\n unknownSinkIds: string[];\n };\n }>({\n query: BUILD_DASHBOARD_TEMPLATE_CONTENT,\n variables: {\n dashboardId: args.dashboardId,\n defaultDashboardName: args.name,\n defaultCategory: args.category ?? null,\n defaultTags: args.tags ?? [],\n },\n fetchPolicy: 'network-only',\n });\n const payload = built.data?.buildDashboardTemplateContent;\n if (!payload?.content) return failure('Could not build a template from this dashboard.');\n content = payload.content;\n sinkCount = payload.sinkCount;\n unknownSinkIds = payload.unknownSinkIds ?? [];\n } catch (err) {\n return failure(err instanceof Error ? err.message : 'Failed to build the template.');\n }\n\n try {\n const app = await globalClient.mutate<{\n createApplication?: { id: string; status: string };\n }>({\n mutation: CREATE_TEMPLATE_APP,\n variables: {\n input: {\n appType: 'DASHBOARD',\n name: args.name,\n slug: args.slug,\n shortDescription: args.description.slice(0, 200),\n description: args.description,\n ...(args.category ? { category: args.category } : {}),\n tags: args.tags ?? [],\n compatibleProducts: ['bigconsole'],\n },\n },\n });\n\n const application = app.data?.createApplication;\n if (!application) {\n return { ...failure('The developer portal did not create the app.'), sinkCount, unknownSinkIds };\n }\n\n // The version manifest is what devportal maps onto StoreProduct.content\n // when the app is submitted and approved.\n await globalClient.mutate({\n mutation: CREATE_TEMPLATE_VERSION,\n variables: {\n applicationId: application.id,\n input: { version: '1.0.0', manifest: content },\n },\n });\n\n return {\n success: true,\n applicationId: application.id,\n status: application.status,\n sinkCount,\n unknownSinkIds,\n };\n } catch (err) {\n return {\n ...failure(err instanceof Error ? err.message : 'Failed to publish to the developer portal.'),\n sinkCount,\n unknownSinkIds,\n };\n }\n },\n [globalClient, workspaceClient]\n );\n\n return useMemo(\n () => ({\n templates,\n loading,\n error,\n totalCount,\n refetch,\n fetchTemplateContent,\n installTemplate,\n publishToStore,\n }),\n [templates, loading, error, totalCount, refetch, fetchTemplateContent, installTemplate, publishToStore]\n );\n}\n\nexport default useStoreDashboardTemplates;\n"],"mappings":"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"}