@burdenoff/microfe-bigconsole 2026.801.2 → 2026.803.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.
@@ -7,63 +7,65 @@ function _(e) {
7
7
  return e.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean).join("-").slice(0, 60);
8
8
  }
9
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);
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(!1), [P, F] = i(null), [I, L] = i(null);
11
11
  r(() => {
12
- t && (T(y), D(_(y)), k(b ?? ""), N(null), F(null));
12
+ t && (T(y), D(_(y)), k(b ?? ""), j(!1), F(null), L(null));
13
13
  }, [
14
14
  t,
15
15
  y,
16
16
  b
17
17
  ]);
18
- let I = n(async () => {
18
+ let R = n(async () => {
19
19
  if (!w.trim()) {
20
- N("A template name is required");
20
+ F("A template name is required");
21
21
  return;
22
22
  }
23
23
  if (!E.trim()) {
24
- N("A slug is required");
24
+ F("A slug is required");
25
25
  return;
26
26
  }
27
27
  if (!O.trim()) {
28
- N("A description is required — it is what people see in the gallery");
28
+ F("A description is required — it is what people see in the gallery");
29
29
  return;
30
30
  }
31
- N(null), j(!0);
31
+ F(null), N(!0);
32
32
  try {
33
33
  let e = await C({
34
34
  dashboardId: v,
35
35
  name: w.trim(),
36
36
  slug: E.trim(),
37
- description: O.trim()
37
+ description: O.trim(),
38
+ includeSampleData: A
38
39
  });
39
40
  if (!e.success) {
40
- N(e.errorMessage ?? "Failed to publish to the store");
41
+ F(e.errorMessage ?? "Failed to publish to the store");
41
42
  return;
42
43
  }
43
- F(e), S?.(e);
44
+ L(e), S?.(e);
44
45
  } finally {
45
- j(!1);
46
+ N(!1);
46
47
  }
47
48
  }, [
48
49
  w,
49
50
  E,
50
51
  O,
52
+ A,
51
53
  v,
52
54
  C,
53
55
  S
54
- ]), L = {
56
+ ]), z = {
55
57
  display: "block",
56
58
  marginBottom: "var(--space-2xs)",
57
59
  fontSize: "var(--font-size-sm)"
58
60
  };
59
61
  return /* @__PURE__ */ h(o, {
60
62
  open: t,
61
- onOpenChange: (e) => !e && !A && x(),
63
+ onOpenChange: (e) => !e && !M && x(),
62
64
  children: /* @__PURE__ */ g(s, {
63
65
  style: { maxWidth: "520px" },
64
66
  children: [
65
67
  /* @__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", {
68
+ I ? /* @__PURE__ */ g("div", {
67
69
  style: { padding: "var(--space-md) 0" },
68
70
  children: [
69
71
  /* @__PURE__ */ h("p", {
@@ -76,21 +78,21 @@ var v = t(function({ isOpen: t, dashboardId: v, dashboardName: y, dashboardDescr
76
78
  color: "var(--color-text-muted)"
77
79
  },
78
80
  children: [
79
- P.sinkCount,
81
+ I.sinkCount,
80
82
  " data source",
81
- P.sinkCount === 1 ? "" : "s",
83
+ I.sinkCount === 1 ? "" : "s",
82
84
  " turned into slots. Finish in the Developer Portal: review the listing, then submit it for approval. It appears in the gallery once approved."
83
85
  ]
84
86
  }),
85
- P.unknownSinkIds.length > 0 && /* @__PURE__ */ g("p", {
87
+ I.unknownSinkIds.length > 0 && /* @__PURE__ */ g("p", {
86
88
  style: {
87
89
  fontSize: "var(--font-size-sm)",
88
90
  color: "var(--color-text-warning)"
89
91
  },
90
92
  children: [
91
- P.unknownSinkIds.length,
93
+ I.unknownSinkIds.length,
92
94
  " data source",
93
- P.unknownSinkIds.length === 1 ? "" : "s",
95
+ I.unknownSinkIds.length === 1 ? "" : "s",
94
96
  " could not be named — those slots use generic labels."
95
97
  ]
96
98
  })
@@ -104,56 +106,56 @@ var v = t(function({ isOpen: t, dashboardId: v, dashboardName: y, dashboardDescr
104
106
  children: [
105
107
  /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
106
108
  htmlFor: "tpl-name",
107
- style: L,
109
+ style: z,
108
110
  children: "Template name"
109
111
  }), /* @__PURE__ */ h(f, {
110
112
  id: "tpl-name",
111
113
  value: w,
112
- disabled: A,
114
+ disabled: M,
113
115
  onChange: (e) => {
114
116
  T(e.target.value), D(_(e.target.value));
115
117
  }
116
118
  })] }),
117
119
  /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
118
120
  htmlFor: "tpl-slug",
119
- style: L,
121
+ style: z,
120
122
  children: "Slug"
121
123
  }), /* @__PURE__ */ h(f, {
122
124
  id: "tpl-slug",
123
125
  value: E,
124
- disabled: A,
126
+ disabled: M,
125
127
  onChange: (e) => D(_(e.target.value))
126
128
  })] }),
127
129
  /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
128
130
  htmlFor: "tpl-desc",
129
- style: L,
131
+ style: z,
130
132
  children: "Description"
131
133
  }), /* @__PURE__ */ h(m, {
132
134
  id: "tpl-desc",
133
135
  rows: 3,
134
136
  value: O,
135
- disabled: A,
137
+ disabled: M,
136
138
  placeholder: "What is this dashboard for, and what data does it need?",
137
139
  onChange: (e) => k(e.target.value)
138
140
  })] }),
139
- M && /* @__PURE__ */ h("p", {
141
+ P && /* @__PURE__ */ h("p", {
140
142
  style: {
141
143
  color: "var(--color-text-danger)",
142
144
  fontSize: "var(--font-size-sm)"
143
145
  },
144
- children: M
146
+ children: P
145
147
  })
146
148
  ]
147
149
  }),
148
150
  /* @__PURE__ */ g(l, { children: [/* @__PURE__ */ h(a, {
149
151
  variant: "outline",
150
152
  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"
153
+ disabled: M,
154
+ children: I ? "Done" : "Cancel"
155
+ }), !I && /* @__PURE__ */ h(a, {
156
+ onClick: R,
157
+ disabled: M,
158
+ children: M ? /* @__PURE__ */ h(p, {}) : "Publish"
157
159
  })] })
158
160
  ]
159
161
  })
