@burdenoff/microfe-workspaces 2026.802.1 → 2026.825.1

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 (49) hide show
  1. package/dist/generated/global-operations.js +44 -1
  2. package/dist/generated/global-operations.js.map +1 -1
  3. package/dist/generated/wspace-operations.js +18 -18
  4. package/dist/generated/wspace-operations.js.map +1 -1
  5. package/dist/hooks/useWorkspaceMutations.js +84 -61
  6. package/dist/hooks/useWorkspaceMutations.js.map +1 -1
  7. package/dist/pages/WorkspaceDetailPage.js +152 -114
  8. package/dist/pages/WorkspaceDetailPage.js.map +1 -1
  9. package/dist/pages/dashboard/DashboardCustomizer.js +62 -62
  10. package/dist/pages/dashboard/DashboardCustomizer.js.map +1 -1
  11. package/dist/pages/dashboard/DashboardPage.js +88 -69
  12. package/dist/pages/dashboard/DashboardPage.js.map +1 -1
  13. package/dist/pages/dashboard/config.js +77 -1
  14. package/dist/pages/dashboard/config.js.map +1 -1
  15. package/dist/pages/dashboard/widgets/AgentsWidget.js +56 -54
  16. package/dist/pages/dashboard/widgets/AgentsWidget.js.map +1 -1
  17. package/dist/pages/dashboard/widgets/GroupsWidget.js +29 -31
  18. package/dist/pages/dashboard/widgets/GroupsWidget.js.map +1 -1
  19. package/dist/pages/dashboard/widgets/MembersWidget.js +33 -33
  20. package/dist/pages/dashboard/widgets/MembersWidget.js.map +1 -1
  21. package/dist/pages/dashboard/widgets/ProjectsWidget.js +42 -44
  22. package/dist/pages/dashboard/widgets/ProjectsWidget.js.map +1 -1
  23. package/dist/pages/dashboard/widgets/QuickActionsWidget.js +171 -161
  24. package/dist/pages/dashboard/widgets/QuickActionsWidget.js.map +1 -1
  25. package/dist/pages/dashboard/widgets/RecentActivityWidget.js +54 -44
  26. package/dist/pages/dashboard/widgets/RecentActivityWidget.js.map +1 -1
  27. package/dist/pages/dashboard/widgets/SessionsWidget.js +57 -41
  28. package/dist/pages/dashboard/widgets/SessionsWidget.js.map +1 -1
  29. package/dist/pages/dashboard/widgets/StorageWidget.js +52 -54
  30. package/dist/pages/dashboard/widgets/StorageWidget.js.map +1 -1
  31. package/dist/pages/dashboard/widgets/TagCloudWidget.js +28 -30
  32. package/dist/pages/dashboard/widgets/TagCloudWidget.js.map +1 -1
  33. package/dist/pages/dashboard/widgets/TipsWidget.js +141 -131
  34. package/dist/pages/dashboard/widgets/TipsWidget.js.map +1 -1
  35. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js +46 -40
  36. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js.map +1 -1
  37. package/dist/pages/dashboard/widgets/VibesWidget.js +44 -34
  38. package/dist/pages/dashboard/widgets/VibesWidget.js.map +1 -1
  39. package/dist/pages/dashboard/widgets/WelcomeWidget.js +43 -41
  40. package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
  41. package/dist/pages/dashboard/widgets/WorkflowRunsWidget.js +68 -62
  42. package/dist/pages/dashboard/widgets/WorkflowRunsWidget.js.map +1 -1
  43. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js +50 -44
  44. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js.map +1 -1
  45. package/dist/pages/dashboard/widgets/WorkflowsOverviewWidget.js +59 -49
  46. package/dist/pages/dashboard/widgets/WorkflowsOverviewWidget.js.map +1 -1
  47. package/dist/pages/dashboard/widgets/WorkspacesWidget.js +35 -33
  48. package/dist/pages/dashboard/widgets/WorkspacesWidget.js.map +1 -1
  49. package/package.json +2 -2
@@ -1,24 +1,24 @@
1
1
  import { tWithFallback as e } from "../../utils/i18n.js";
2
- import { DEFAULT_DASHBOARD_WIDGETS as t, DEFAULT_DASHBOARD_WIDGETS_BY_PRODUCT as n, WIDGET_DEFINITIONS as r, isWidgetAvailable as i } from "./config.js";
3
- import { nativeImpact as a } from "../../utils/nativeBridge.js";
4
- import { useCallback as o, useEffect as s, useState as c } from "react";
5
- import { ArrowDown as l, ArrowUp as u, GripVertical as d, Loader2 as f, RotateCcw as p, Save as m } from "lucide-react";
6
- import { jsx as h, jsxs as g } from "react/jsx-runtime";
7
- import { Badge as _, Button as v, Separator as y, Sheet as b, SheetContent as x, SheetDescription as S, SheetFooter as C, SheetHeader as w, SheetTitle as T, Switch as E } from "@burdenoff/fe-libs/ui";
8
- import { useI18n as D } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
2
+ import { DEFAULT_DASHBOARD_WIDGETS as t, DEFAULT_DASHBOARD_WIDGETS_BY_PRODUCT as n, WIDGET_DEFINITIONS as r, isWidgetAvailable as i, resolveWidgetCopy as a } from "./config.js";
3
+ import { nativeImpact as o } from "../../utils/nativeBridge.js";
4
+ import { useCallback as s, useEffect as c, useState as l } from "react";
5
+ import { ArrowDown as u, ArrowUp as d, GripVertical as f, Loader2 as p, RotateCcw as m, Save as h } from "lucide-react";
6
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
7
+ import { Badge as v, Button as y, Separator as b, Sheet as x, SheetContent as S, SheetDescription as C, SheetFooter as w, SheetHeader as T, SheetTitle as E, Switch as D } from "@burdenoff/fe-libs/ui";
8
+ import { useI18n as O } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
9
9
  //#region src/pages/dashboard/DashboardCustomizer.tsx