@@ -1 +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":";;;;;AA+CA,SAAS,EAAQ,GAAuB;AACtC,QAAO,EACJ,aAAa,CACb,MAAM,aAAa,CACnB,OAAO,QAAQ,CACf,KAAK,IAAI,CACT,MAAM,GAAG,GAAG;;AAGjB,IAAa,IAAsD,EAAK,SAA8B,EACpG,WACA,gBACA,kBACA,yBACA,YACA,kBACC;CACD,IAAM,EAAE,sBAAmB,EAA2B,EAAE,UAAU,IAAO,CAAC,EAEpE,CAAC,GAAM,KAAW,EAAS,EAAc,EACzC,CAAC,GAAM,KAAW,EAAS,EAAQ,EAAc,CAAC,EAClD,CAAC,GAAa,KAAkB,EAAS,KAAwB,GAAG,EACpE,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAQ,KAAa,EAAsC,KAAK;AAEvE,SAAgB;AACT,QACL,EAAQ,EAAc,EACtB,EAAQ,EAAQ,EAAc,CAAC,EAC/B,EAAe,KAAwB,GAAG,EAC1C,EAAS,KAAK,EACd,EAAU,KAAK;IACd;EAAC;EAAQ;EAAe;EAAqB,CAAC;CAEjD,IAAM,IAAgB,EAAY,YAAY;AAC5C,MAAI,CAAC,EAAK,MAAM,EAAE;AAChB,KAAS,8BAA8B;AACvC;;AAEF,MAAI,CAAC,EAAK,MAAM,EAAE;AAChB,KAAS,qBAAqB;AAC9B;;AAEF,MAAI,CAAC,EAAY,MAAM,EAAE;AACvB,KAAS,mEAAmE;AAC5E;;AAIF,EADA,EAAS,KAAK,EACd,EAAc,GAAK;AACnB,MAAI;GACF,IAAM,IAAY,MAAM,EAAe;IACrC;IACA,MAAM,EAAK,MAAM;IACjB,MAAM,EAAK,MAAM;IACjB,aAAa,EAAY,MAAM;IAChC,CAAC;AACF,OAAI,CAAC,EAAU,SAAS;AACtB,MAAS,EAAU,gBAAgB,iCAAiC;AACpE;;AAGF,GADA,EAAU,EAAU,EACpB,IAAc,EAAU;YAChB;AACR,KAAc,GAAM;;IAErB;EAAC;EAAM;EAAM;EAAa;EAAa;EAAgB;EAAY,CAAC,EAEjE,IAAQ;EAAE,SAAS;EAAS,cAAc;EAAoB,UAAU;EAAuB;AAErG,QACE,kBAAC,GAAD;EAAQ,MAAM;EAAQ,eAAe,MAAS,CAAC,KAAQ,CAAC,KAAc,GAAS;YAC7E,kBAAC,GAAD;GAAe,OAAO,EAAE,UAAU,SAAS;aAA3C;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAa,uBAAiC,CAAA,EAC9C,kBAAC,GAAD,EAAA,UAAmB,8IAGC,CAAA,CACP,EAAA,CAAA;IAEd,IACC,kBAAC,OAAD;KAAK,OAAO,EAAE,SAAS,qBAAqB;eAA5C;MACE,kBAAC,KAAD;OAAG,OAAO,EAAE,YAAY,6BAA6B;iBAAE;OAA0B,CAAA;MACjF,kBAAC,KAAD;OAAG,OAAO;QAAE,UAAU;QAAuB,OAAO;QAA2B;iBAA/E;QACG,EAAO;QAAU;QAAa,EAAO,cAAc,IAAI,KAAK;QAAI;QAG/D;;MACH,EAAO,eAAe,SAAS,KAC9B,kBAAC,KAAD;OAAG,OAAO;QAAE,UAAU;QAAuB,OAAO;QAA6B;iBAAjF;QACG,EAAO,eAAe;QAAO;QAC7B,EAAO,eAAe,WAAW,IAAI,KAAK;QAAI;QAC7C;;MAEF;SAEN,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,eAAe;MAAU,KAAK;MAAmB;eAAhF;MACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAW,OAAO;iBAAO;OAEhC,CAAA,EACR,kBAAC,GAAD;OACE,IAAG;OACH,OAAO;OACP,UAAU;OACV,WAAW,MAAM;AAEf,QADA,EAAQ,EAAE,OAAO,MAAM,EACvB,EAAQ,EAAQ,EAAE,OAAO,MAAM,CAAC;;OAElC,CAAA,CACE,EAAA,CAAA;MAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAW,OAAO;iBAAO;OAEhC,CAAA,EACR,kBAAC,GAAD;OACE,IAAG;OACH,OAAO;OACP,UAAU;OACV,WAAW,MAAM,EAAQ,EAAQ,EAAE,OAAO,MAAM,CAAC;OACjD,CAAA,CACE,EAAA,CAAA;MAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAW,OAAO;iBAAO;OAEhC,CAAA,EACR,kBAAC,GAAD;OACE,IAAG;OACH,MAAM;OACN,OAAO;OACP,UAAU;OACV,aAAY;OACZ,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;OAC/C,CAAA,CACE,EAAA,CAAA;MAEL,KAAS,kBAAC,KAAD;OAAG,OAAO;QAAE,OAAO;QAA4B,UAAU;QAAuB;iBAAG;OAAU,CAAA;MACnG;;IAGR,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,SAAS;KAAS,UAAU;eACnD,IAAS,SAAS;KACZ,CAAA,EACR,CAAC,KACA,kBAAC,GAAD;KAAQ,SAAS;KAAe,UAAU;eACvC,IAAa,kBAAC,GAAD,EAAW,CAAA,GAAG;KACrB,CAAA,CAEE,EAAA,CAAA;IACD;;EACT,CAAA;EAEX"}
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 [includeSampleData, setIncludeSampleData] = useState(false);\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 setIncludeSampleData(false);\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 includeSampleData,\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, includeSampleData, 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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"}
@@ -1,12 +1,18 @@
1
1
  import { asArray as e, asRecord as t, asString as n, formatCell as r, isNumeric as i } from "./shared.js";
2
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
2
+ import { resolveCellLinkHref as a } from "../../widgets/table/cellLink.js";
3
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
3
4
  //#region src/bigconsole/components/embed/widgets/TableEmbed.tsx
4
- var s = 500;
5
- function c(i, a) {
6
- let o = e(i.columns).map((e) => t(e)).map((e) => ({
7
- key: n(e.key),
8
- label: n(e.label ?? e.key)
9
- })).filter((e) => e.key.length > 0);
5
+ var c = 500;
6
+ function l(i, a) {
7
+ let o = e(i.columns).map((e) => t(e)).map((e) => {
8
+ let t = n(e.key) || n(e.field);
9
+ return {
10
+ key: t,
11
+ label: n(e.label ?? e.header ?? t),
12
+ format: n(e.format) || void 0,
13
+ linkPattern: n(e.linkPattern) || void 0
14
+ };
15
+ }).filter((e) => e.key.length > 0);
10
16
  if (o.length > 0) return o;
11
17
  let s = /* @__PURE__ */ new Set(), c = [];
12
18
  for (let e of a) for (let t of Object.keys(e)) s.has(t) || (s.add(t), c.push(t));
@@ -15,38 +21,48 @@ function c(i, a) {
15
21
  label: e
16
22
  }));
17
23
  }
18
- function l({ widget: n }) {
19
- let l = t(n.config), u = e(t(n.data).rows).map((e) => t(e)).slice(0, s), d = c(l, u);
20
- if (d.length === 0 || u.length === 0) return /* @__PURE__ */ a("div", {
24
+ function u({ widget: n }) {
25
+ let u = t(n.config), d = e(t(n.data).rows).map((e) => t(e)).slice(0, c), f = l(u, d);
26
+ if (f.length === 0 || d.length === 0) return /* @__PURE__ */ o("div", {
21
27
  className: "flex h-full items-center justify-center bg-bg-surface p-4 text-sm text-text-secondary",
22
28
  children: "No data"
23
29
  });
24
- let f = new Set(d.filter((e) => {
25
- let t = u.map((t) => t[e.key]).filter((e) => e != null && e !== "");
30
+ let p = new Set(f.filter((e) => {
31
+ let t = d.map((t) => t[e.key]).filter((e) => e != null && e !== "");
26
32
  return t.length > 0 && t.every(i);
27
33
  }).map((e) => e.key));
28
- return /* @__PURE__ */ o("div", {
34
+ return /* @__PURE__ */ s("div", {
29
35
  className: "h-full w-full overflow-auto bg-bg-surface",
30
- children: [n.title && /* @__PURE__ */ a("div", {
36
+ children: [n.title && /* @__PURE__ */ o("div", {
31
37
  className: "border-b border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary",
32
38
  children: n.title
33
- }), /* @__PURE__ */ o("table", {
39
+ }), /* @__PURE__ */ s("table", {
34
40
  className: "w-full border-collapse text-sm",
35
- children: [/* @__PURE__ */ a("thead", { children: /* @__PURE__ */ a("tr", { children: d.map((e) => /* @__PURE__ */ a("th", {
41
+ children: [/* @__PURE__ */ o("thead", { children: /* @__PURE__ */ o("tr", { children: f.map((e) => /* @__PURE__ */ o("th", {
36
42
  scope: "col",
37
- className: `sticky top-0 z-10 border-b border-border-default bg-bg-sunken px-3 py-2 font-medium text-text-secondary ${f.has(e.key) ? "text-right tabular-nums" : "text-left"}`,
43
+ className: `sticky top-0 z-10 border-b border-border-default bg-bg-sunken px-3 py-2 font-medium text-text-secondary ${p.has(e.key) ? "text-right tabular-nums" : "text-left"}`,
38
44
  children: e.label
39
- }, e.key)) }) }), /* @__PURE__ */ a("tbody", { children: u.map((e, t) => /* @__PURE__ */ a("tr", {
45
+ }, e.key)) }) }), /* @__PURE__ */ o("tbody", { children: d.map((e, t) => /* @__PURE__ */ o("tr", {
40
46
  className: "hover:bg-bg-sunken/50",
41
- children: d.map((t) => /* @__PURE__ */ a("td", {
42
- className: `border-b border-border-subtle px-3 py-2 text-text-primary ${f.has(t.key) ? "text-right tabular-nums" : "text-left"}`,
43
- children: r(e[t.key])
44
- }, t.key))
47
+ children: f.map((t) => {
48
+ let n = t.format === "link" ? a(t.linkPattern, e[t.key], e) : null;
49
+ return /* @__PURE__ */ o("td", {
50
+ className: `border-b border-border-subtle px-3 py-2 text-text-primary ${p.has(t.key) ? "text-right tabular-nums" : "text-left"}`,
51
+ children: n ? /* @__PURE__ */ o("a", {
52
+ href: n,
53
+ target: "_blank",
54
+ rel: "noopener noreferrer",
55
+ title: n,
56
+ className: "text-action-primary-bg hover:underline",
57
+ children: r(e[t.key])
58
+ }) : r(e[t.key])
59
+ }, t.key);
60
+ })
45
61
  }, t)) })]
46
62
  })]
47
63
  });
48
64
  }
49
65
  //#endregion
50
- export { l as TableEmbed };
66
+ export { u as TableEmbed };
51
67
 
52
68
  //# sourceMappingURL=TableEmbed.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TableEmbed.js","names":[],"sources":["../../../../../src/bigconsole/components/embed/widgets/TableEmbed.tsx"],"sourcesContent":["/** Read-only table embed renderer (BOFF-2986 / BOFF-5733). */\nimport type { EmbedWidgetRenderProps } from '../types';\nimport { asRecord, asArray, asString, formatCell, isNumeric } from './shared';\n\ninterface Column {\n key: string;\n label: string;\n}\n\n/** Hard client-side cap; the publication builder is expected to page/limit rows. */\nconst MAX_ROWS = 500;\n\nfunction readColumns(config: Record<string, unknown>, rows: Record<string, unknown>[]): Column[] {\n const configured = asArray(config.columns)\n .map((c) => asRecord(c))\n .map((c) => ({ key: asString(c.key), label: asString(c.label ?? c.key) }))\n .filter((c) => c.key.length > 0);\n // A publisher that pinned columns chose exactly what to show — respect it.\n if (configured.length > 0) return configured;\n\n // Derive from the data when the publication didn't pin columns. Union the keys\n // across all rows (rows can be sparse), then drop noise so an embed never shows\n // a raw internal dump: `_`-prefixed technical keys and columns that are empty\n // in every row (the \"empty metadata/badge column\" gap from BOFF-5733).\n const seen = new Set<string>();\n const keys: string[] = [];\n for (const row of rows) {\n for (const key of Object.keys(row)) {\n if (!seen.has(key)) {\n seen.add(key);\n keys.push(key);\n }\n }\n }\n return keys\n .filter((key) => !key.startsWith('_'))\n .filter((key) => rows.some((row) => formatCell(row[key]) !== ''))\n .map((key) => ({ key, label: key }));\n}\n\nexport function TableEmbed({ widget }: EmbedWidgetRenderProps) {\n const config = asRecord(widget.config);\n const data = asRecord(widget.data);\n const rows = asArray(data.rows)\n .map((r) => asRecord(r))\n .slice(0, MAX_ROWS);\n const columns = readColumns(config, rows);\n\n if (columns.length === 0 || rows.length === 0) {\n return (\n <div className=\"flex h-full items-center justify-center bg-bg-surface p-4 text-sm text-text-secondary\">\n No data\n </div>\n );\n }\n\n // Right-align a column when its populated cells are all numeric (matches the\n // in-app TableCell), so currency/counts line up on the decimal edge.\n const numericColumns = new Set(\n columns\n .filter((col) => {\n const values = rows.map((r) => r[col.key]).filter((v) => v !== null && v !== undefined && v !== '');\n return values.length > 0 && values.every(isNumeric);\n })\n .map((col) => col.key)\n );\n\n return (\n <div className=\"h-full w-full overflow-auto bg-bg-surface\">\n {widget.title && (\n <div className=\"border-b border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary\">\n {widget.title}\n </div>\n )}\n <table className=\"w-full border-collapse text-sm\">\n <thead>\n <tr>\n {columns.map((col) => (\n // `sticky` lives on the cells (not the title, which scrolls away)\n // so column headers stay visible while scrolling the 500-row cap.\n // Cells carry their own opaque background so rows don't show through.\n <th\n key={col.key}\n scope=\"col\"\n className={`sticky top-0 z-10 border-b border-border-default bg-bg-sunken px-3 py-2 font-medium text-text-secondary ${\n numericColumns.has(col.key) ? 'text-right tabular-nums' : 'text-left'\n }`}\n >\n {col.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.map((row, i) => (\n <tr key={i} className=\"hover:bg-bg-sunken/50\">\n {columns.map((col) => (\n <td\n key={col.key}\n className={`border-b border-border-subtle px-3 py-2 text-text-primary ${\n numericColumns.has(col.key) ? 'text-right tabular-nums' : 'text-left'\n }`}\n >\n {formatCell(row[col.key])}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":";;;AAUA,IAAM,IAAW;AAEjB,SAAS,EAAY,GAAiC,GAA2C;CAC/F,IAAM,IAAa,EAAQ,EAAO,QAAQ,CACvC,KAAK,MAAM,EAAS,EAAE,CAAC,CACvB,KAAK,OAAO;EAAE,KAAK,EAAS,EAAE,IAAI;EAAE,OAAO,EAAS,EAAE,SAAS,EAAE,IAAI;EAAE,EAAE,CACzE,QAAQ,MAAM,EAAE,IAAI,SAAS,EAAE;AAElC,KAAI,EAAW,SAAS,EAAG,QAAO;CAMlC,IAAM,oBAAO,IAAI,KAAa,EACxB,IAAiB,EAAE;AACzB,MAAK,IAAM,KAAO,EAChB,MAAK,IAAM,KAAO,OAAO,KAAK,EAAI,CAChC,CAAK,EAAK,IAAI,EAAI,KAChB,EAAK,IAAI,EAAI,EACb,EAAK,KAAK,EAAI;AAIpB,QAAO,EACJ,QAAQ,MAAQ,CAAC,EAAI,WAAW,IAAI,CAAC,CACrC,QAAQ,MAAQ,EAAK,MAAM,MAAQ,EAAW,EAAI,GAAK,KAAK,GAAG,CAAC,CAChE,KAAK,OAAS;EAAE;EAAK,OAAO;EAAK,EAAE;;AAGxC,SAAgB,EAAW,EAAE,aAAkC;CAC7D,IAAM,IAAS,EAAS,EAAO,OAAO,EAEhC,IAAO,EADA,EAAS,EAAO,KAAK,CACR,KAAK,CAC5B,KAAK,MAAM,EAAS,EAAE,CAAC,CACvB,MAAM,GAAG,EAAS,EACf,IAAU,EAAY,GAAQ,EAAK;AAEzC,KAAI,EAAQ,WAAW,KAAK,EAAK,WAAW,EAC1C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAwF;EAEjG,CAAA;CAMV,IAAM,IAAiB,IAAI,IACzB,EACG,QAAQ,MAAQ;EACf,IAAM,IAAS,EAAK,KAAK,MAAM,EAAE,EAAI,KAAK,CAAC,QAAQ,MAAM,KAAM,QAA2B,MAAM,GAAG;AACnG,SAAO,EAAO,SAAS,KAAK,EAAO,MAAM,EAAU;GACnD,CACD,KAAK,MAAQ,EAAI,IAAI,CACzB;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,EAAO,SACN,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAO;GACJ,CAAA,EAER,kBAAC,SAAD;GAAO,WAAU;aAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD,EAAA,UACG,EAAQ,KAAK,MAIZ,kBAAC,MAAD;IAEE,OAAM;IACN,WAAW,2GACT,EAAe,IAAI,EAAI,IAAI,GAAG,4BAA4B;cAG3D,EAAI;IACF,EAPE,EAAI,IAON,CACL,EACC,CAAA,EACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAK,KAAK,GAAK,MACd,kBAAC,MAAD;IAAY,WAAU;cACnB,EAAQ,KAAK,MACZ,kBAAC,MAAD;KAEE,WAAW,6DACT,EAAe,IAAI,EAAI,IAAI,GAAG,4BAA4B;eAG3D,EAAW,EAAI,EAAI,KAAK;KACtB,EANE,EAAI,IAMN,CACL;IACC,EAXI,EAWJ,CACL,EACI,CAAA,CACF;KACJ"}
1
+ {"version":3,"file":"TableEmbed.js","names":[],"sources":["../../../../../src/bigconsole/components/embed/widgets/TableEmbed.tsx"],"sourcesContent":["/** Read-only table embed renderer (BOFF-2986 / BOFF-5733). */\nimport type { EmbedWidgetRenderProps } from '../types';\nimport { asRecord, asArray, asString, formatCell, isNumeric } from './shared';\nimport { resolveCellLinkHref } from '../../widgets/table/cellLink';\n\ninterface Column {\n key: string;\n label: string;\n /** When 'link', the cell renders a navigable anchor (see cellLink). */\n format?: string;\n /** URL template for a `link` column; `{value}`/`{field}` tokens. */\n linkPattern?: string;\n}\n\n/** Hard client-side cap; the publication builder is expected to page/limit rows. */\nconst MAX_ROWS = 500;\n\nfunction readColumns(config: Record<string, unknown>, rows: Record<string, unknown>[]): Column[] {\n const configured = asArray(config.columns)\n .map((c) => asRecord(c))\n .map((c) => {\n // Mirror the live table's key/label fallbacks: a column may use `field`\n // instead of `key` and `header` instead of `label` (config-UI alias).\n const key = asString(c.key) || asString(c.field);\n return {\n key,\n label: asString(c.label ?? c.header ?? key),\n format: asString(c.format) || undefined,\n linkPattern: asString(c.linkPattern) || undefined,\n };\n })\n .filter((c) => c.key.length > 0);\n // A publisher that pinned columns chose exactly what to show — respect it.\n if (configured.length > 0) return configured;\n\n // Derive from the data when the publication didn't pin columns. Union the keys\n // across all rows (rows can be sparse), then drop noise so an embed never shows\n // a raw internal dump: `_`-prefixed technical keys and columns that are empty\n // in every row (the \"empty metadata/badge column\" gap from BOFF-5733).\n const seen = new Set<string>();\n const keys: string[] = [];\n for (const row of rows) {\n for (const key of Object.keys(row)) {\n if (!seen.has(key)) {\n seen.add(key);\n keys.push(key);\n }\n }\n }\n return keys\n .filter((key) => !key.startsWith('_'))\n .filter((key) => rows.some((row) => formatCell(row[key]) !== ''))\n .map((key) => ({ key, label: key }));\n}\n\nexport function TableEmbed({ widget }: EmbedWidgetRenderProps) {\n const config = asRecord(widget.config);\n const data = asRecord(widget.data);\n const rows = asArray(data.rows)\n .map((r) => asRecord(r))\n .slice(0, MAX_ROWS);\n const columns = readColumns(config, rows);\n\n if (columns.length === 0 || rows.length === 0) {\n return (\n <div className=\"flex h-full items-center justify-center bg-bg-surface p-4 text-sm text-text-secondary\">\n No data\n </div>\n );\n }\n\n // Right-align a column when its populated cells are all numeric (matches the\n // in-app TableCell), so currency/counts line up on the decimal edge.\n const numericColumns = new Set(\n columns\n .filter((col) => {\n const values = rows.map((r) => r[col.key]).filter((v) => v !== null && v !== undefined && v !== '');\n return values.length > 0 && values.every(isNumeric);\n })\n .map((col) => col.key)\n );\n\n return (\n <div className=\"h-full w-full overflow-auto bg-bg-surface\">\n {widget.title && (\n <div className=\"border-b border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary\">\n {widget.title}\n </div>\n )}\n <table className=\"w-full border-collapse text-sm\">\n <thead>\n <tr>\n {columns.map((col) => (\n // `sticky` lives on the cells (not the title, which scrolls away)\n // so column headers stay visible while scrolling the 500-row cap.\n // Cells carry their own opaque background so rows don't show through.\n <th\n key={col.key}\n scope=\"col\"\n className={`sticky top-0 z-10 border-b border-border-default bg-bg-sunken px-3 py-2 font-medium text-text-secondary ${\n numericColumns.has(col.key) ? 'text-right tabular-nums' : 'text-left'\n }`}\n >\n {col.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.map((row, i) => (\n <tr key={i} className=\"hover:bg-bg-sunken/50\">\n {columns.map((col) => {\n // Mirror the in-app TableCell: only `link` columns navigate.\n const href = col.format === 'link' ? resolveCellLinkHref(col.linkPattern, row[col.key], row) : null;\n return (\n <td\n key={col.key}\n className={`border-b border-border-subtle px-3 py-2 text-text-primary ${\n numericColumns.has(col.key) ? 'text-right tabular-nums' : 'text-left'\n }`}\n >\n {href ? (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n title={href}\n className=\"text-action-primary-bg hover:underline\"\n >\n {formatCell(row[col.key])}\n </a>\n ) : (\n formatCell(row[col.key])\n )}\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":";;;;AAeA,IAAM,IAAW;AAEjB,SAAS,EAAY,GAAiC,GAA2C;CAC/F,IAAM,IAAa,EAAQ,EAAO,QAAQ,CACvC,KAAK,MAAM,EAAS,EAAE,CAAC,CACvB,KAAK,MAAM;EAGV,IAAM,IAAM,EAAS,EAAE,IAAI,IAAI,EAAS,EAAE,MAAM;AAChD,SAAO;GACL;GACA,OAAO,EAAS,EAAE,SAAS,EAAE,UAAU,EAAI;GAC3C,QAAQ,EAAS,EAAE,OAAO,IAAI,KAAA;GAC9B,aAAa,EAAS,EAAE,YAAY,IAAI,KAAA;GACzC;GACD,CACD,QAAQ,MAAM,EAAE,IAAI,SAAS,EAAE;AAElC,KAAI,EAAW,SAAS,EAAG,QAAO;CAMlC,IAAM,oBAAO,IAAI,KAAa,EACxB,IAAiB,EAAE;AACzB,MAAK,IAAM,KAAO,EAChB,MAAK,IAAM,KAAO,OAAO,KAAK,EAAI,CAChC,CAAK,EAAK,IAAI,EAAI,KAChB,EAAK,IAAI,EAAI,EACb,EAAK,KAAK,EAAI;AAIpB,QAAO,EACJ,QAAQ,MAAQ,CAAC,EAAI,WAAW,IAAI,CAAC,CACrC,QAAQ,MAAQ,EAAK,MAAM,MAAQ,EAAW,EAAI,GAAK,KAAK,GAAG,CAAC,CAChE,KAAK,OAAS;EAAE;EAAK,OAAO;EAAK,EAAE;;AAGxC,SAAgB,EAAW,EAAE,aAAkC;CAC7D,IAAM,IAAS,EAAS,EAAO,OAAO,EAEhC,IAAO,EADA,EAAS,EAAO,KAAK,CACR,KAAK,CAC5B,KAAK,MAAM,EAAS,EAAE,CAAC,CACvB,MAAM,GAAG,EAAS,EACf,IAAU,EAAY,GAAQ,EAAK;AAEzC,KAAI,EAAQ,WAAW,KAAK,EAAK,WAAW,EAC1C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAwF;EAEjG,CAAA;CAMV,IAAM,IAAiB,IAAI,IACzB,EACG,QAAQ,MAAQ;EACf,IAAM,IAAS,EAAK,KAAK,MAAM,EAAE,EAAI,KAAK,CAAC,QAAQ,MAAM,KAAM,QAA2B,MAAM,GAAG;AACnG,SAAO,EAAO,SAAS,KAAK,EAAO,MAAM,EAAU;GACnD,CACD,KAAK,MAAQ,EAAI,IAAI,CACzB;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,EAAO,SACN,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAO;GACJ,CAAA,EAER,kBAAC,SAAD;GAAO,WAAU;aAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD,EAAA,UACG,EAAQ,KAAK,MAIZ,kBAAC,MAAD;IAEE,OAAM;IACN,WAAW,2GACT,EAAe,IAAI,EAAI,IAAI,GAAG,4BAA4B;cAG3D,EAAI;IACF,EAPE,EAAI,IAON,CACL,EACC,CAAA,EACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAK,KAAK,GAAK,MACd,kBAAC,MAAD;IAAY,WAAU;cACnB,EAAQ,KAAK,MAAQ;KAEpB,IAAM,IAAO,EAAI,WAAW,SAAS,EAAoB,EAAI,aAAa,EAAI,EAAI,MAAM,EAAI,GAAG;AAC/F,YACE,kBAAC,MAAD;MAEE,WAAW,6DACT,EAAe,IAAI,EAAI,IAAI,GAAG,4BAA4B;gBAG3D,IACC,kBAAC,KAAD;OACQ;OACN,QAAO;OACP,KAAI;OACJ,OAAO;OACP,WAAU;iBAET,EAAW,EAAI,EAAI,KAAK;OACvB,CAAA,GAEJ,EAAW,EAAI,EAAI,KAAK;MAEvB,EAlBE,EAAI,IAkBN;MAEP;IACC,EA3BI,EA2BJ,CACL,EACI,CAAA,CACF;KACJ"}
@@ -3,7 +3,7 @@ import { memo as t } from "react";
3
3
  import { MessageSquare as n } from "lucide-react";
4
4
  import { jsx as r, jsxs as i } from "react/jsx-runtime";
5
5
  //#region src/bigconsole/components/widgets/WidgetHeader.tsx
6
- var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isLoading: l, timeUntilRefresh: u, hasDrilldown: d, onRefresh: f, onExportCSV: p, onExportPNG: m, onDrilldown: h, onToggleComments: g, commentsOpen: _, commentCount: v = 0 }) {
6
+ var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isLoading: l, isSampleData: u, timeUntilRefresh: d, hasDrilldown: f, onRefresh: p, onExportCSV: m, onExportPNG: h, onDrilldown: g, onToggleComments: _, commentsOpen: v, commentCount: y = 0 }) {
7
7
  return /* @__PURE__ */ i("div", {
8
8
  className: `
9
9
  flex items-center justify-between
@@ -32,7 +32,12 @@ var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isL
32
32
  title: t.title,
33
33
  children: t.title
34
34
  }),
35
- d && /* @__PURE__ */ i("span", {
35
+ u && /* @__PURE__ */ r("span", {
36
+ className: "\n flex-shrink-0\n px-1.5 py-0.5\n rounded\n text-[10px] font-medium uppercase tracking-wide\n bg-status-info-bg-subtle text-status-info-text\n ",
37
+ title: "Example values from the template. Connect a data source to see your own data.",
38
+ children: "Sample"
39
+ }),
40
+ f && /* @__PURE__ */ i("span", {
36
41
  className: "\n flex-shrink-0 inline-flex items-center gap-0.5\n px-1.5 py-0.5 rounded-md\n bg-action-primary-bg/10 text-action-primary-text\n text-[10px] font-medium\n ",
37
42
  title: "Click data to drill down",
38
43
  children: [/* @__PURE__ */ r("svg", {
@@ -48,19 +53,19 @@ var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isL
48
53
  })
49
54
  }), "Drilldown"]
50
55
  }),
51
- u && !c && /* @__PURE__ */ r("span", {
56
+ d && !c && /* @__PURE__ */ r("span", {
52
57
  className: "\n flex-shrink-0 text-xs\n text-text-secondary\n tabular-nums\n ",
53
58
  title: "Time until next refresh",
54
- children: u
59
+ children: d
55
60
  })
56
61
  ]
57
62
  }), /* @__PURE__ */ i("div", {
58
63
  className: "flex items-center gap-1 ml-2",
59
64
  children: [
60
- h && !c && /* @__PURE__ */ r("button", {
65
+ g && !c && /* @__PURE__ */ r("button", {
61
66
  type: "button",
62
67
  onClick: (e) => {
63
- e.stopPropagation(), h();
68
+ e.stopPropagation(), g();
64
69
  },
65
70
  className: "\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n ",
66
71
  title: "Drill down to details",
@@ -77,10 +82,10 @@ var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isL
77
82
  })
78
83
  })
79
84
  }),
80
- f && /* @__PURE__ */ r("button", {
85
+ p && /* @__PURE__ */ r("button", {
81
86
  type: "button",
82
87
  onClick: (e) => {
83
- e.stopPropagation(), f();
88
+ e.stopPropagation(), p();
84
89
  },
85
90
  disabled: l,
86
91
  className: `
@@ -106,22 +111,22 @@ var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isL
106
111
  })
107
112
  })
108
113
  }),
109
- g && /* @__PURE__ */ i("button", {
114
+ _ && /* @__PURE__ */ i("button", {
110
115
  type: "button",
111
116
  onClick: (e) => {
112
- e.stopPropagation(), g();
117
+ e.stopPropagation(), _();
113
118
  },
114
119
  "aria-label": "Toggle comments",
115
- "aria-pressed": _,
120
+ "aria-pressed": v,
116
121
  className: `
117
122
  relative p-1.5 rounded-md
118
123
  transition-colors
119
- ${_ ? "text-action-primary-text bg-action-primary-bg/10" : "text-text-secondary hover:text-text-primary hover:bg-bg-sunken"}
124
+ ${v ? "text-action-primary-text bg-action-primary-bg/10" : "text-text-secondary hover:text-text-primary hover:bg-bg-sunken"}
120
125
  `,
121
126
  title: "Comments",
122
- children: [/* @__PURE__ */ r(n, { className: "w-4 h-4" }), v > 0 && /* @__PURE__ */ r("span", {
127
+ children: [/* @__PURE__ */ r(n, { className: "w-4 h-4" }), y > 0 && /* @__PURE__ */ r("span", {
123
128
  className: "absolute -top-0.5 -right-0.5 min-w-[14px] h-[14px] px-0.5 rounded-full bg-action-primary-bg text-action-primary-text text-[9px] font-semibold leading-[14px] text-center tabular-nums",
124
- children: v > 99 ? "99+" : v
129
+ children: y > 99 ? "99+" : y
125
130
  })]
126
131
  }),
127
132
  /* @__PURE__ */ r("div", {
@@ -129,9 +134,9 @@ var a = t(function({ widget: t, definition: a, isSelected: s, isEditMode: c, isL
129
134
  children: /* @__PURE__ */ r(e, {
130
135
  widget: t,
131
136
  isEditMode: c,
132
- onRefresh: f,
133
- onExportCSV: p,
134
- onExportPNG: m
137
+ onRefresh: p,
138
+ onExportCSV: m,
139
+ onExportPNG: h
135
140
  })
136
141
  })
137
142
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetHeader.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetHeader.tsx"],"sourcesContent":["/**\n * WidgetHeader Component\n *\n * Header section of a widget with title, icon, and action menu.\n */\n\nimport { type FC, type ReactElement, memo } from 'react';\nimport { MessageSquare } from 'lucide-react';\nimport type { Widget } from '../../types';\nimport type { WidgetDefinition } from './WidgetRegistry';\nimport { WidgetMenu } from './WidgetMenu';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetHeaderProps {\n /** The widget data */\n widget: Widget;\n /** Widget definition from registry */\n definition: WidgetDefinition;\n /** Whether the widget is selected */\n isSelected?: boolean;\n /** Whether in edit mode */\n isEditMode?: boolean;\n /** Whether the widget is loading */\n isLoading?: boolean;\n /** Time until next refresh (formatted string) */\n timeUntilRefresh?: string;\n /** Whether drilldown is enabled on this widget */\n hasDrilldown?: boolean;\n /** Callback to refresh widget */\n onRefresh?: () => void;\n /** Callback to export as CSV */\n onExportCSV?: () => void;\n /** Callback to export as PNG */\n onExportPNG?: () => void;\n /** Callback for drilldown */\n onDrilldown?: () => void;\n /** Toggle the widget comments panel */\n onToggleComments?: () => void;\n /** Whether the comments panel is currently open */\n commentsOpen?: boolean;\n /** Number of comments on this widget (badge) */\n commentCount?: number;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetHeader: FC<WidgetHeaderProps> = memo(function WidgetHeader({\n widget,\n definition,\n isSelected,\n isEditMode,\n isLoading,\n timeUntilRefresh,\n hasDrilldown,\n onRefresh,\n onExportCSV,\n onExportPNG,\n onDrilldown,\n onToggleComments,\n commentsOpen,\n commentCount = 0,\n}) {\n return (\n <div\n className={`\n flex items-center justify-between\n px-4 py-3\n border-b border-border-default\n bg-bg-sunken\n ${isSelected && isEditMode ? 'bg-action-primary-bg/5' : ''}\n `}\n >\n {/* Left side: Icon and Title - THIS IS THE DRAG HANDLE */}\n <div\n className={`\n widget-drag-handle\n flex items-center gap-2 min-w-0 flex-1\n ${isEditMode ? 'cursor-move' : ''}\n `}\n >\n {/* Widget type icon */}\n <div\n className={`\n flex-shrink-0 w-5 h-5\n text-text-secondary\n ${isLoading ? 'animate-pulse' : ''}\n `}\n >\n <WidgetIcon name={definition.icon} />\n </div>\n\n {/* Title */}\n <h3\n className=\"\n text-sm font-medium\n text-text-primary\n truncate\n \"\n title={widget.title}\n >\n {widget.title}\n </h3>\n\n {/* Drilldown indicator badge */}\n {hasDrilldown && (\n <span\n className=\"\n flex-shrink-0 inline-flex items-center gap-0.5\n px-1.5 py-0.5 rounded-md\n bg-action-primary-bg/10 text-action-primary-text\n text-[10px] font-medium\n \"\n title=\"Click data to drill down\"\n >\n <svg className=\"w-2.5 h-2.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2.5}\n d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\"\n />\n </svg>\n Drilldown\n </span>\n )}\n\n {/* Refresh indicator */}\n {timeUntilRefresh && !isEditMode && (\n <span\n className=\"\n flex-shrink-0 text-xs\n text-text-secondary\n tabular-nums\n \"\n title=\"Time until next refresh\"\n >\n {timeUntilRefresh}\n </span>\n )}\n </div>\n\n {/* Right side: Actions */}\n <div className=\"flex items-center gap-1 ml-2\">\n {/* Drilldown button (shown in view mode) */}\n {onDrilldown && !isEditMode && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDrilldown();\n }}\n className=\"\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n \"\n title=\"Drill down to details\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\"\n />\n </svg>\n </button>\n )}\n\n {/* Refresh button */}\n {onRefresh && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onRefresh();\n }}\n disabled={isLoading}\n className={`\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n disabled:opacity-50 disabled:cursor-not-allowed\n ${isLoading ? 'animate-spin' : ''}\n `}\n title=\"Refresh data\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n </button>\n )}\n\n {/* Comments toggle */}\n {onToggleComments && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleComments();\n }}\n aria-label=\"Toggle comments\"\n aria-pressed={commentsOpen}\n className={`\n relative p-1.5 rounded-md\n transition-colors\n ${\n commentsOpen\n ? 'text-action-primary-text bg-action-primary-bg/10'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-sunken'\n }\n `}\n title=\"Comments\"\n >\n <MessageSquare className=\"w-4 h-4\" />\n {commentCount > 0 && (\n <span className=\"absolute -top-0.5 -right-0.5 min-w-[14px] h-[14px] px-0.5 rounded-full bg-action-primary-bg text-action-primary-text text-[9px] font-semibold leading-[14px] text-center tabular-nums\">\n {commentCount > 99 ? '99+' : commentCount}\n </span>\n )}\n </button>\n )}\n\n {/* Menu (always visible) */}\n <div className=\"block\">\n <WidgetMenu\n widget={widget}\n isEditMode={isEditMode}\n onRefresh={onRefresh}\n onExportCSV={onExportCSV}\n onExportPNG={onExportPNG}\n />\n </div>\n </div>\n </div>\n );\n});\n\n// ============================================================================\n// Widget Icon Component\n// ============================================================================\n\ninterface WidgetIconProps {\n name: string;\n className?: string;\n}\n\nconst WidgetIcon: FC<WidgetIconProps> = ({ name, className = '' }) => {\n // Simple icon mapping - in production, use lucide-react or similar\n const icons: Record<string, ReactElement> = {\n TrendingUp: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 7h8m0 0v8m0-8l-8 8-4-4-6 6\" />\n </svg>\n ),\n BarChart3: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M18 20V10M12 20V4M6 20v-6\" />\n </svg>\n ),\n LineChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M7 20l4-16m2 16l4-16M6 9h14M4 15h14\" />\n </svg>\n ),\n Table: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n ),\n Gauge: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n ),\n Activity: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M22 12h-4l-3 9L9 3l-3 9H2\" />\n </svg>\n ),\n List: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 6h16M4 10h16M4 14h16M4 18h16\" />\n </svg>\n ),\n PieChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z\"\n />\n </svg>\n ),\n BarChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n ),\n AreaChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M7 12l3-3 3 3 4-4M8 21l4-4 4 4M3 4h18M4 4v16\"\n />\n </svg>\n ),\n };\n\n // Default icon\n const defaultIcon = (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n );\n\n return icons[name] || defaultIcon;\n};\n\nexport default WidgetHeader;\n"],"mappings":"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"}
1
+ {"version":3,"file":"WidgetHeader.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/WidgetHeader.tsx"],"sourcesContent":["/**\n * WidgetHeader Component\n *\n * Header section of a widget with title, icon, and action menu.\n */\n\nimport { type FC, type ReactElement, memo } from 'react';\nimport { MessageSquare } from 'lucide-react';\nimport type { Widget } from '../../types';\nimport type { WidgetDefinition } from './WidgetRegistry';\nimport { WidgetMenu } from './WidgetMenu';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface WidgetHeaderProps {\n /** The widget data */\n widget: Widget;\n /** Widget definition from registry */\n definition: WidgetDefinition;\n /** Whether the widget is selected */\n isSelected?: boolean;\n /** Whether in edit mode */\n isEditMode?: boolean;\n /**\n * The rendered values come from a template's embedded sample, not a\n * connected data source. Badged so nobody mistakes example numbers for\n * their own.\n */\n isSampleData?: boolean;\n /** Whether the widget is loading */\n isLoading?: boolean;\n /** Time until next refresh (formatted string) */\n timeUntilRefresh?: string;\n /** Whether drilldown is enabled on this widget */\n hasDrilldown?: boolean;\n /** Callback to refresh widget */\n onRefresh?: () => void;\n /** Callback to export as CSV */\n onExportCSV?: () => void;\n /** Callback to export as PNG */\n onExportPNG?: () => void;\n /** Callback for drilldown */\n onDrilldown?: () => void;\n /** Toggle the widget comments panel */\n onToggleComments?: () => void;\n /** Whether the comments panel is currently open */\n commentsOpen?: boolean;\n /** Number of comments on this widget (badge) */\n commentCount?: number;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const WidgetHeader: FC<WidgetHeaderProps> = memo(function WidgetHeader({\n widget,\n definition,\n isSelected,\n isEditMode,\n isLoading,\n isSampleData,\n timeUntilRefresh,\n hasDrilldown,\n onRefresh,\n onExportCSV,\n onExportPNG,\n onDrilldown,\n onToggleComments,\n commentsOpen,\n commentCount = 0,\n}) {\n return (\n <div\n className={`\n flex items-center justify-between\n px-4 py-3\n border-b border-border-default\n bg-bg-sunken\n ${isSelected && isEditMode ? 'bg-action-primary-bg/5' : ''}\n `}\n >\n {/* Left side: Icon and Title - THIS IS THE DRAG HANDLE */}\n <div\n className={`\n widget-drag-handle\n flex items-center gap-2 min-w-0 flex-1\n ${isEditMode ? 'cursor-move' : ''}\n `}\n >\n {/* Widget type icon */}\n <div\n className={`\n flex-shrink-0 w-5 h-5\n text-text-secondary\n ${isLoading ? 'animate-pulse' : ''}\n `}\n >\n <WidgetIcon name={definition.icon} />\n </div>\n\n {/* Title */}\n <h3\n className=\"\n text-sm font-medium\n text-text-primary\n truncate\n \"\n title={widget.title}\n >\n {widget.title}\n </h3>\n\n {/* Sample-data badge — example values, not the viewer's own data */}\n {isSampleData && (\n <span\n className=\"\n flex-shrink-0\n px-1.5 py-0.5\n rounded\n text-[10px] font-medium uppercase tracking-wide\n bg-status-info-bg-subtle text-status-info-text\n \"\n title=\"Example values from the template. Connect a data source to see your own data.\"\n >\n Sample\n </span>\n )}\n\n {/* Drilldown indicator badge */}\n {hasDrilldown && (\n <span\n className=\"\n flex-shrink-0 inline-flex items-center gap-0.5\n px-1.5 py-0.5 rounded-md\n bg-action-primary-bg/10 text-action-primary-text\n text-[10px] font-medium\n \"\n title=\"Click data to drill down\"\n >\n <svg className=\"w-2.5 h-2.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2.5}\n d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\"\n />\n </svg>\n Drilldown\n </span>\n )}\n\n {/* Refresh indicator */}\n {timeUntilRefresh && !isEditMode && (\n <span\n className=\"\n flex-shrink-0 text-xs\n text-text-secondary\n tabular-nums\n \"\n title=\"Time until next refresh\"\n >\n {timeUntilRefresh}\n </span>\n )}\n </div>\n\n {/* Right side: Actions */}\n <div className=\"flex items-center gap-1 ml-2\">\n {/* Drilldown button (shown in view mode) */}\n {onDrilldown && !isEditMode && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onDrilldown();\n }}\n className=\"\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n \"\n title=\"Drill down to details\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\"\n />\n </svg>\n </button>\n )}\n\n {/* Refresh button */}\n {onRefresh && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onRefresh();\n }}\n disabled={isLoading}\n className={`\n p-1.5 rounded-md\n text-text-secondary\n hover:text-text-primary\n hover:bg-bg-sunken\n transition-colors\n disabled:opacity-50 disabled:cursor-not-allowed\n ${isLoading ? 'animate-spin' : ''}\n `}\n title=\"Refresh data\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n </button>\n )}\n\n {/* Comments toggle */}\n {onToggleComments && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onToggleComments();\n }}\n aria-label=\"Toggle comments\"\n aria-pressed={commentsOpen}\n className={`\n relative p-1.5 rounded-md\n transition-colors\n ${\n commentsOpen\n ? 'text-action-primary-text bg-action-primary-bg/10'\n : 'text-text-secondary hover:text-text-primary hover:bg-bg-sunken'\n }\n `}\n title=\"Comments\"\n >\n <MessageSquare className=\"w-4 h-4\" />\n {commentCount > 0 && (\n <span className=\"absolute -top-0.5 -right-0.5 min-w-[14px] h-[14px] px-0.5 rounded-full bg-action-primary-bg text-action-primary-text text-[9px] font-semibold leading-[14px] text-center tabular-nums\">\n {commentCount > 99 ? '99+' : commentCount}\n </span>\n )}\n </button>\n )}\n\n {/* Menu (always visible) */}\n <div className=\"block\">\n <WidgetMenu\n widget={widget}\n isEditMode={isEditMode}\n onRefresh={onRefresh}\n onExportCSV={onExportCSV}\n onExportPNG={onExportPNG}\n />\n </div>\n </div>\n </div>\n );\n});\n\n// ============================================================================\n// Widget Icon Component\n// ============================================================================\n\ninterface WidgetIconProps {\n name: string;\n className?: string;\n}\n\nconst WidgetIcon: FC<WidgetIconProps> = ({ name, className = '' }) => {\n // Simple icon mapping - in production, use lucide-react or similar\n const icons: Record<string, ReactElement> = {\n TrendingUp: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 7h8m0 0v8m0-8l-8 8-4-4-6 6\" />\n </svg>\n ),\n BarChart3: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M18 20V10M12 20V4M6 20v-6\" />\n </svg>\n ),\n LineChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M7 20l4-16m2 16l4-16M6 9h14M4 15h14\" />\n </svg>\n ),\n Table: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n ),\n Gauge: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n ),\n Activity: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M22 12h-4l-3 9L9 3l-3 9H2\" />\n </svg>\n ),\n List: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 6h16M4 10h16M4 14h16M4 18h16\" />\n </svg>\n ),\n PieChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z\"\n />\n </svg>\n ),\n BarChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n ),\n AreaChart: (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M7 12l3-3 3 3 4-4M8 21l4-4 4 4M3 4h18M4 4v16\"\n />\n </svg>\n ),\n };\n\n // Default icon\n const defaultIcon = (\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" className={className}>\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n );\n\n return icons[name] || defaultIcon;\n};\n\nexport default WidgetHeader;\n"],"mappings":";;;;;AAyDA,IAAa,IAAsC,EAAK,SAAsB,EAC5E,WACA,eACA,eACA,eACA,cACA,iBACA,qBACA,iBACA,cACA,gBACA,gBACA,gBACA,qBACA,iBACA,kBAAe,KACd;AACD,QACE,kBAAC,OAAD;EACE,WAAW;;;;;UAKP,KAAc,IAAa,2BAA2B,GAAG;;YAN/D,CAUE,kBAAC,OAAD;GACE,WAAW;;;YAGP,IAAa,gBAAgB,GAAG;;aAJtC;IAQE,kBAAC,OAAD;KACE,WAAW;;;cAGP,IAAY,kBAAkB,GAAG;;eAGrC,kBAAC,GAAD,EAAY,MAAM,EAAW,MAAQ,CAAA;KACjC,CAAA;IAGN,kBAAC,MAAD;KACE,WAAU;KAKV,OAAO,EAAO;eAEb,EAAO;KACL,CAAA;IAGJ,KACC,kBAAC,QAAD;KACE,WAAU;KAOV,OAAM;eACP;KAEM,CAAA;IAIR,KACC,kBAAC,QAAD;KACE,WAAU;KAMV,OAAM;eAPR,CASE,kBAAC,OAAD;MAAK,WAAU;MAAc,MAAK;MAAO,QAAO;MAAe,SAAQ;gBACrE,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA,EAAA,YAED;;IAIR,KAAoB,CAAC,KACpB,kBAAC,QAAD;KACE,WAAU;KAKV,OAAM;eAEL;KACI,CAAA;IAEL;MAGN,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEG,KAAe,CAAC,KACf,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAa;;KAEf,WAAU;KAOV,OAAM;eAEN,kBAAC,OAAD;MAAK,WAAU;MAAU,MAAK;MAAO,QAAO;MAAe,SAAQ;gBACjE,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA;KACC,CAAA;IAIV,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAW;;KAEb,UAAU;KACV,WAAW;;;;;;;gBAOP,IAAY,iBAAiB,GAAG;;KAEpC,OAAM;eAEN,kBAAC,OAAD;MAAK,WAAU;MAAU,MAAK;MAAO,QAAO;MAAe,SAAQ;gBACjE,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA;KACC,CAAA;IAIV,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAkB;;KAEpB,cAAW;KACX,gBAAc;KACd,WAAW;;;gBAIP,IACI,qDACA,iEACL;;KAEH,OAAM;eAjBR,CAmBE,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA,EACpC,IAAe,KACd,kBAAC,QAAD;MAAM,WAAU;gBACb,IAAe,KAAK,QAAQ;MACxB,CAAA,CAEF;;IAIX,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACU;MACI;MACD;MACE;MACA;MACb,CAAA;KACE,CAAA;IACF;KACF;;EAER,EAWI,KAAmC,EAAE,SAAM,eAAY,SAAS;CAEpE,IAAM,IAAsC;EAC1C,YACE,kBAAC,OAAD;GAAK,MAAK;GAAO,QAAO;GAAe,SAAQ;GAAuB;aACpE,kBAAC,QAAD;IAAM,eAAc;IAAQ,gBAAe;IAAQ,aAAa;IAAG,GAAE;IAAmC,CAAA;GACpG,CAAA;EAER,WACE,kBAAC,OAAD;GAAK,MAAK;GAAO,QAAO;GAAe,SAAQ;GAAuB;aACpE,kBAAC,QAAD;IAAM,eAAc;IAAQ,gBAAe;IAAQ,aAAa;IAAG,GAAE;IAA8B,CAAA;GAC/F,CAAA;EAER,WACE,kBAAC,OAAD;GAAK,MAAK;GAAO,QAAO;GAAe,SAAQ;GAAuB;aACpE,kBAAC,QAAD;IAAM,eAAc;IAAQ,gBAAe;IAAQ,aAAa;IAAG,GAAE;IAAwC,CAAA;GACzG,CAAA;EAER,OACE,kBAAC,OAAD;GAAK,MAAK;GAAO,QAAO;GAAe,SAAQ;GAAuB;aACpE,kBAAC,QAAD;IACE,eAAc;IACd,gBAAe;IACf,aAAa;IACb,GAAE;IACF,CAAA;GACE,CAAA;EAER,OACE,kBAAC,OAAD;GAAK,MAAK;GAAO,QAAO;GAAe,SAAQ;GAAuB;aACpE,kBAAC,QAAD;IACE,eAAc;IACd,gBAAe;IACf,aAAa;IACb,GAAE;IACF,CAAA;GACE,CAAA;EAER,UACE,kBAAC,OAAD;GAAK,MAAK;GAAO,QAAO;GAAe,SAAQ;GAAuB;aACpE,kBAAC,QAAD;IAAM,eAAc;IAAQ,gBAAe;IAAQ,aAAa;IAAG,GAAE;IAA8B,CAAA;GAC/F,CAAA;EAER,MACE,kBAAC,OAAD;GAAK,MAAK;GAAO,QAAO;GAAe,SAAQ;GAAuB;aACpE,kBAAC,QAAD;IAAM,eAAc;IAAQ,gBAAe;IAAQ,aAAa;IAAG,GAAE;IAAoC,CAAA;GACrG,CAAA;EAER,UACE,kBAAC,OAAD;GAAK,MAAK;GAAO,QAAO;GAAe,SAAQ;GAAuB;aAAtE,CACE,kBAAC,QAAD;IACE,eAAc;IACd,gBAAe;IACf,aAAa;IACb,GAAE;IACF,CAAA,EACF,kBAAC,QAAD;IACE,eAAc;IACd,gBAAe;IACf,aAAa;IACb,GAAE;IACF,CAAA,CACE;;EAER,UACE,kBAAC,OAAD;GAAK,MAAK;GAAO,QAAO;GAAe,SAAQ;GAAuB;aACpE,kBAAC,QAAD;IACE,eAAc;IACd,gBAAe;IACf,aAAa;IACb,GAAE;IACF,CAAA;GACE,CAAA;EAER,WACE,kBAAC,OAAD;GAAK,MAAK;GAAO,QAAO;GAAe,SAAQ;GAAuB;aACpE,kBAAC,QAAD;IACE,eAAc;IACd,gBAAe;IACf,aAAa;IACb,GAAE;IACF,CAAA;GACE,CAAA;EAET,EAGK,IACJ,kBAAC,OAAD;EAAK,MAAK;EAAO,QAAO;EAAe,SAAQ;EAAuB;YACpE,kBAAC,QAAD;GACE,eAAc;GACd,gBAAe;GACf,aAAa;GACb,GAAE;GACF,CAAA;EACE,CAAA;AAGR,QAAO,EAAM,MAAS"}