10
- function O({ open: O, onOpenChange: k, widgets: A, onSave: j, isSaving: M, productId: N = "workspaces" }) {
11
- let { t: P } = D(), [F, I] = c(A), [L, R] = c(!1);
12
- s(() => {
13
- O && (I([...A]), R(!1));
14
- }, [O, A]);
15
- let z = o((e) => {
16
- I((t) => t.map((t) => t.widgetId === e ? {
10
+ function k({ open: k, onOpenChange: A, widgets: j, onSave: M, isSaving: N, productId: P = "workspaces" }) {
11
+ let { t: F } = O(), I = (t, n) => e(F, t, n), [L, R] = l(j), [z, B] = l(!1);
12
+ c(() => {
13
+ k && (R([...j]), B(!1));
14
+ }, [k, j]);
15
+ let V = s((e) => {
16
+ R((t) => t.map((t) => t.widgetId === e ? {
17
17
  ...t,
18
18
  enabled: !t.enabled
19
- } : t)), R(!0);
20
- }, []), B = o((e, t) => {
21
- I((n) => {
19
+ } : t)), B(!0);
20
+ }, []), H = s((e, t) => {
21
+ R((n) => {
22
22
  let r = [...n].sort((e, t) => e.order - t.order), i = r.findIndex((t) => t.widgetId === e);
23
23
  if (i < 0) return n;
24
24
  let a = t === "up" ? i - 1 : i + 1;
@@ -31,72 +31,72 @@ function O({ open: O, onOpenChange: k, widgets: A, onSave: j, isSaving: M, produ
31
31
  ...r[a],
32
32
  order: o
33
33
  }, r;
34
- }), R(!0);
35
- }, []), V = o(() => {
36
- I([...n[N] ?? t]), R(!0);
37
- }, [N]), H = o(async () => {
38
- await j(F), R(!1), k(!1);
34
+ }), B(!0);
35
+ }, []), U = s(() => {
36
+ R([...n[P] ?? t]), B(!0);
37
+ }, [P]), W = s(async () => {
38
+ await M(L), B(!1), A(!1);
39
39
  }, [
40
- F,
41
- j,
42
- k
43
- ]), U = [...F].sort((e, t) => e.order - t.order);
44
- return /* @__PURE__ */ h(b, {
45
- open: O,
46
- onOpenChange: k,
47
- children: /* @__PURE__ */ g(x, {
40
+ L,
41
+ M,
42
+ A
43
+ ]), G = [...L].sort((e, t) => e.order - t.order);
44
+ return /* @__PURE__ */ g(x, {
45
+ open: k,
46
+ onOpenChange: A,
47
+ children: /* @__PURE__ */ _(S, {
48
48
  className: "w-full sm:max-w-md overflow-y-auto",
49
49
  side: "right",
50
50
  children: [
51
- /* @__PURE__ */ g(w, { children: [/* @__PURE__ */ h(T, { children: e(P, "dashboardCustomizer.title", "Customize Dashboard") }), /* @__PURE__ */ h(S, { children: e(P, "dashboardCustomizer.description", "Toggle widgets on or off and reorder them to personalize your dashboard.") })] }),
52
- /* @__PURE__ */ h("div", {
51
+ /* @__PURE__ */ _(T, { children: [/* @__PURE__ */ g(E, { children: e(F, "dashboardCustomizer.title", "Customize Dashboard") }), /* @__PURE__ */ g(C, { children: e(F, "dashboardCustomizer.description", "Toggle widgets on or off and reorder them to personalize your dashboard.") })] }),
52
+ /* @__PURE__ */ g("div", {
53
53
  className: "py-4 space-y-1",
54
- children: U.map((e, t) => {
54
+ children: G.map((e, t) => {
55
55
  let n = r[e.widgetId];
56
- return !n || !i(n, N) ? null : /* @__PURE__ */ g("div", {
56
+ return !n || !i(n, P) ? null : /* @__PURE__ */ _("div", {
57
57
  className: `flex items-center gap-3 p-3 rounded-lg border transition-colors ${e.enabled ? "border-border-default bg-bg-surface" : "border-transparent bg-bg-sunken/30 opacity-60"}`,
58
58
  children: [
59
- /* @__PURE__ */ h(d, { className: "size-4 text-text-secondary shrink-0" }),
60
- /* @__PURE__ */ g("div", {
59
+ /* @__PURE__ */ g(f, { className: "size-4 text-text-secondary shrink-0" }),
60
+ /* @__PURE__ */ _("div", {
61
61
  className: "flex-1 min-w-0",
62
- children: [/* @__PURE__ */ g("div", {
62
+ children: [/* @__PURE__ */ _("div", {
63
63
  className: "flex items-center gap-2",
64
- children: [/* @__PURE__ */ h("span", {
64
+ children: [/* @__PURE__ */ g("span", {
65
65
  className: "text-sm font-medium text-text-primary",
66
- children: n.title
67
- }), /* @__PURE__ */ h(_, {
66
+ children: a(I, n).title
67
+ }), /* @__PURE__ */ g(v, {
68
68
  variant: "outline",
69
69
  className: "text-[10px]",
70
70
  children: n.defaultSize
71
71
  })]
72
- }), /* @__PURE__ */ h("p", {
72
+ }), /* @__PURE__ */ g("p", {
73
73
  className: "text-xs text-text-secondary truncate",
74
- children: n.description
74
+ children: a(I, n).description
75
75
  })]
76
76
  }),
77
- /* @__PURE__ */ g("div", {
77
+ /* @__PURE__ */ _("div", {
78
78
  className: "flex items-center gap-1 shrink-0",
79
79
  children: [
80
- /* @__PURE__ */ h(v, {
80
+ /* @__PURE__ */ g(y, {
81
81
  variant: "ghost",
82
82
  size: "sm",
83
83
  className: "size-7 p-0",
84
84
  disabled: t === 0,
85
- onClick: () => B(e.widgetId, "up"),
86
- children: /* @__PURE__ */ h(u, { className: "size-3" })
85
+ onClick: () => H(e.widgetId, "up"),
86
+ children: /* @__PURE__ */ g(d, { className: "size-3" })
87
87
  }),
88
- /* @__PURE__ */ h(v, {
88
+ /* @__PURE__ */ g(y, {
89
89
  variant: "ghost",
90
90
  size: "sm",
91
91
  className: "size-7 p-0",
92
- disabled: t === U.length - 1,
93
- onClick: () => B(e.widgetId, "down"),
94
- children: /* @__PURE__ */ h(l, { className: "size-3" })
92
+ disabled: t === G.length - 1,
93
+ onClick: () => H(e.widgetId, "down"),
94
+ children: /* @__PURE__ */ g(u, { className: "size-3" })
95
95
  }),
96
- /* @__PURE__ */ h(E, {
96
+ /* @__PURE__ */ g(D, {
97
97
  checked: e.enabled,
98
98
  onCheckedChange: () => {
99
- a("medium"), z(e.widgetId);
99
+ o("medium"), V(e.widgetId);
100
100
  },
101
101
  disabled: !n.removable,
102
102
  className: "ml-1"
@@ -107,21 +107,21 @@ function O({ open: O, onOpenChange: k, widgets: A, onSave: j, isSaving: M, produ
107
107
  }, e.widgetId);
108
108
  })
109
109
  }),
110
- /* @__PURE__ */ h(y, {}),
111
- /* @__PURE__ */ g(C, {
110
+ /* @__PURE__ */ g(b, {}),
111
+ /* @__PURE__ */ _(w, {
112
112
  className: "flex-row gap-2 pt-4",
113
- children: [/* @__PURE__ */ g(v, {
113
+ children: [/* @__PURE__ */ _(y, {
114
114
  variant: "outline",
115
115
  size: "sm",
116
- onClick: V,
116
+ onClick: U,
117
117
  className: "flex-1",
118
- children: [/* @__PURE__ */ h(p, { className: "size-3 mr-1.5" }), e(P, "dashboardCustomizer.resetToDefaults", "Reset to defaults")]
119
- }), /* @__PURE__ */ g(v, {
118
+ children: [/* @__PURE__ */ g(m, { className: "size-3 mr-1.5" }), e(F, "dashboardCustomizer.resetToDefaults", "Reset to defaults")]
119
+ }), /* @__PURE__ */ _(y, {
120
120
  size: "sm",
121
- onClick: H,
122
- disabled: !L || M,
121
+ onClick: W,
122
+ disabled: !z || N,
123
123
  className: "flex-1",
124
- children: [M ? /* @__PURE__ */ h(f, { className: "size-3 mr-1.5 animate-spin" }) : /* @__PURE__ */ h(m, { className: "size-3 mr-1.5" }), e(P, "dashboardCustomizer.saveLayout", "Save layout")]
124
+ children: [N ? /* @__PURE__ */ g(p, { className: "size-3 mr-1.5 animate-spin" }) : /* @__PURE__ */ g(h, { className: "size-3 mr-1.5" }), e(F, "dashboardCustomizer.saveLayout", "Save layout")]
125
125
  })]
126
126
  })
127
127
  ]
@@ -129,6 +129,6 @@ function O({ open: O, onOpenChange: k, widgets: A, onSave: j, isSaving: M, produ
129
129
  });
130
130
  }
131
131
  //#endregion
132
- export { O as DashboardCustomizer };
132
+ export { k as DashboardCustomizer };
133
133
 
134
134
  //# sourceMappingURL=DashboardCustomizer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardCustomizer.js","names":[],"sources":["../../../src/pages/dashboard/DashboardCustomizer.tsx"],"sourcesContent":["/**\n * DashboardCustomizer — Sheet overlay for customizing dashboard widgets.\n *\n * Allows users to toggle widgets on/off, reorder them, and save their layout.\n */\nimport { useState, useCallback, useEffect } from 'react';\nimport {\n Sheet,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetDescription,\n SheetFooter,\n Button,\n Switch,\n Badge,\n Separator,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowUp, ArrowDown, RotateCcw, Save, Loader2, GripVertical } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../../utils/i18n';\nimport type { WidgetConfig } from './types';\nimport {\n WIDGET_DEFINITIONS,\n DEFAULT_DASHBOARD_WIDGETS_BY_PRODUCT,\n DEFAULT_DASHBOARD_WIDGETS,\n isWidgetAvailable,\n} from './config';\nimport { nativeImpact } from '../../utils/nativeBridge';\n\ninterface DashboardCustomizerProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n widgets: WidgetConfig[];\n onSave: (widgets: WidgetConfig[]) => Promise<void>;\n isSaving: boolean;\n productId?: string;\n}\n\nexport function DashboardCustomizer({\n open,\n onOpenChange,\n widgets,\n onSave,\n isSaving,\n productId = 'workspaces',\n}: DashboardCustomizerProps) {\n const { t } = useI18n();\n\n const [localWidgets, setLocalWidgets] = useState<WidgetConfig[]>(widgets);\n const [hasChanges, setHasChanges] = useState(false);\n\n // Sync local state when sheet opens or widgets prop changes while open\n useEffect(() => {\n if (open) {\n setLocalWidgets([...widgets]);\n setHasChanges(false);\n }\n }, [open, widgets]);\n\n const toggleWidget = useCallback((widgetId: string) => {\n setLocalWidgets((prev) =>\n prev.map((w) => (w.widgetId === widgetId ? { ...w, enabled: !w.enabled } : w))\n );\n setHasChanges(true);\n }, []);\n\n const moveWidget = useCallback((widgetId: string, direction: 'up' | 'down') => {\n setLocalWidgets((prev) => {\n const sorted = [...prev].sort((a, b) => a.order - b.order);\n const idx = sorted.findIndex((w) => w.widgetId === widgetId);\n if (idx < 0) return prev;\n\n const swapIdx = direction === 'up' ? idx - 1 : idx + 1;\n if (swapIdx < 0 || swapIdx >= sorted.length) return prev;\n\n // Swap orders\n const tempOrder = sorted[idx].order;\n sorted[idx] = { ...sorted[idx], order: sorted[swapIdx].order };\n sorted[swapIdx] = { ...sorted[swapIdx], order: tempOrder };\n\n return sorted;\n });\n setHasChanges(true);\n }, []);\n\n const resetToDefaults = useCallback(() => {\n const defaults = DEFAULT_DASHBOARD_WIDGETS_BY_PRODUCT[productId] ?? DEFAULT_DASHBOARD_WIDGETS;\n setLocalWidgets([...defaults]);\n setHasChanges(true);\n }, [productId]);\n\n const handleSave = useCallback(async () => {\n await onSave(localWidgets);\n setHasChanges(false);\n onOpenChange(false);\n }, [localWidgets, onSave, onOpenChange]);\n\n const sorted = [...localWidgets].sort((a, b) => a.order - b.order);\n\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent className=\"w-full sm:max-w-md overflow-y-auto\" side=\"right\">\n <SheetHeader>\n <SheetTitle>\n {tWithFallback(t, 'dashboardCustomizer.title', 'Customize Dashboard')}\n </SheetTitle>\n <SheetDescription>\n {tWithFallback(\n t,\n 'dashboardCustomizer.description',\n 'Toggle widgets on or off and reorder them to personalize your dashboard.'\n )}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"py-4 space-y-1\">\n {sorted.map((widget, idx) => {\n const definition = WIDGET_DEFINITIONS[widget.widgetId];\n if (!definition) return null;\n if (!isWidgetAvailable(definition, productId)) return null;\n\n return (\n <div\n key={widget.widgetId}\n className={`flex items-center gap-3 p-3 rounded-lg border transition-colors ${\n widget.enabled\n ? 'border-border-default bg-bg-surface'\n : 'border-transparent bg-bg-sunken/30 opacity-60'\n }`}\n >\n <GripVertical className=\"size-4 text-text-secondary shrink-0\" />\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">\n {definition.title}\n </span>\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {definition.defaultSize}\n </Badge>\n </div>\n <p className=\"text-xs text-text-secondary truncate\">{definition.description}</p>\n </div>\n\n <div className=\"flex items-center gap-1 shrink-0\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"size-7 p-0\"\n disabled={idx === 0}\n onClick={() => moveWidget(widget.widgetId, 'up')}\n >\n <ArrowUp className=\"size-3\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"size-7 p-0\"\n disabled={idx === sorted.length - 1}\n onClick={() => moveWidget(widget.widgetId, 'down')}\n >\n <ArrowDown className=\"size-3\" />\n </Button>\n <Switch\n checked={widget.enabled}\n onCheckedChange={() => {\n void nativeImpact('medium');\n toggleWidget(widget.widgetId);\n }}\n disabled={!definition.removable}\n className=\"ml-1\"\n />\n </div>\n </div>\n );\n })}\n </div>\n\n <Separator />\n\n <SheetFooter className=\"flex-row gap-2 pt-4\">\n <Button variant=\"outline\" size=\"sm\" onClick={resetToDefaults} className=\"flex-1\">\n <RotateCcw className=\"size-3 mr-1.5\" />\n {tWithFallback(t, 'dashboardCustomizer.resetToDefaults', 'Reset to defaults')}\n </Button>\n <Button\n size=\"sm\"\n onClick={handleSave}\n disabled={!hasChanges || isSaving}\n className=\"flex-1\"\n >\n {isSaving ? (\n <Loader2 className=\"size-3 mr-1.5 animate-spin\" />\n ) : (\n <Save className=\"size-3 mr-1.5\" />\n )}\n {tWithFallback(t, 'dashboardCustomizer.saveLayout', 'Save layout')}\n </Button>\n </SheetFooter>\n </SheetContent>\n </Sheet>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardCustomizer.js","names":[],"sources":["../../../src/pages/dashboard/DashboardCustomizer.tsx"],"sourcesContent":["/**\n * DashboardCustomizer — Sheet overlay for customizing dashboard widgets.\n *\n * Allows users to toggle widgets on/off, reorder them, and save their layout.\n */\nimport { useState, useCallback, useEffect } from 'react';\nimport {\n Sheet,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetDescription,\n SheetFooter,\n Button,\n Switch,\n Badge,\n Separator,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowUp, ArrowDown, RotateCcw, Save, Loader2, GripVertical } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../../utils/i18n';\nimport type { WidgetConfig } from './types';\nimport {\n WIDGET_DEFINITIONS,\n DEFAULT_DASHBOARD_WIDGETS_BY_PRODUCT,\n DEFAULT_DASHBOARD_WIDGETS,\n isWidgetAvailable,\n resolveWidgetCopy,\n} from './config';\nimport { nativeImpact } from '../../utils/nativeBridge';\n\ninterface DashboardCustomizerProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n widgets: WidgetConfig[];\n onSave: (widgets: WidgetConfig[]) => Promise<void>;\n isSaving: boolean;\n productId?: string;\n}\n\nexport function DashboardCustomizer({\n open,\n onOpenChange,\n widgets,\n onSave,\n isSaving,\n productId = 'workspaces',\n}: DashboardCustomizerProps) {\n const { t } = useI18n();\n // Widget titles/descriptions live in module-scope WIDGET_DEFINITIONS, so they\n // are translated here at render time with literal keys. [BOFF-5997]\n const trLiteral = (key: string, fallback: string): string => tWithFallback(t, key, fallback);\n\n const [localWidgets, setLocalWidgets] = useState<WidgetConfig[]>(widgets);\n const [hasChanges, setHasChanges] = useState(false);\n\n // Sync local state when sheet opens or widgets prop changes while open\n useEffect(() => {\n if (open) {\n setLocalWidgets([...widgets]);\n setHasChanges(false);\n }\n }, [open, widgets]);\n\n const toggleWidget = useCallback((widgetId: string) => {\n setLocalWidgets((prev) =>\n prev.map((w) => (w.widgetId === widgetId ? { ...w, enabled: !w.enabled } : w))\n );\n setHasChanges(true);\n }, []);\n\n const moveWidget = useCallback((widgetId: string, direction: 'up' | 'down') => {\n setLocalWidgets((prev) => {\n const sorted = [...prev].sort((a, b) => a.order - b.order);\n const idx = sorted.findIndex((w) => w.widgetId === widgetId);\n if (idx < 0) return prev;\n\n const swapIdx = direction === 'up' ? idx - 1 : idx + 1;\n if (swapIdx < 0 || swapIdx >= sorted.length) return prev;\n\n // Swap orders\n const tempOrder = sorted[idx].order;\n sorted[idx] = { ...sorted[idx], order: sorted[swapIdx].order };\n sorted[swapIdx] = { ...sorted[swapIdx], order: tempOrder };\n\n return sorted;\n });\n setHasChanges(true);\n }, []);\n\n const resetToDefaults = useCallback(() => {\n const defaults = DEFAULT_DASHBOARD_WIDGETS_BY_PRODUCT[productId] ?? DEFAULT_DASHBOARD_WIDGETS;\n setLocalWidgets([...defaults]);\n setHasChanges(true);\n }, [productId]);\n\n const handleSave = useCallback(async () => {\n await onSave(localWidgets);\n setHasChanges(false);\n onOpenChange(false);\n }, [localWidgets, onSave, onOpenChange]);\n\n const sorted = [...localWidgets].sort((a, b) => a.order - b.order);\n\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent className=\"w-full sm:max-w-md overflow-y-auto\" side=\"right\">\n <SheetHeader>\n <SheetTitle>\n {tWithFallback(t, 'dashboardCustomizer.title', 'Customize Dashboard')}\n </SheetTitle>\n <SheetDescription>\n {tWithFallback(\n t,\n 'dashboardCustomizer.description',\n 'Toggle widgets on or off and reorder them to personalize your dashboard.'\n )}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"py-4 space-y-1\">\n {sorted.map((widget, idx) => {\n const definition = WIDGET_DEFINITIONS[widget.widgetId];\n if (!definition) return null;\n if (!isWidgetAvailable(definition, productId)) return null;\n\n return (\n <div\n key={widget.widgetId}\n className={`flex items-center gap-3 p-3 rounded-lg border transition-colors ${\n widget.enabled\n ? 'border-border-default bg-bg-surface'\n : 'border-transparent bg-bg-sunken/30 opacity-60'\n }`}\n >\n <GripVertical className=\"size-4 text-text-secondary shrink-0\" />\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">\n {resolveWidgetCopy(trLiteral, definition).title}\n </span>\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {definition.defaultSize}\n </Badge>\n </div>\n <p className=\"text-xs text-text-secondary truncate\">\n {resolveWidgetCopy(trLiteral, definition).description}\n </p>\n </div>\n\n <div className=\"flex items-center gap-1 shrink-0\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"size-7 p-0\"\n disabled={idx === 0}\n onClick={() => moveWidget(widget.widgetId, 'up')}\n >\n <ArrowUp className=\"size-3\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"size-7 p-0\"\n disabled={idx === sorted.length - 1}\n onClick={() => moveWidget(widget.widgetId, 'down')}\n >\n <ArrowDown className=\"size-3\" />\n </Button>\n <Switch\n checked={widget.enabled}\n onCheckedChange={() => {\n void nativeImpact('medium');\n toggleWidget(widget.widgetId);\n }}\n disabled={!definition.removable}\n className=\"ml-1\"\n />\n </div>\n </div>\n );\n })}\n </div>\n\n <Separator />\n\n <SheetFooter className=\"flex-row gap-2 pt-4\">\n <Button variant=\"outline\" size=\"sm\" onClick={resetToDefaults} className=\"flex-1\">\n <RotateCcw className=\"size-3 mr-1.5\" />\n {tWithFallback(t, 'dashboardCustomizer.resetToDefaults', 'Reset to defaults')}\n </Button>\n <Button\n size=\"sm\"\n onClick={handleSave}\n disabled={!hasChanges || isSaving}\n className=\"flex-1\"\n >\n {isSaving ? (\n <Loader2 className=\"size-3 mr-1.5 animate-spin\" />\n ) : (\n <Save className=\"size-3 mr-1.5\" />\n )}\n {tWithFallback(t, 'dashboardCustomizer.saveLayout', 'Save layout')}\n </Button>\n </SheetFooter>\n </SheetContent>\n </Sheet>\n );\n}\n"],"mappings":";;;;;;;;;AAwCA,SAAgB,EAAoB,EAClC,SACA,iBACA,YACA,WACA,aACA,eAAY,gBACe;CAC3B,IAAM,EAAE,SAAM,GAAS,EAGjB,KAAa,GAAa,MAA6B,EAAc,GAAG,GAAK,EAAS,EAEtF,CAAC,GAAc,KAAmB,EAAyB,EAAQ,EACnE,CAAC,GAAY,KAAiB,EAAS,GAAM;AAGnD,SAAgB;AACd,EAAI,MACF,EAAgB,CAAC,GAAG,EAAQ,CAAC,EAC7B,EAAc,GAAM;IAErB,CAAC,GAAM,EAAQ,CAAC;CAEnB,IAAM,IAAe,GAAa,MAAqB;AAIrD,EAHA,GAAiB,MACf,EAAK,KAAK,MAAO,EAAE,aAAa,IAAW;GAAE,GAAG;GAAG,SAAS,CAAC,EAAE;GAAS,GAAG,EAAG,CAC/E,EACD,EAAc,GAAK;IAClB,EAAE,CAAC,EAEA,IAAa,GAAa,GAAkB,MAA6B;AAgB7E,EAfA,GAAiB,MAAS;GACxB,IAAM,IAAS,CAAC,GAAG,EAAK,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,EACpD,IAAM,EAAO,WAAW,MAAM,EAAE,aAAa,EAAS;AAC5D,OAAI,IAAM,EAAG,QAAO;GAEpB,IAAM,IAAU,MAAc,OAAO,IAAM,IAAI,IAAM;AACrD,OAAI,IAAU,KAAK,KAAW,EAAO,OAAQ,QAAO;GAGpD,IAAM,IAAY,EAAO,GAAK;AAI9B,UAHA,EAAO,KAAO;IAAE,GAAG,EAAO;IAAM,OAAO,EAAO,GAAS;IAAO,EAC9D,EAAO,KAAW;IAAE,GAAG,EAAO;IAAU,OAAO;IAAW,EAEnD;IACP,EACF,EAAc,GAAK;IAClB,EAAE,CAAC,EAEA,IAAkB,QAAkB;AAGxC,EADA,EAAgB,CAAC,GADA,EAAqC,MAAc,EACvC,CAAC,EAC9B,EAAc,GAAK;IAClB,CAAC,EAAU,CAAC,EAET,IAAa,EAAY,YAAY;AAGzC,EAFA,MAAM,EAAO,EAAa,EAC1B,EAAc,GAAM,EACpB,EAAa,GAAM;IAClB;EAAC;EAAc;EAAQ;EAAa,CAAC,EAElC,IAAS,CAAC,GAAG,EAAa,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;AAElE,QACE,kBAAC,GAAD;EAAa;EAAoB;YAC/B,kBAAC,GAAD;GAAc,WAAU;GAAqC,MAAK;aAAlE;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACG,EAAc,GAAG,6BAA6B,sBAAsB,EAC1D,CAAA,EACb,kBAAC,GAAD,EAAA,UACG,EACC,GACA,mCACA,2EACD,EACgB,CAAA,CACP,EAAA,CAAA;IAEd,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAO,KAAK,GAAQ,MAAQ;MAC3B,IAAM,IAAa,EAAmB,EAAO;AAI7C,aAHI,CAAC,KACD,CAAC,EAAkB,GAAY,EAAU,GAAS,OAGpD,kBAAC,OAAD;OAEE,WAAW,mEACT,EAAO,UACH,wCACA;iBALR;QAQE,kBAAC,GAAD,EAAc,WAAU,uCAAwC,CAAA;QAEhE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAkB,GAAW,EAAW,CAAC;WACrC,CAAA,EACP,kBAAC,GAAD;WAAO,SAAQ;WAAU,WAAU;qBAChC,EAAW;WACN,CAAA,CACJ;aACN,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAkB,GAAW,EAAW,CAAC;UACxC,CAAA,CACA;;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,GAAD;WACE,SAAQ;WACR,MAAK;WACL,WAAU;WACV,UAAU,MAAQ;WAClB,eAAe,EAAW,EAAO,UAAU,KAAK;qBAEhD,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;WACvB,CAAA;UACT,kBAAC,GAAD;WACE,SAAQ;WACR,MAAK;WACL,WAAU;WACV,UAAU,MAAQ,EAAO,SAAS;WAClC,eAAe,EAAW,EAAO,UAAU,OAAO;qBAElD,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;WACzB,CAAA;UACT,kBAAC,GAAD;WACE,SAAS,EAAO;WAChB,uBAAuB;AAErB,YADK,EAAa,SAAS,EAC3B,EAAa,EAAO,SAAS;;WAE/B,UAAU,CAAC,EAAW;WACtB,WAAU;WACV,CAAA;UACE;;QACF;SApDC,EAAO,SAoDR;OAER;KACE,CAAA;IAEN,kBAAC,GAAD,EAAa,CAAA;IAEb,kBAAC,GAAD;KAAa,WAAU;eAAvB,CACE,kBAAC,GAAD;MAAQ,SAAQ;MAAU,MAAK;MAAK,SAAS;MAAiB,WAAU;gBAAxE,CACE,kBAAC,GAAD,EAAW,WAAU,iBAAkB,CAAA,EACtC,EAAc,GAAG,uCAAuC,oBAAoB,CACtE;SACT,kBAAC,GAAD;MACE,MAAK;MACL,SAAS;MACT,UAAU,CAAC,KAAc;MACzB,WAAU;gBAJZ,CAMG,IACC,kBAAC,GAAD,EAAS,WAAU,8BAA+B,CAAA,GAElD,kBAAC,GAAD,EAAM,WAAU,iBAAkB,CAAA,EAEnC,EAAc,GAAG,kCAAkC,cAAc,CAC3D;QACG;;IACD;;EACT,CAAA"}
@@ -1,43 +1,62 @@
1
1
  import { useWorkspacesPageView as e } from "../../hooks/useWorkspacesPageView.js";
2
2
  import { useGlobalDashboardData as t, useProjectsDashboardData as n, useVibecontrolsDashboardData as r, useWorkspaceDashboardData as i } from "./hooks/useDashboardData.js";
3
- import { useUpdateDashboardPreferences as a } from "./hooks/useDashboardPreferences.js";
4
- import { getEffectiveWidgets as o } from "./config.js";
5
- import { DashboardGrid as s } from "./DashboardGrid.js";
6
- import { DashboardCustomizer as ee } from "./DashboardCustomizer.js";
7
- import { useCallback as c, useMemo as l, useState as u } from "react";
8
- import { AlertCircle as d, Building2 as f, GitBranch as p, Loader2 as m, RefreshCw as h, Settings2 as g, Sparkles as _ } from "lucide-react";
9
- import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
10
- import { AuroraBackground as x, Button as S, GradientText as C, PagePurpose as w } from "@burdenoff/fe-libs/ui";
11
- import { useI18n as T } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
- import { PageHeader as E } from "@burdenoff/fe-libs/chrome";
3
+ import { useUpdateDashboardPreferences as ee } from "./hooks/useDashboardPreferences.js";
4
+ import { getEffectiveWidgets as a } from "./config.js";
5
+ import { DashboardGrid as te } from "./DashboardGrid.js";
6
+ import { DashboardCustomizer as o } from "./DashboardCustomizer.js";
7
+ import { useCallback as s, useMemo as c, useState as l } from "react";
8
+ import { AlertCircle as u, Building2 as d, GitBranch as f, Loader2 as p, RefreshCw as m, Settings2 as ne, Sparkles as h } from "lucide-react";
9
+ import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
10
+ import { AuroraBackground as y, Button as b, GradientText as x, PagePurpose as S } from "@burdenoff/fe-libs/ui";
11
+ import { useI18n as C } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
+ import { PageHeader as w } from "@burdenoff/fe-libs/chrome";
13
13
  //#region src/pages/dashboard/DashboardPage.tsx
14
- var D = {
14
+ var T = {
15
15
  workspaces: {
16
16
  noun: "Workspaces",
17
17
  subtitle: "Your workspace overview at a glance",
18
18
  purpose: "This is your home base — a single glance across everything that matters: your workspaces and projects, recent activity, upcoming events and live status. Use Customize to choose which cards appear and how they are arranged, and click any card to dive into the full view.",
19
- Icon: f
19
+ Icon: d
20
20
  },
21
21
  vibecontrols: {
22
22
  noun: "VibeControls",
23
23
  subtitle: "Your workspace at a glance",
24
24
  purpose: "This is your home base for this workspace — check on recent vibes, active sessions, and your agent fleet, then jump straight into whatever needs you. Use Customize to choose which cards appear and how they are arranged.",
25
- Icon: _
25
+ Icon: h
26
26
  },
27
27
  fluidgrids: {
28
28
  noun: "FluidGrids",
29
29
  subtitle: "Your workflow automation at a glance",
30
30
  purpose: "This is your home base — track workflow health, recent runs, and team activity, then jump straight into building or debugging. Use Customize to choose which cards appear and how they are arranged.",
31
- Icon: p
31
+ Icon: f
32
32
  }
33
33
  };
34
- function O({ navigate: f, authToken: p, currentUser: _, productId: O = "workspaces" }) {
34
+ function E(e, t, n) {
35
+ switch (t) {
36
+ case "vibecontrols": return {
37
+ noun: e("dashboard.titleNoun.vibecontrols", n.noun),
38
+ subtitle: e("dashboard.subtitle.vibecontrols", n.subtitle),
39
+ purpose: e("dashboard.purpose.vibecontrols", n.purpose)
40
+ };
41
+ case "fluidgrids": return {
42
+ noun: e("dashboard.titleNoun.fluidgrids", n.noun),
43
+ subtitle: e("dashboard.subtitle.fluidgrids", n.subtitle),
44
+ purpose: e("dashboard.purpose.fluidgrids", n.purpose)
45
+ };
46
+ default: return {
47
+ noun: e("dashboard.titleNoun.workspaces", n.noun),
48
+ subtitle: e("dashboard.subtitle.workspaces", n.subtitle),
49
+ purpose: e("dashboard.purpose.workspaces", n.purpose)
50
+ };
51
+ }
52
+ }
53
+ function D({ navigate: d, authToken: f, currentUser: h, productId: D = "workspaces" }) {
35
54
  e("workspaces.dashboard");
36
- let { t: te } = T(), k = (e, t) => {
37
- let n = te(e);
55
+ let { t: O } = C(), k = (e, t) => {
56
+ let n = O(e);
38
57
  return n === e ? t : n;
39
- }, [ne, A] = u(!1), j = D[O] ?? D.workspaces, M = j.Icon, { data: N, isLoading: P, error: F, refetch: I } = i(p), { data: L, isLoading: R, error: z, refetch: B } = t(p), { data: V, isLoading: H, refetch: U } = n(p), W = O === "vibecontrols", { data: G, isLoading: K, refetch: q } = r(p, W), J = a(p), Y = P || R || H || K, X = l(() => !N && !L && !V && !G ? null : {
40
- ...N ?? {
58
+ }, [re, A] = l(!1), j = T[D] ?? T.workspaces, M = E(k, D, j), N = j.Icon, { data: P, isLoading: F, error: I, refetch: L } = i(f), { data: R, isLoading: z, error: B, refetch: V } = t(f), { data: H, isLoading: U, refetch: W } = n(f), G = D === "vibecontrols", { data: K, isLoading: q, refetch: J } = r(f, G), Y = ee(f), X = F || z || U || q, ie = c(() => !P && !R && !H && !K ? null : {
59
+ ...P ?? {
41
60
  workspaces: {
42
61
  items: [],
43
62
  total: 0
@@ -59,66 +78,66 @@ function O({ navigate: f, authToken: p, currentUser: _, productId: O = "workspac
59
78
  workflowStats: null,
60
79
  storageUsage: null
61
80
  },
62
- workspaces: L?.workspaces ?? {
81
+ workspaces: R?.workspaces ?? {
63
82
  items: [],
64
83
  total: 0
65
84
  },
66
- projects: V ?? {
85
+ projects: H ?? {
67
86
  items: [],
68
87
  total: 0
69
88
  },
70
- recentVibes: G?.recentVibes,
71
- activeSessions: G?.activeSessions,
72
- agentFleet: G?.agentFleet ?? null
89
+ recentVibes: K?.recentVibes,
90
+ activeSessions: K?.activeSessions,
91
+ agentFleet: K?.agentFleet ?? null
92
+ }, [
93
+ P,
94
+ R,
95
+ H,
96
+ K
97
+ ]), Z = R?.dashboardPreferences, Q = c(() => a(Z?.widgets ?? null, D), [Z, D]), $ = s(() => {
98
+ L(), V(), W(), G && J();
73
99
  }, [
74
- N,
75
100
  L,
76
101
  V,
102
+ W,
103
+ J,
77
104
  G
78
- ]), Z = L?.dashboardPreferences, Q = l(() => o(Z?.widgets ?? null, O), [Z, O]), $ = c(() => {
79
- I(), B(), U(), W && q();
80
- }, [
81
- I,
82
- B,
83
- U,
84
- q,
85
- W
86
- ]), re = c(async (e) => {
87
- await J.mutateAsync(e);
88
- }, [J]), ie = F || z;
89
- return /* @__PURE__ */ b("div", {
105
+ ]), ae = s(async (e) => {
106
+ await Y.mutateAsync(e);
107
+ }, [Y]), oe = I || B;
108
+ return /* @__PURE__ */ v("div", {
90
109
  className: "min-h-[calc(100vh-8rem)] p-4 md:p-6 bg-bg-canvas",
91
110
  "data-tour": "main-content",
92
111
  children: [
93
- /* @__PURE__ */ b("div", {
112
+ /* @__PURE__ */ v("div", {
94
113
  className: "relative overflow-hidden rounded-card -mx-1 px-1 pt-1",
95
- children: [/* @__PURE__ */ y(x, { intensity: "subtle" }), /* @__PURE__ */ y(E, {
96
- icon: /* @__PURE__ */ y(M, { className: "size-6" }),
97
- title: /* @__PURE__ */ b(v, { children: [
98
- /* @__PURE__ */ y(C, { children: k(`dashboard.titleNoun.${O}`, j.noun) }),
114
+ children: [/* @__PURE__ */ _(y, { intensity: "subtle" }), /* @__PURE__ */ _(w, {
115
+ icon: /* @__PURE__ */ _(N, { className: "size-6" }),
116
+ title: /* @__PURE__ */ v(g, { children: [
117
+ /* @__PURE__ */ _(x, { children: M.noun }),
99
118
  " ",
100
119
  k("dashboard.titleSuffix", "Dashboard")
101
120
  ] }),
102
- description: k(`dashboard.subtitle.${O}`, j.subtitle),
103
- actions: /* @__PURE__ */ b("div", {
121
+ description: M.subtitle,
122
+ actions: /* @__PURE__ */ v("div", {
104
123
  className: "flex flex-wrap items-center gap-2",
105
124
  "data-tour": "quick-actions",
106
- children: [/* @__PURE__ */ b(S, {
125
+ children: [/* @__PURE__ */ v(b, {
107
126
  variant: "outline",
108
127
  size: "sm",
109
128
  onClick: $,
110
- disabled: Y,
129
+ disabled: X,
111
130
  className: "text-xs",
112
- children: [Y ? /* @__PURE__ */ y(m, { className: "size-3.5 sm:mr-1.5 animate-spin" }) : /* @__PURE__ */ y(h, { className: "size-3.5 sm:mr-1.5" }), /* @__PURE__ */ y("span", {
131
+ children: [X ? /* @__PURE__ */ _(p, { className: "size-3.5 sm:mr-1.5 animate-spin" }) : /* @__PURE__ */ _(m, { className: "size-3.5 sm:mr-1.5" }), /* @__PURE__ */ _("span", {
113
132
  className: "hidden sm:inline",
114
133
  children: k("dashboard.refresh", "Refresh")
115
134
  })]
116
- }), /* @__PURE__ */ b(S, {
135
+ }), /* @__PURE__ */ v(b, {
117
136
  variant: "outline",
118
137
  size: "sm",
119
138
  onClick: () => A(!0),
120
139
  className: "text-xs",
121
- children: [/* @__PURE__ */ y(g, { className: "size-3.5 sm:mr-1.5" }), /* @__PURE__ */ y("span", {
140
+ children: [/* @__PURE__ */ _(ne, { className: "size-3.5 sm:mr-1.5" }), /* @__PURE__ */ _("span", {
122
141
  className: "hidden sm:inline",
123
142
  children: k("dashboard.customize", "Customize")
124
143
  })]
@@ -126,23 +145,23 @@ function O({ navigate: f, authToken: p, currentUser: _, productId: O = "workspac
126
145
  })
127
146
  })]
128
147
  }),
129
- /* @__PURE__ */ y(w, {
130
- icon: /* @__PURE__ */ y(M, { className: "size-4" }),
148
+ /* @__PURE__ */ _(S, {
149
+ icon: /* @__PURE__ */ _(N, { className: "size-4" }),
131
150
  className: "mb-6 -mt-2",
132
- children: k(`dashboard.purpose.${O}`, j.purpose)
151
+ children: M.purpose
133
152
  }),
134
- ie && /* @__PURE__ */ b("div", {
153
+ oe && /* @__PURE__ */ v("div", {
135
154
  className: "mb-4 p-3 bg-status-error-bg/10 border border-status-error-border/20 rounded-lg flex items-center gap-2",
136
- children: [/* @__PURE__ */ y(d, { className: "size-4 text-status-error-text shrink-0" }), /* @__PURE__ */ b("p", {
155
+ children: [/* @__PURE__ */ _(u, { className: "size-4 text-status-error-text shrink-0" }), /* @__PURE__ */ v("p", {
137
156
  className: "text-sm text-status-error-text",
138
157
  children: [
139
158
  k("dashboard.error.partialData", "Some data may be unavailable."),
140
159
  " ",
141
- F ? k("dashboard.error.workspaceDown", "Workspace services may be down.") : "",
160
+ I ? k("dashboard.error.workspaceDown", "Workspace services may be down.") : "",
142
161
  " ",
143
- z ? k("dashboard.error.globalDown", "Global services may be down.") : "",
162
+ B ? k("dashboard.error.globalDown", "Global services may be down.") : "",
144
163
  " ",
145
- /* @__PURE__ */ y("button", {
164
+ /* @__PURE__ */ _("button", {
146
165
  type: "button",
147
166
  className: "underline font-medium hover:text-status-error-text/80",
148
167
  onClick: $,
@@ -151,26 +170,26 @@ function O({ navigate: f, authToken: p, currentUser: _, productId: O = "workspac
151
170
  ]
152
171
  })]
153
172
  }),
154
- /* @__PURE__ */ y(s, {
173
+ /* @__PURE__ */ _(te, {
155
174
  widgets: Q,
156
- workspaceData: X,
157
- globalData: L ?? null,
158
- isLoading: Y,
159
- navigate: f,
160
- productId: O
175
+ workspaceData: ie,
176
+ globalData: R ?? null,
177
+ isLoading: X,
178
+ navigate: d,
179
+ productId: D
161
180
  }),
162
- /* @__PURE__ */ y(ee, {
163
- open: ne,
181
+ /* @__PURE__ */ _(o, {
182
+ open: re,
164
183
  onOpenChange: A,
165
184
  widgets: Q,
166
- onSave: re,
167
- isSaving: J.isPending,
168
- productId: O
185
+ onSave: ae,
186
+ isSaving: Y.isPending,
187
+ productId: D
169
188
  })
170
189
  ]
171
190
  });
172
191
  }
173
192
  //#endregion
174
- export { O as DashboardPage };
193
+ export { D as DashboardPage };
175
194
 
176
195
  //# sourceMappingURL=DashboardPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/pages/dashboard/DashboardPage.tsx"],"sourcesContent":["/**\n * DashboardPage — Comprehensive workspace dashboard.\n *\n * This is the main dashboard component exported from microfe-workspaces.\n * It fetches data from both workspace and global gateways, manages widget\n * layout preferences, and renders a customizable grid of widgets.\n *\n * Props:\n * - navigate: Shell navigation function\n * - authToken: Current user's auth token\n * - currentUser: Basic user info from shell\n *\n * Data Flow:\n * 1. Two parallel queries: workspace gateway + global gateway\n * 2. Global query returns user preferences (dashboard layout)\n * 3. Widget layout falls back to defaults if no preferences saved\n * 4. Data is passed as props to individual widgets\n */\nimport { useState, useMemo, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Button, PagePurpose, AuroraBackground, GradientText } from '@burdenoff/fe-libs/ui';\nimport { PageHeader } from '@burdenoff/fe-libs/chrome';\nimport {\n Settings2,\n RefreshCw,\n Loader2,\n AlertCircle,\n Building2,\n Sparkles,\n GitBranch,\n type LucideIcon,\n} from 'lucide-react';\nimport {\n useWorkspaceDashboardData,\n useGlobalDashboardData,\n useProjectsDashboardData,\n useVibecontrolsDashboardData,\n} from './hooks/useDashboardData';\nimport { useUpdateDashboardPreferences } from './hooks/useDashboardPreferences';\nimport { DashboardGrid } from './DashboardGrid';\nimport { DashboardCustomizer } from './DashboardCustomizer';\nimport { getEffectiveWidgets } from './config';\nimport type { WidgetConfig } from './types';\nimport { useWorkspacesPageView } from '../../hooks/useWorkspacesPageView';\n\ninterface ProductHeroCopy {\n noun: string;\n subtitle: string;\n purpose: string;\n Icon: LucideIcon;\n}\n\n/** Per-product hero heading/subtitle/purpose copy — keeps the dashboard from\n * reading as generic boilerplate for products layered on the shared page. */\nconst PRODUCT_HERO: Record<string, ProductHeroCopy> = {\n workspaces: {\n noun: 'Workspaces',\n subtitle: 'Your workspace overview at a glance',\n purpose:\n 'This is your home base — a single glance across everything that matters: your workspaces and projects, recent activity, upcoming events and live status. Use Customize to choose which cards appear and how they are arranged, and click any card to dive into the full view.',\n Icon: Building2,\n },\n vibecontrols: {\n noun: 'VibeControls',\n subtitle: 'Your workspace at a glance',\n purpose:\n 'This is your home base for this workspace — check on recent vibes, active sessions, and your agent fleet, then jump straight into whatever needs you. Use Customize to choose which cards appear and how they are arranged.',\n Icon: Sparkles,\n },\n fluidgrids: {\n noun: 'FluidGrids',\n subtitle: 'Your workflow automation at a glance',\n purpose:\n 'This is your home base — track workflow health, recent runs, and team activity, then jump straight into building or debugging. Use Customize to choose which cards appear and how they are arranged.',\n Icon: GitBranch,\n },\n};\n\ninterface DashboardPageProps {\n navigate: (path: string) => void;\n authToken?: string | null;\n currentUser?: {\n id: string;\n email: string;\n firstName?: string;\n lastName?: string;\n name?: string;\n } | null;\n /**\n * Host product identifier ('workspaces', 'vibecontrols', etc.). Drives\n * which widgets appear in the registry + default layout. Defaults to\n * 'workspaces' for legacy callers.\n */\n productId?: string;\n}\n\nexport function DashboardPage({\n navigate,\n authToken,\n currentUser: _currentUser,\n productId = 'workspaces',\n}: DashboardPageProps) {\n useWorkspacesPageView('workspaces.dashboard');\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const [customizerOpen, setCustomizerOpen] = useState(false);\n const hero = PRODUCT_HERO[productId] ?? PRODUCT_HERO.workspaces;\n const HeroIcon = hero.Icon;\n\n // ─── Data Fetching ────────────────────────────────────────────────────────\n const {\n data: workspaceData,\n isLoading: wsLoading,\n error: wsError,\n refetch: refetchWs,\n } = useWorkspaceDashboardData(authToken);\n\n const {\n data: globalData,\n isLoading: globalLoading,\n error: globalError,\n refetch: refetchGlobal,\n } = useGlobalDashboardData(authToken);\n\n const {\n data: projectsData,\n isLoading: projectsLoading,\n refetch: refetchProjects,\n } = useProjectsDashboardData(authToken);\n\n const isVibecontrols = productId === 'vibecontrols';\n const {\n data: vibecontrolsData,\n isLoading: vibecontrolsLoading,\n refetch: refetchVibecontrols,\n } = useVibecontrolsDashboardData(authToken, isVibecontrols);\n\n const updatePrefs = useUpdateDashboardPreferences(authToken);\n\n const isLoading = wsLoading || globalLoading || projectsLoading || vibecontrolsLoading;\n\n // ─── Merge data ───────────────────────────────────────────────────────────\n // Workspace/org stats come from the global tenant service (globalData),\n // while workspace-scoped features (activity, calendar, tags, etc.) come\n // from the workspace gateway (workspaceData). Merge them so widgets\n // reading workspaceData.workspaces see the real tenant data.\n const mergedWorkspaceData = useMemo(() => {\n if (!workspaceData && !globalData && !projectsData && !vibecontrolsData) return null;\n return {\n ...(workspaceData ?? {\n workspaces: { items: [], total: 0 },\n projects: { items: [], total: 0 },\n recentActivities: [],\n upcomingEvents: [],\n tags: { items: [], total: 0 },\n groups: { items: [], totalCount: 0 },\n workflowStats: null,\n storageUsage: null,\n }),\n // Override with real workspace/org data from the global gateway\n workspaces: globalData?.workspaces ?? { items: [], total: 0 },\n // Override with real project data from the separate projects query\n // (fetched independently to avoid being poisoned by other subgraph errors)\n projects: projectsData ?? { items: [], total: 0 },\n // VibeControls-only data; undefined for other products\n recentVibes: vibecontrolsData?.recentVibes,\n activeSessions: vibecontrolsData?.activeSessions,\n agentFleet: vibecontrolsData?.agentFleet ?? null,\n };\n }, [workspaceData, globalData, projectsData, vibecontrolsData]);\n\n // ─── Widget Configuration ─────────────────────────────────────────────────\n const savedPrefs = globalData?.dashboardPreferences;\n const widgets = useMemo(\n () => getEffectiveWidgets(savedPrefs?.widgets ?? null, productId),\n [savedPrefs, productId]\n );\n\n // ─── Handlers ─────────────────────────────────────────────────────────────\n const handleRefresh = useCallback(() => {\n refetchWs();\n refetchGlobal();\n refetchProjects();\n if (isVibecontrols) refetchVibecontrols();\n }, [refetchWs, refetchGlobal, refetchProjects, refetchVibecontrols, isVibecontrols]);\n\n const handleSaveWidgets = useCallback(\n async (newWidgets: WidgetConfig[]) => {\n await updatePrefs.mutateAsync(newWidgets);\n },\n [updatePrefs]\n );\n\n // ─── Error Banner ─────────────────────────────────────────────────────────\n const hasError = wsError || globalError;\n\n return (\n <div className=\"min-h-[calc(100vh-8rem)] p-4 md:p-6 bg-bg-canvas\" data-tour=\"main-content\">\n {/* Header */}\n <div className=\"relative overflow-hidden rounded-card -mx-1 px-1 pt-1\">\n <AuroraBackground intensity=\"subtle\" />\n <PageHeader\n icon={<HeroIcon className=\"size-6\" />}\n title={\n <>\n <GradientText>{tr(`dashboard.titleNoun.${productId}`, hero.noun)}</GradientText>{' '}\n {tr('dashboard.titleSuffix', 'Dashboard')}\n </>\n }\n description={tr(`dashboard.subtitle.${productId}`, hero.subtitle)}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\" data-tour=\"quick-actions\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleRefresh}\n disabled={isLoading}\n className=\"text-xs\"\n >\n {isLoading ? (\n <Loader2 className=\"size-3.5 sm:mr-1.5 animate-spin\" />\n ) : (\n <RefreshCw className=\"size-3.5 sm:mr-1.5\" />\n )}\n <span className=\"hidden sm:inline\">{tr('dashboard.refresh', 'Refresh')}</span>\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setCustomizerOpen(true)}\n className=\"text-xs\"\n >\n <Settings2 className=\"size-3.5 sm:mr-1.5\" />\n <span className=\"hidden sm:inline\">{tr('dashboard.customize', 'Customize')}</span>\n </Button>\n </div>\n }\n />\n </div>\n\n <PagePurpose icon={<HeroIcon className=\"size-4\" />} className=\"mb-6 -mt-2\">\n {tr(`dashboard.purpose.${productId}`, hero.purpose)}\n </PagePurpose>\n\n {/* Error Banner */}\n {hasError && (\n <div className=\"mb-4 p-3 bg-status-error-bg/10 border border-status-error-border/20 rounded-lg flex items-center gap-2\">\n <AlertCircle className=\"size-4 text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">\n {tr('dashboard.error.partialData', 'Some data may be unavailable.')}{' '}\n {wsError ? tr('dashboard.error.workspaceDown', 'Workspace services may be down.') : ''}{' '}\n {globalError ? tr('dashboard.error.globalDown', 'Global services may be down.') : ''}{' '}\n <button\n type=\"button\"\n className=\"underline font-medium hover:text-status-error-text/80\"\n onClick={handleRefresh}\n >\n {tr('dashboard.error.tryRefreshing', 'Try refreshing')}\n </button>\n </p>\n </div>\n )}\n\n {/* Widget Grid */}\n {/* Merge workspace/org data from the global query into workspaceData\n so widgets that read workspaceData.workspaces see the real data\n from the global tenant service. */}\n <DashboardGrid\n widgets={widgets}\n workspaceData={mergedWorkspaceData}\n globalData={globalData ?? null}\n isLoading={isLoading}\n navigate={navigate}\n productId={productId}\n />\n\n {/* Customizer */}\n <DashboardCustomizer\n open={customizerOpen}\n onOpenChange={setCustomizerOpen}\n widgets={widgets}\n onSave={handleSaveWidgets}\n isSaving={updatePrefs.isPending}\n productId={productId}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/pages/dashboard/DashboardPage.tsx"],"sourcesContent":["/**\n * DashboardPage — Comprehensive workspace dashboard.\n *\n * This is the main dashboard component exported from microfe-workspaces.\n * It fetches data from both workspace and global gateways, manages widget\n * layout preferences, and renders a customizable grid of widgets.\n *\n * Props:\n * - navigate: Shell navigation function\n * - authToken: Current user's auth token\n * - currentUser: Basic user info from shell\n *\n * Data Flow:\n * 1. Two parallel queries: workspace gateway + global gateway\n * 2. Global query returns user preferences (dashboard layout)\n * 3. Widget layout falls back to defaults if no preferences saved\n * 4. Data is passed as props to individual widgets\n */\nimport { useState, useMemo, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Button, PagePurpose, AuroraBackground, GradientText } from '@burdenoff/fe-libs/ui';\nimport { PageHeader } from '@burdenoff/fe-libs/chrome';\nimport {\n Settings2,\n RefreshCw,\n Loader2,\n AlertCircle,\n Building2,\n Sparkles,\n GitBranch,\n type LucideIcon,\n} from 'lucide-react';\nimport {\n useWorkspaceDashboardData,\n useGlobalDashboardData,\n useProjectsDashboardData,\n useVibecontrolsDashboardData,\n} from './hooks/useDashboardData';\nimport { useUpdateDashboardPreferences } from './hooks/useDashboardPreferences';\nimport { DashboardGrid } from './DashboardGrid';\nimport { DashboardCustomizer } from './DashboardCustomizer';\nimport { getEffectiveWidgets } from './config';\nimport type { WidgetConfig } from './types';\nimport { useWorkspacesPageView } from '../../hooks/useWorkspacesPageView';\n\ninterface ProductHeroCopy {\n noun: string;\n subtitle: string;\n purpose: string;\n Icon: LucideIcon;\n}\n\n/** Per-product hero heading/subtitle/purpose copy — keeps the dashboard from\n * reading as generic boilerplate for products layered on the shared page. */\nconst PRODUCT_HERO: Record<string, ProductHeroCopy> = {\n workspaces: {\n noun: 'Workspaces',\n subtitle: 'Your workspace overview at a glance',\n purpose:\n 'This is your home base — a single glance across everything that matters: your workspaces and projects, recent activity, upcoming events and live status. Use Customize to choose which cards appear and how they are arranged, and click any card to dive into the full view.',\n Icon: Building2,\n },\n vibecontrols: {\n noun: 'VibeControls',\n subtitle: 'Your workspace at a glance',\n purpose:\n 'This is your home base for this workspace — check on recent vibes, active sessions, and your agent fleet, then jump straight into whatever needs you. Use Customize to choose which cards appear and how they are arranged.',\n Icon: Sparkles,\n },\n fluidgrids: {\n noun: 'FluidGrids',\n subtitle: 'Your workflow automation at a glance',\n purpose:\n 'This is your home base — track workflow health, recent runs, and team activity, then jump straight into building or debugging. Use Customize to choose which cards appear and how they are arranged.',\n Icon: GitBranch,\n },\n};\n\n/**\n * Resolve the per-product hero copy through i18n with **literal** keys.\n *\n * These three strings used to build their key by interpolating productId into\n * a template literal. The key extractor\n * (fluidgrids-app/scripts/extract-i18n-keys.ts) only collects string literals,\n * so an interpolated key is never extracted and never seeded — it can only ever\n * render its English fallback, in every locale, forever. Spelling each product\n * out gives the extractor something to find. [BOFF-5997]\n */\nfunction resolveHeroCopy(\n tr: (key: string, fallback: string) => string,\n productId: string,\n hero: ProductHeroCopy\n): { noun: string; subtitle: string; purpose: string } {\n switch (productId) {\n case 'vibecontrols':\n return {\n noun: tr('dashboard.titleNoun.vibecontrols', hero.noun),\n subtitle: tr('dashboard.subtitle.vibecontrols', hero.subtitle),\n purpose: tr('dashboard.purpose.vibecontrols', hero.purpose),\n };\n case 'fluidgrids':\n return {\n noun: tr('dashboard.titleNoun.fluidgrids', hero.noun),\n subtitle: tr('dashboard.subtitle.fluidgrids', hero.subtitle),\n purpose: tr('dashboard.purpose.fluidgrids', hero.purpose),\n };\n default:\n return {\n noun: tr('dashboard.titleNoun.workspaces', hero.noun),\n subtitle: tr('dashboard.subtitle.workspaces', hero.subtitle),\n purpose: tr('dashboard.purpose.workspaces', hero.purpose),\n };\n }\n}\n\ninterface DashboardPageProps {\n navigate: (path: string) => void;\n authToken?: string | null;\n currentUser?: {\n id: string;\n email: string;\n firstName?: string;\n lastName?: string;\n name?: string;\n } | null;\n /**\n * Host product identifier ('workspaces', 'vibecontrols', etc.). Drives\n * which widgets appear in the registry + default layout. Defaults to\n * 'workspaces' for legacy callers.\n */\n productId?: string;\n}\n\nexport function DashboardPage({\n navigate,\n authToken,\n currentUser: _currentUser,\n productId = 'workspaces',\n}: DashboardPageProps) {\n useWorkspacesPageView('workspaces.dashboard');\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const [customizerOpen, setCustomizerOpen] = useState(false);\n const hero = PRODUCT_HERO[productId] ?? PRODUCT_HERO.workspaces;\n const heroCopy = resolveHeroCopy(tr, productId, hero);\n const HeroIcon = hero.Icon;\n\n // ─── Data Fetching ────────────────────────────────────────────────────────\n const {\n data: workspaceData,\n isLoading: wsLoading,\n error: wsError,\n refetch: refetchWs,\n } = useWorkspaceDashboardData(authToken);\n\n const {\n data: globalData,\n isLoading: globalLoading,\n error: globalError,\n refetch: refetchGlobal,\n } = useGlobalDashboardData(authToken);\n\n const {\n data: projectsData,\n isLoading: projectsLoading,\n refetch: refetchProjects,\n } = useProjectsDashboardData(authToken);\n\n const isVibecontrols = productId === 'vibecontrols';\n const {\n data: vibecontrolsData,\n isLoading: vibecontrolsLoading,\n refetch: refetchVibecontrols,\n } = useVibecontrolsDashboardData(authToken, isVibecontrols);\n\n const updatePrefs = useUpdateDashboardPreferences(authToken);\n\n const isLoading = wsLoading || globalLoading || projectsLoading || vibecontrolsLoading;\n\n // ─── Merge data ───────────────────────────────────────────────────────────\n // Workspace/org stats come from the global tenant service (globalData),\n // while workspace-scoped features (activity, calendar, tags, etc.) come\n // from the workspace gateway (workspaceData). Merge them so widgets\n // reading workspaceData.workspaces see the real tenant data.\n const mergedWorkspaceData = useMemo(() => {\n if (!workspaceData && !globalData && !projectsData && !vibecontrolsData) return null;\n return {\n ...(workspaceData ?? {\n workspaces: { items: [], total: 0 },\n projects: { items: [], total: 0 },\n recentActivities: [],\n upcomingEvents: [],\n tags: { items: [], total: 0 },\n groups: { items: [], totalCount: 0 },\n workflowStats: null,\n storageUsage: null,\n }),\n // Override with real workspace/org data from the global gateway\n workspaces: globalData?.workspaces ?? { items: [], total: 0 },\n // Override with real project data from the separate projects query\n // (fetched independently to avoid being poisoned by other subgraph errors)\n projects: projectsData ?? { items: [], total: 0 },\n // VibeControls-only data; undefined for other products\n recentVibes: vibecontrolsData?.recentVibes,\n activeSessions: vibecontrolsData?.activeSessions,\n agentFleet: vibecontrolsData?.agentFleet ?? null,\n };\n }, [workspaceData, globalData, projectsData, vibecontrolsData]);\n\n // ─── Widget Configuration ─────────────────────────────────────────────────\n const savedPrefs = globalData?.dashboardPreferences;\n const widgets = useMemo(\n () => getEffectiveWidgets(savedPrefs?.widgets ?? null, productId),\n [savedPrefs, productId]\n );\n\n // ─── Handlers ─────────────────────────────────────────────────────────────\n const handleRefresh = useCallback(() => {\n refetchWs();\n refetchGlobal();\n refetchProjects();\n if (isVibecontrols) refetchVibecontrols();\n }, [refetchWs, refetchGlobal, refetchProjects, refetchVibecontrols, isVibecontrols]);\n\n const handleSaveWidgets = useCallback(\n async (newWidgets: WidgetConfig[]) => {\n await updatePrefs.mutateAsync(newWidgets);\n },\n [updatePrefs]\n );\n\n // ─── Error Banner ─────────────────────────────────────────────────────────\n const hasError = wsError || globalError;\n\n return (\n <div className=\"min-h-[calc(100vh-8rem)] p-4 md:p-6 bg-bg-canvas\" data-tour=\"main-content\">\n {/* Header */}\n <div className=\"relative overflow-hidden rounded-card -mx-1 px-1 pt-1\">\n <AuroraBackground intensity=\"subtle\" />\n <PageHeader\n icon={<HeroIcon className=\"size-6\" />}\n title={\n <>\n <GradientText>{heroCopy.noun}</GradientText>{' '}\n {tr('dashboard.titleSuffix', 'Dashboard')}\n </>\n }\n description={heroCopy.subtitle}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\" data-tour=\"quick-actions\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleRefresh}\n disabled={isLoading}\n className=\"text-xs\"\n >\n {isLoading ? (\n <Loader2 className=\"size-3.5 sm:mr-1.5 animate-spin\" />\n ) : (\n <RefreshCw className=\"size-3.5 sm:mr-1.5\" />\n )}\n <span className=\"hidden sm:inline\">{tr('dashboard.refresh', 'Refresh')}</span>\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setCustomizerOpen(true)}\n className=\"text-xs\"\n >\n <Settings2 className=\"size-3.5 sm:mr-1.5\" />\n <span className=\"hidden sm:inline\">{tr('dashboard.customize', 'Customize')}</span>\n </Button>\n </div>\n }\n />\n </div>\n\n <PagePurpose icon={<HeroIcon className=\"size-4\" />} className=\"mb-6 -mt-2\">\n {heroCopy.purpose}\n </PagePurpose>\n\n {/* Error Banner */}\n {hasError && (\n <div className=\"mb-4 p-3 bg-status-error-bg/10 border border-status-error-border/20 rounded-lg flex items-center gap-2\">\n <AlertCircle className=\"size-4 text-status-error-text shrink-0\" />\n <p className=\"text-sm text-status-error-text\">\n {tr('dashboard.error.partialData', 'Some data may be unavailable.')}{' '}\n {wsError ? tr('dashboard.error.workspaceDown', 'Workspace services may be down.') : ''}{' '}\n {globalError ? tr('dashboard.error.globalDown', 'Global services may be down.') : ''}{' '}\n <button\n type=\"button\"\n className=\"underline font-medium hover:text-status-error-text/80\"\n onClick={handleRefresh}\n >\n {tr('dashboard.error.tryRefreshing', 'Try refreshing')}\n </button>\n </p>\n </div>\n )}\n\n {/* Widget Grid */}\n {/* Merge workspace/org data from the global query into workspaceData\n so widgets that read workspaceData.workspaces see the real data\n from the global tenant service. */}\n <DashboardGrid\n widgets={widgets}\n workspaceData={mergedWorkspaceData}\n globalData={globalData ?? null}\n isLoading={isLoading}\n navigate={navigate}\n productId={productId}\n />\n\n {/* Customizer */}\n <DashboardCustomizer\n open={customizerOpen}\n onOpenChange={setCustomizerOpen}\n widgets={widgets}\n onSave={handleSaveWidgets}\n isSaving={updatePrefs.isPending}\n productId={productId}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAsDA,IAAM,IAAgD;CACpD,YAAY;EACV,MAAM;EACN,UAAU;EACV,SACE;EACF,MAAM;EACP;CACD,cAAc;EACZ,MAAM;EACN,UAAU;EACV,SACE;EACF,MAAM;EACP;CACD,YAAY;EACV,MAAM;EACN,UAAU;EACV,SACE;EACF,MAAM;EACP;CACF;AAYD,SAAS,EACP,GACA,GACA,GACqD;AACrD,SAAQ,GAAR;EACE,KAAK,eACH,QAAO;GACL,MAAM,EAAG,oCAAoC,EAAK,KAAK;GACvD,UAAU,EAAG,mCAAmC,EAAK,SAAS;GAC9D,SAAS,EAAG,kCAAkC,EAAK,QAAQ;GAC5D;EACH,KAAK,aACH,QAAO;GACL,MAAM,EAAG,kCAAkC,EAAK,KAAK;GACrD,UAAU,EAAG,iCAAiC,EAAK,SAAS;GAC5D,SAAS,EAAG,gCAAgC,EAAK,QAAQ;GAC1D;EACH,QACE,QAAO;GACL,MAAM,EAAG,kCAAkC,EAAK,KAAK;GACrD,UAAU,EAAG,iCAAiC,EAAK,SAAS;GAC5D,SAAS,EAAG,gCAAgC,EAAK,QAAQ;GAC1D;;;AAsBP,SAAgB,EAAc,EAC5B,aACA,cACA,aAAa,GACb,eAAY,gBACS;AACrB,GAAsB,uBAAuB;CAC7C,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAI,EAAE,EAAI;AAChB,SAAO,MAAM,IAAM,IAAW;IAE1B,CAAC,IAAgB,KAAqB,EAAS,GAAM,EACrD,IAAO,EAAa,MAAc,EAAa,YAC/C,IAAW,EAAgB,GAAI,GAAW,EAAK,EAC/C,IAAW,EAAK,MAGhB,EACJ,MAAM,GACN,WAAW,GACX,OAAO,GACP,SAAS,MACP,EAA0B,EAAU,EAElC,EACJ,MAAM,GACN,WAAW,GACX,OAAO,GACP,SAAS,MACP,EAAuB,EAAU,EAE/B,EACJ,MAAM,GACN,WAAW,GACX,SAAS,MACP,EAAyB,EAAU,EAEjC,IAAiB,MAAc,gBAC/B,EACJ,MAAM,GACN,WAAW,GACX,SAAS,MACP,EAA6B,GAAW,EAAe,EAErD,IAAc,GAA8B,EAAU,EAEtD,IAAY,KAAa,KAAiB,KAAmB,GAO7D,KAAsB,QACtB,CAAC,KAAiB,CAAC,KAAc,CAAC,KAAgB,CAAC,IAAyB,OACzE;EACL,GAAI,KAAiB;GACnB,YAAY;IAAE,OAAO,EAAE;IAAE,OAAO;IAAG;GACnC,UAAU;IAAE,OAAO,EAAE;IAAE,OAAO;IAAG;GACjC,kBAAkB,EAAE;GACpB,gBAAgB,EAAE;GAClB,MAAM;IAAE,OAAO,EAAE;IAAE,OAAO;IAAG;GAC7B,QAAQ;IAAE,OAAO,EAAE;IAAE,YAAY;IAAG;GACpC,eAAe;GACf,cAAc;GACf;EAED,YAAY,GAAY,cAAc;GAAE,OAAO,EAAE;GAAE,OAAO;GAAG;EAG7D,UAAU,KAAgB;GAAE,OAAO,EAAE;GAAE,OAAO;GAAG;EAEjD,aAAa,GAAkB;EAC/B,gBAAgB,GAAkB;EAClC,YAAY,GAAkB,cAAc;EAC7C,EACA;EAAC;EAAe;EAAY;EAAc;EAAiB,CAAC,EAGzD,IAAa,GAAY,sBACzB,IAAU,QACR,EAAoB,GAAY,WAAW,MAAM,EAAU,EACjE,CAAC,GAAY,EAAU,CACxB,EAGK,IAAgB,QAAkB;AAItC,EAHA,GAAW,EACX,GAAe,EACf,GAAiB,EACb,KAAgB,GAAqB;IACxC;EAAC;EAAW;EAAe;EAAiB;EAAqB;EAAe,CAAC,EAE9E,KAAoB,EACxB,OAAO,MAA+B;AACpC,QAAM,EAAY,YAAY,EAAW;IAE3C,CAAC,EAAY,CACd,EAGK,KAAW,KAAW;AAE5B,QACE,kBAAC,OAAD;EAAK,WAAU;EAAmD,aAAU;YAA5E;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAkB,WAAU,UAAW,CAAA,EACvC,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;KACrC,OACE,kBAAA,GAAA,EAAA,UAAA;MACE,kBAAC,GAAD,EAAA,UAAe,EAAS,MAAoB,CAAA;MAAC;MAC5C,EAAG,yBAAyB,YAAY;MACxC,EAAA,CAAA;KAEL,aAAa,EAAS;KACtB,SACE,kBAAC,OAAD;MAAK,WAAU;MAAoC,aAAU;gBAA7D,CACE,kBAAC,GAAD;OACE,SAAQ;OACR,MAAK;OACL,SAAS;OACT,UAAU;OACV,WAAU;iBALZ,CAOG,IACC,kBAAC,GAAD,EAAS,WAAU,mCAAoC,CAAA,GAEvD,kBAAC,GAAD,EAAW,WAAU,sBAAuB,CAAA,EAE9C,kBAAC,QAAD;QAAM,WAAU;kBAAoB,EAAG,qBAAqB,UAAU;QAAQ,CAAA,CACvE;UACT,kBAAC,GAAD;OACE,SAAQ;OACR,MAAK;OACL,eAAe,EAAkB,GAAK;OACtC,WAAU;iBAJZ,CAME,kBAAC,IAAD,EAAW,WAAU,sBAAuB,CAAA,EAC5C,kBAAC,QAAD;QAAM,WAAU;kBAAoB,EAAG,uBAAuB,YAAY;QAAQ,CAAA,CAC3E;SACL;;KAER,CAAA,CACE;;GAEN,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;IAAE,WAAU;cAC3D,EAAS;IACE,CAAA;GAGb,MACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAa,WAAU,0CAA2C,CAAA,EAClE,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG,EAAG,+BAA+B,gCAAgC;MAAE;MACpE,IAAU,EAAG,iCAAiC,kCAAkC,GAAG;MAAI;MACvF,IAAc,EAAG,8BAA8B,+BAA+B,GAAG;MAAI;MACtF,kBAAC,UAAD;OACE,MAAK;OACL,WAAU;OACV,SAAS;iBAER,EAAG,iCAAiC,iBAAiB;OAC/C,CAAA;MACP;OACA;;GAOR,kBAAC,IAAD;IACW;IACT,eAAe;IACf,YAAY,KAAc;IACf;IACD;IACC;IACX,CAAA;GAGF,kBAAC,GAAD;IACE,MAAM;IACN,cAAc;IACL;IACT,QAAQ;IACR,UAAU,EAAY;IACX;IACX,CAAA;GACE"}