@burdenoff/microfe-bigconsole 2026.925.1 → 2026.928.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 (56) hide show
  1. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +142 -145
  2. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js.map +1 -1
  3. package/dist/bigconsole/components/embed/EmbedWidgetRenderer.js +43 -13
  4. package/dist/bigconsole/components/embed/EmbedWidgetRenderer.js.map +1 -1
  5. package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js +5 -5
  6. package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js.map +1 -1
  7. package/dist/bigconsole/components/embed/renderer-manifest.js +14 -47
  8. package/dist/bigconsole/components/embed/renderer-manifest.js.map +1 -1
  9. package/dist/bigconsole/components/embed/types.js.map +1 -1
  10. package/dist/bigconsole/components/embed/widgetShim.js +28 -0
  11. package/dist/bigconsole/components/embed/widgetShim.js.map +1 -0
  12. package/dist/bigconsole/components/embed/widgets/CalendarEmbed.js +38 -0
  13. package/dist/bigconsole/components/embed/widgets/CalendarEmbed.js.map +1 -0
  14. package/dist/bigconsole/components/embed/widgets/ChartEmbed.js +225 -0
  15. package/dist/bigconsole/components/embed/widgets/ChartEmbed.js.map +1 -0
  16. package/dist/bigconsole/components/embed/widgets/EmbedFrame.js +18 -0
  17. package/dist/bigconsole/components/embed/widgets/EmbedFrame.js.map +1 -0
  18. package/dist/bigconsole/components/embed/widgets/FormEmbed.js +147 -0
  19. package/dist/bigconsole/components/embed/widgets/FormEmbed.js.map +1 -0
  20. package/dist/bigconsole/components/embed/widgets/GaugeEmbed.js +44 -0
  21. package/dist/bigconsole/components/embed/widgets/GaugeEmbed.js.map +1 -0
  22. package/dist/bigconsole/components/embed/widgets/HeatmapEmbed.js +35 -0
  23. package/dist/bigconsole/components/embed/widgets/HeatmapEmbed.js.map +1 -0
  24. package/dist/bigconsole/components/embed/widgets/KanbanEmbed.js +50 -0
  25. package/dist/bigconsole/components/embed/widgets/KanbanEmbed.js.map +1 -0
  26. package/dist/bigconsole/components/embed/widgets/PivotEmbed.js +146 -0
  27. package/dist/bigconsole/components/embed/widgets/PivotEmbed.js.map +1 -0
  28. package/dist/bigconsole/components/embed/widgets/ProgressEmbed.js +30 -0
  29. package/dist/bigconsole/components/embed/widgets/ProgressEmbed.js.map +1 -0
  30. package/dist/bigconsole/components/embed/widgets/RetentionEmbed.js +41 -0
  31. package/dist/bigconsole/components/embed/widgets/RetentionEmbed.js.map +1 -0
  32. package/dist/bigconsole/components/embed/widgets/TimelineEmbed.js +40 -0
  33. package/dist/bigconsole/components/embed/widgets/TimelineEmbed.js.map +1 -0
  34. package/dist/bigconsole/components/embed/widgets/shared.js +13 -1
  35. package/dist/bigconsole/components/embed/widgets/shared.js.map +1 -1
  36. package/dist/bigconsole/components/widgets/calendar/CalendarView.js +152 -0
  37. package/dist/bigconsole/components/widgets/calendar/CalendarView.js.map +1 -0
  38. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +10 -147
  39. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
  40. package/dist/bigconsole/components/widgets/chart/ChartWidget.js +19 -19
  41. package/dist/bigconsole/components/widgets/chart/index.js +8 -8
  42. package/dist/bigconsole/components/widgets/index.js +5 -5
  43. package/dist/bigconsole/components/widgets/kanban/KanbanView.js +192 -0
  44. package/dist/bigconsole/components/widgets/kanban/KanbanView.js.map +1 -0
  45. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +10 -187
  46. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
  47. package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +20 -20
  48. package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js +23 -22
  49. package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js.map +1 -1
  50. package/dist/bigconsole/components/widgets/utils/translate.js +9 -0
  51. package/dist/bigconsole/components/widgets/utils/translate.js.map +1 -0
  52. package/dist/bigconsole/hooks/useDashboardEmbedPolicy.js +2 -4
  53. package/dist/bigconsole/hooks/useDashboardEmbedPolicy.js.map +1 -1
  54. package/dist/generated/wspace-operations.js +0 -1
  55. package/dist/generated/wspace-operations.js.map +1 -1
  56. package/package.json +1 -1
@@ -0,0 +1,146 @@
1
+ import { asArray as e, asFiniteNumber as t, asOptionalString as n, asRecord as r, asRecordArray as i, displayValue as a, formatNumber as o } from "./shared.js";
2
+ import { EmbedFrame as s } from "./EmbedFrame.js";
3
+ import { useCallback as c, useMemo as l, useState as u } from "react";
4
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
5
+ //#region src/bigconsole/components/embed/widgets/PivotEmbed.tsx
6
+ var p = 50, m = 2e3;
7
+ function h(e, n, i) {
8
+ if (i) {
9
+ let n = t(e);
10
+ if (n !== null) return n;
11
+ }
12
+ return t(r(e)[n]);
13
+ }
14
+ function g({ widget: t }) {
15
+ let g = t.config, _ = l(() => ({
16
+ format: n(g.numberFormat, 20) ?? "number",
17
+ decimals: g.decimalPlaces
18
+ }), [g.numberFormat, g.decimalPlaces]), v = g.showRowTotals !== !1, y = g.showColumnTotals !== !1, b = g.showGrandTotal !== !1, x = g.expandable !== !1, S = l(() => {
19
+ let o = r(t.data), s = typeof o.columnField == "string" && o.columnField.length > 0 ? e(o.columnKeys).map(a).slice(0, p) : [], c = e(o.rowFields).map(a).filter(Boolean).slice(0, 10).join(" / "), l = i(o.valueFields, 10).map((e) => {
20
+ let t = a(e.key), r = n(e.aggregation, 20);
21
+ return {
22
+ key: t,
23
+ label: n(e.label, 80) ?? (r ? `${r}(${t})` : t)
24
+ };
25
+ }).filter((e) => e.key !== ""), u = i(o.nodes, m).map((t) => {
26
+ let n = e(t.path).map(a).slice(0, 10);
27
+ return {
28
+ path: n,
29
+ key: n.join("\0"),
30
+ byColumn: r(t.byColumn),
31
+ total: t.total
32
+ };
33
+ }).filter((e) => e.path.length > 0), d = /* @__PURE__ */ new Set();
34
+ for (let e of u) for (let t = 1; t < e.path.length; t++) d.add(e.path.slice(0, t).join("\0"));
35
+ return {
36
+ columnKeys: s,
37
+ rowHeader: c,
38
+ valueFields: l,
39
+ nodes: u,
40
+ parentKeys: d,
41
+ columnTotals: r(o.columnTotals),
42
+ grandTotal: o.grandTotal
43
+ };
44
+ }, [t.data]), [C, w] = u(/* @__PURE__ */ new Set()), T = c((e) => {
45
+ w((t) => {
46
+ let n = new Set(t);
47
+ return n.has(e) ? n.delete(e) : n.add(e), n;
48
+ });
49
+ }, []), { columnKeys: E, rowHeader: D, valueFields: O, nodes: k, parentKeys: A, columnTotals: j, grandTotal: M } = S;
50
+ if (k.length === 0 || O.length === 0) return /* @__PURE__ */ d(s, {
51
+ title: t.title,
52
+ children: /* @__PURE__ */ d("div", {
53
+ className: "flex h-full items-center justify-center p-4 text-sm text-text-tertiary",
54
+ children: "No data"
55
+ })
56
+ });
57
+ let N = O.length === 1, P = E.length > 0, F = (e) => e === null ? "" : o(e, _), I = (e) => e.path.some((t, n) => n < e.path.length - 1 && C.has(e.path.slice(0, n + 1).join("\0"))), L = k.filter((e) => !I(e)), R = "border-b border-border-subtle px-3 py-1.5 text-right tabular-nums text-text-primary", z = "border-b border-border-default bg-bg-sunken px-3 py-2 text-right text-xs font-medium text-text-secondary";
58
+ return /* @__PURE__ */ d(s, {
59
+ title: t.title,
60
+ children: /* @__PURE__ */ f("table", {
61
+ className: "w-full border-collapse text-sm",
62
+ children: [
63
+ /* @__PURE__ */ d("thead", {
64
+ className: "sticky top-0",
65
+ children: /* @__PURE__ */ f("tr", { children: [
66
+ /* @__PURE__ */ d("th", {
67
+ className: `${z} text-left`,
68
+ children: D
69
+ }),
70
+ P ? E.flatMap((e) => O.map((t) => /* @__PURE__ */ d("th", {
71
+ className: z,
72
+ children: N ? e : `${e} · ${t.label}`
73
+ }, `${e}|${t.key}`))) : O.map((e) => /* @__PURE__ */ d("th", {
74
+ className: z,
75
+ children: e.label
76
+ }, e.key)),
77
+ P && v && O.map((e) => /* @__PURE__ */ d("th", {
78
+ className: z,
79
+ children: N ? "Total" : `Total · ${e.label}`
80
+ }, `total|${e.key}`))
81
+ ] })
82
+ }),
83
+ /* @__PURE__ */ d("tbody", { children: L.map((e) => {
84
+ let t = e.path.length - 1, n = A.has(e.key), r = !C.has(e.key);
85
+ return /* @__PURE__ */ f("tr", {
86
+ className: n ? "bg-bg-sunken/40" : void 0,
87
+ children: [
88
+ /* @__PURE__ */ d("td", {
89
+ className: "border-b border-border-subtle px-3 py-1.5 text-left text-text-primary",
90
+ children: /* @__PURE__ */ f("span", {
91
+ className: "inline-flex items-center gap-1",
92
+ style: { paddingLeft: `${t * 16}px` },
93
+ children: [n && x ? /* @__PURE__ */ d("button", {
94
+ type: "button",
95
+ onClick: () => T(e.key),
96
+ "aria-expanded": r,
97
+ className: "w-4 text-text-tertiary hover:text-text-primary",
98
+ children: r ? "▾" : "▸"
99
+ }) : /* @__PURE__ */ d("span", { className: "w-4" }), /* @__PURE__ */ d("span", {
100
+ className: n ? "font-medium" : void 0,
101
+ children: e.path[e.path.length - 1]
102
+ })]
103
+ })
104
+ }),
105
+ P ? E.flatMap((t) => O.map((n) => /* @__PURE__ */ d("td", {
106
+ className: R,
107
+ children: F(h(e.byColumn[t], n.key, N))
108
+ }, `${t}|${n.key}`))) : O.map((t) => /* @__PURE__ */ d("td", {
109
+ className: R,
110
+ children: F(h(e.total, t.key, N))
111
+ }, t.key)),
112
+ P && v && O.map((t) => /* @__PURE__ */ d("td", {
113
+ className: `${R} font-medium`,
114
+ children: F(h(e.total, t.key, N))
115
+ }, `total|${t.key}`))
116
+ ]
117
+ }, e.key);
118
+ }) }),
119
+ (y || b) && /* @__PURE__ */ d("tfoot", { children: /* @__PURE__ */ f("tr", {
120
+ className: "bg-bg-sunken font-medium",
121
+ children: [
122
+ /* @__PURE__ */ d("td", {
123
+ className: "px-3 py-2 text-left text-text-primary",
124
+ children: "Total"
125
+ }),
126
+ P ? E.flatMap((e) => O.map((t) => /* @__PURE__ */ d("td", {
127
+ className: "px-3 py-2 text-right tabular-nums text-text-primary",
128
+ children: y ? F(h(j[e], t.key, N)) : ""
129
+ }, `${e}|${t.key}`))) : O.map((e) => /* @__PURE__ */ d("td", {
130
+ className: "px-3 py-2 text-right tabular-nums text-text-primary",
131
+ children: b ? F(h(M, e.key, N)) : ""
132
+ }, e.key)),
133
+ P && v && O.map((e) => /* @__PURE__ */ d("td", {
134
+ className: "px-3 py-2 text-right tabular-nums text-text-primary",
135
+ children: b ? F(h(M, e.key, N)) : ""
136
+ }, `total|${e.key}`))
137
+ ]
138
+ }) })
139
+ ]
140
+ })
141
+ });
142
+ }
143
+ //#endregion
144
+ export { g as PivotEmbed };
145
+
146
+ //# sourceMappingURL=PivotEmbed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PivotEmbed.js","names":[],"sources":["../../../../../src/bigconsole/components/embed/widgets/PivotEmbed.tsx"],"sourcesContent":["/**\n * Read-only pivot table embed renderer (BOFF-7926).\n *\n * The service aggregates SERVER-SIDE and sends only the aggregated tree — raw\n * rows never reach a public embed (the in-app pivot's field shelf lists every\n * key of the raw rows). Shape:\n *\n * {\n * rowFields: string[],\n * columnField: string | null,\n * columnKeys: string[], // ['Total'] when columnField is null\n * valueFields: [{ key, label?, aggregation? }],\n * nodes: [{ path: string[], byColumn?: {col: {vf: n}}, total?: {vf: n} | n }],\n * columnTotals?: {col: {vf: n}},\n * grandTotal?: {vf: n} | n,\n * }\n *\n * Config: showRowTotals, showColumnTotals, showGrandTotal, expandable,\n * numberFormat, decimalPlaces. Expand/collapse is local UI state only.\n */\nimport { useCallback, useMemo, useState } from 'react';\nimport type { EmbedWidgetRenderProps } from '../types';\nimport { EmbedFrame } from './EmbedFrame';\nimport {\n asArray,\n asFiniteNumber,\n asOptionalString,\n asRecord,\n asRecordArray,\n displayValue,\n formatNumber,\n} from './shared';\n\nconst MAX_COLUMNS = 50;\nconst MAX_NODES = 2000;\n\ninterface PivotValueField {\n key: string;\n label: string;\n}\ninterface PivotNode {\n path: string[];\n key: string;\n byColumn: Record<string, unknown>;\n total: unknown;\n}\n\n/** Read `obj[col][vf]` (or `obj[vf]` / a bare number when there is one value field). */\nfunction pick(source: unknown, vf: string, single: boolean): number | null {\n if (single) {\n const direct = asFiniteNumber(source);\n if (direct !== null) return direct;\n }\n return asFiniteNumber(asRecord(source)[vf]);\n}\n\nexport function PivotEmbed({ widget }: EmbedWidgetRenderProps) {\n const config = widget.config;\n const numberConfig = useMemo(\n () => ({ format: asOptionalString(config.numberFormat, 20) ?? 'number', decimals: config.decimalPlaces }),\n [config.numberFormat, config.decimalPlaces]\n );\n const showRowTotals = config.showRowTotals !== false;\n const showColumnTotals = config.showColumnTotals !== false;\n const showGrandTotal = config.showGrandTotal !== false;\n const expandable = config.expandable !== false;\n\n const model = useMemo(() => {\n const d = asRecord(widget.data);\n // No column field: the service still sends `columnKeys: ['Total']`; render\n // the value fields directly from each node's `total` instead.\n const hasColumnField = typeof d.columnField === 'string' && d.columnField.length > 0;\n const columnKeys = hasColumnField ? asArray(d.columnKeys).map(displayValue).slice(0, MAX_COLUMNS) : [];\n const rowHeader = asArray(d.rowFields).map(displayValue).filter(Boolean).slice(0, 10).join(' / ');\n const valueFields: PivotValueField[] = asRecordArray(d.valueFields, 10)\n .map((v) => {\n const key = displayValue(v.key);\n const agg = asOptionalString(v.aggregation, 20);\n const label = asOptionalString(v.label, 80) ?? (agg ? `${agg}(${key})` : key);\n return { key, label };\n })\n .filter((v) => v.key !== '');\n const nodes: PivotNode[] = asRecordArray(d.nodes, MAX_NODES)\n .map((n) => {\n const path = asArray(n.path).map(displayValue).slice(0, 10);\n return { path, key: path.join('\\u0000'), byColumn: asRecord(n.byColumn), total: n.total };\n })\n .filter((n) => n.path.length > 0);\n const parentKeys = new Set<string>();\n for (const n of nodes) {\n for (let i = 1; i < n.path.length; i++) parentKeys.add(n.path.slice(0, i).join('\\u0000'));\n }\n return {\n columnKeys,\n rowHeader,\n valueFields,\n nodes,\n parentKeys,\n columnTotals: asRecord(d.columnTotals),\n grandTotal: d.grandTotal,\n };\n }, [widget.data]);\n\n const [collapsed, setCollapsed] = useState<ReadonlySet<string>>(new Set());\n const toggle = useCallback((key: string) => {\n setCollapsed((prev) => {\n const next = new Set(prev);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n return next;\n });\n }, []);\n\n const { columnKeys, rowHeader, valueFields, nodes, parentKeys, columnTotals, grandTotal } = model;\n if (nodes.length === 0 || valueFields.length === 0) {\n return (\n <EmbedFrame title={widget.title}>\n <div className=\"flex h-full items-center justify-center p-4 text-sm text-text-tertiary\">No data</div>\n </EmbedFrame>\n );\n }\n\n const single = valueFields.length === 1;\n const hasColumns = columnKeys.length > 0;\n const fmt = (v: number | null) => (v === null ? '' : formatNumber(v, numberConfig));\n const isHidden = (n: PivotNode) =>\n n.path.some((_, i) => i < n.path.length - 1 && collapsed.has(n.path.slice(0, i + 1).join('\\u0000')));\n const visible = nodes.filter((n) => !isHidden(n));\n\n const cellClass = 'border-b border-border-subtle px-3 py-1.5 text-right tabular-nums text-text-primary';\n const headClass =\n 'border-b border-border-default bg-bg-sunken px-3 py-2 text-right text-xs font-medium text-text-secondary';\n\n return (\n <EmbedFrame title={widget.title}>\n <table className=\"w-full border-collapse text-sm\">\n <thead className=\"sticky top-0\">\n <tr>\n <th className={`${headClass} text-left`}>{rowHeader}</th>\n {hasColumns\n ? columnKeys.flatMap((col) =>\n valueFields.map((vf) => (\n <th key={`${col}|${vf.key}`} className={headClass}>\n {single ? col : `${col} · ${vf.label}`}\n </th>\n ))\n )\n : valueFields.map((vf) => (\n <th key={vf.key} className={headClass}>\n {vf.label}\n </th>\n ))}\n {hasColumns &&\n showRowTotals &&\n valueFields.map((vf) => (\n <th key={`total|${vf.key}`} className={headClass}>\n {single ? 'Total' : `Total · ${vf.label}`}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {visible.map((n) => {\n const depth = n.path.length - 1;\n const isParent = parentKeys.has(n.key);\n const open = !collapsed.has(n.key);\n return (\n <tr key={n.key} className={isParent ? 'bg-bg-sunken/40' : undefined}>\n <td className=\"border-b border-border-subtle px-3 py-1.5 text-left text-text-primary\">\n <span className=\"inline-flex items-center gap-1\" style={{ paddingLeft: `${depth * 16}px` }}>\n {isParent && expandable ? (\n <button\n type=\"button\"\n onClick={() => toggle(n.key)}\n aria-expanded={open}\n className=\"w-4 text-text-tertiary hover:text-text-primary\"\n >\n {open ? '▾' : '▸'}\n </button>\n ) : (\n <span className=\"w-4\" />\n )}\n <span className={isParent ? 'font-medium' : undefined}>{n.path[n.path.length - 1]}</span>\n </span>\n </td>\n {hasColumns\n ? columnKeys.flatMap((col) =>\n valueFields.map((vf) => (\n <td key={`${col}|${vf.key}`} className={cellClass}>\n {fmt(pick(n.byColumn[col], vf.key, single))}\n </td>\n ))\n )\n : valueFields.map((vf) => (\n <td key={vf.key} className={cellClass}>\n {fmt(pick(n.total, vf.key, single))}\n </td>\n ))}\n {hasColumns &&\n showRowTotals &&\n valueFields.map((vf) => (\n <td key={`total|${vf.key}`} className={`${cellClass} font-medium`}>\n {fmt(pick(n.total, vf.key, single))}\n </td>\n ))}\n </tr>\n );\n })}\n </tbody>\n {(showColumnTotals || showGrandTotal) && (\n <tfoot>\n <tr className=\"bg-bg-sunken font-medium\">\n <td className=\"px-3 py-2 text-left text-text-primary\">Total</td>\n {hasColumns\n ? columnKeys.flatMap((col) =>\n valueFields.map((vf) => (\n <td key={`${col}|${vf.key}`} className=\"px-3 py-2 text-right tabular-nums text-text-primary\">\n {showColumnTotals ? fmt(pick(columnTotals[col], vf.key, single)) : ''}\n </td>\n ))\n )\n : valueFields.map((vf) => (\n <td key={vf.key} className=\"px-3 py-2 text-right tabular-nums text-text-primary\">\n {showGrandTotal ? fmt(pick(grandTotal, vf.key, single)) : ''}\n </td>\n ))}\n {hasColumns &&\n showRowTotals &&\n valueFields.map((vf) => (\n <td key={`total|${vf.key}`} className=\"px-3 py-2 text-right tabular-nums text-text-primary\">\n {showGrandTotal ? fmt(pick(grandTotal, vf.key, single)) : ''}\n </td>\n ))}\n </tr>\n </tfoot>\n )}\n </table>\n </EmbedFrame>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { asFiniteNumber as e, asOptionalString as t, asRecord as n } from "./shared.js";
2
+ import { toWidgetShim as r } from "../widgetShim.js";
3
+ import { EmbedFrame as i } from "./EmbedFrame.js";
4
+ import a from "../../widgets/progress/ProgressWidget.js";
5
+ import { useMemo as o } from "react";
6
+ import { jsx as s } from "react/jsx-runtime";
7
+ //#region src/bigconsole/components/embed/widgets/ProgressEmbed.tsx
8
+ function c({ widget: c }) {
9
+ let l = o(() => r(c), [c]), u = o(() => {
10
+ let r = n(c.data), i = e(r.value);
11
+ if (i !== null) return {
12
+ value: i,
13
+ max: e(r.max) ?? void 0,
14
+ target: e(r.target) ?? void 0,
15
+ label: t(r.label, 120),
16
+ description: t(r.description, 300)
17
+ };
18
+ }, [c.data]);
19
+ return /* @__PURE__ */ s(i, {
20
+ title: c.title,
21
+ children: /* @__PURE__ */ s(a, {
22
+ widget: l,
23
+ data: u
24
+ })
25
+ });
26
+ }
27
+ //#endregion
28
+ export { c as ProgressEmbed };
29
+
30
+ //# sourceMappingURL=ProgressEmbed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProgressEmbed.js","names":[],"sources":["../../../../../src/bigconsole/components/embed/widgets/ProgressEmbed.tsx"],"sourcesContent":["/**\n * Read-only progress embed renderer (BOFF-7926).\n *\n * Reuses the in-app ProgressWidget. The service projects the final\n * `{ value, max?, target?, label?, description? }`.\n */\nimport { useMemo } from 'react';\nimport { ProgressWidget, type ProgressData } from '../../widgets/progress/ProgressWidget';\nimport type { EmbedWidgetRenderProps } from '../types';\nimport { toWidgetShim } from '../widgetShim';\nimport { EmbedFrame } from './EmbedFrame';\nimport { asFiniteNumber, asOptionalString, asRecord } from './shared';\n\nexport function ProgressEmbed({ widget }: EmbedWidgetRenderProps) {\n const shim = useMemo(() => toWidgetShim(widget), [widget]);\n const data = useMemo((): ProgressData | undefined => {\n const d = asRecord(widget.data);\n const value = asFiniteNumber(d.value);\n if (value === null) return undefined;\n return {\n value,\n max: asFiniteNumber(d.max) ?? undefined,\n target: asFiniteNumber(d.target) ?? undefined,\n label: asOptionalString(d.label, 120),\n description: asOptionalString(d.description, 300),\n };\n }, [widget.data]);\n return (\n <EmbedFrame title={widget.title}>\n <ProgressWidget widget={shim} data={data} />\n </EmbedFrame>\n );\n}\n"],"mappings":";;;;;;;AAaA,SAAgB,EAAc,EAAE,aAAkC;CAChE,IAAM,IAAO,QAAc,EAAa,EAAO,EAAE,CAAC,EAAO,CAAC,EACpD,IAAO,QAAwC;EACnD,IAAM,IAAI,EAAS,EAAO,KAAK,EACzB,IAAQ,EAAe,EAAE,MAAM;AACjC,YAAU,KACd,QAAO;GACL;GACA,KAAK,EAAe,EAAE,IAAI,IAAI,KAAA;GAC9B,QAAQ,EAAe,EAAE,OAAO,IAAI,KAAA;GACpC,OAAO,EAAiB,EAAE,OAAO,IAAI;GACrC,aAAa,EAAiB,EAAE,aAAa,IAAI;GAClD;IACA,CAAC,EAAO,KAAK,CAAC;AACjB,QACE,kBAAC,GAAD;EAAY,OAAO,EAAO;YACxB,kBAAC,GAAD;GAAgB,QAAQ;GAAY;GAAQ,CAAA;EACjC,CAAA"}
@@ -0,0 +1,41 @@
1
+ import { asArray as e, asFiniteNumber as t, asRecord as n, asRecordArray as r, displayValue as i } from "./shared.js";
2
+ import { toWidgetShim as a } from "../widgetShim.js";
3
+ import { EmbedFrame as o } from "./EmbedFrame.js";
4
+ import s from "../../widgets/retention/RetentionWidget.js";
5
+ import { useMemo as c } from "react";
6
+ import { jsx as l } from "react/jsx-runtime";
7
+ //#region src/bigconsole/components/embed/widgets/RetentionEmbed.tsx
8
+ var u = 100, d = 52;
9
+ function f(e) {
10
+ let n = [];
11
+ for (let r of e) {
12
+ let e = t(r);
13
+ if (e === null) break;
14
+ n.push(e);
15
+ }
16
+ return n;
17
+ }
18
+ function p({ widget: p }) {
19
+ let m = c(() => a(p), [p]), h = c(() => {
20
+ let a = n(p.data), o = e(a.periods).map(i).slice(0, d), s = r(a.cohorts, u).map((n) => ({
21
+ cohort: i(n.cohort),
22
+ initialSize: t(n.initialSize) ?? 0,
23
+ values: f(e(n.values).slice(0, d))
24
+ }));
25
+ return o.length > 0 && s.length > 0 ? {
26
+ periods: o,
27
+ cohorts: s
28
+ } : void 0;
29
+ }, [p.data]);
30
+ return /* @__PURE__ */ l(o, {
31
+ title: p.title,
32
+ children: /* @__PURE__ */ l(s, {
33
+ widget: m,
34
+ data: h
35
+ })
36
+ });
37
+ }
38
+ //#endregion
39
+ export { p as RetentionEmbed };
40
+
41
+ //# sourceMappingURL=RetentionEmbed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RetentionEmbed.js","names":[],"sources":["../../../../../src/bigconsole/components/embed/widgets/RetentionEmbed.tsx"],"sourcesContent":["/**\n * Read-only retention/cohort embed renderer (BOFF-7926).\n *\n * Reuses the in-app RetentionWidget. The service projects\n * `{ periods: string[], cohorts: [{ cohort, initialSize, values: (number | null)[] }] }`.\n */\nimport { useMemo } from 'react';\nimport { RetentionWidget, type RetentionData } from '../../widgets/retention/RetentionWidget';\nimport type { EmbedWidgetRenderProps } from '../types';\nimport { toWidgetShim } from '../widgetShim';\nimport { EmbedFrame } from './EmbedFrame';\nimport { asArray, asFiniteNumber, asRecord, asRecordArray, displayValue } from './shared';\n\nconst MAX_COHORTS = 100;\nconst MAX_PERIODS = 52;\n\n/**\n * Values up to the first missing period. The service sends `null` for periods a\n * cohort has not reached yet (always trailing); the in-app grid renders a\n * shorter row as blank cells, whereas a null cell would crash `toFixed`.\n */\nfunction leadingNumbers(values: unknown[]): number[] {\n const out: number[] = [];\n for (const v of values) {\n const n = asFiniteNumber(v);\n if (n === null) break;\n out.push(n);\n }\n return out;\n}\n\nexport function RetentionEmbed({ widget }: EmbedWidgetRenderProps) {\n const shim = useMemo(() => toWidgetShim(widget), [widget]);\n const data = useMemo((): RetentionData | undefined => {\n const d = asRecord(widget.data);\n const periods = asArray(d.periods).map(displayValue).slice(0, MAX_PERIODS);\n const cohorts = asRecordArray(d.cohorts, MAX_COHORTS).map((c) => ({\n cohort: displayValue(c.cohort),\n initialSize: asFiniteNumber(c.initialSize) ?? 0,\n values: leadingNumbers(asArray(c.values).slice(0, MAX_PERIODS)),\n }));\n return periods.length > 0 && cohorts.length > 0 ? { periods, cohorts } : undefined;\n }, [widget.data]);\n return (\n <EmbedFrame title={widget.title}>\n <RetentionWidget widget={shim} data={data} />\n </EmbedFrame>\n );\n}\n"],"mappings":";;;;;;;AAaA,IAAM,IAAc,KACd,IAAc;AAOpB,SAAS,EAAe,GAA6B;CACnD,IAAM,IAAgB,EAAE;AACxB,MAAK,IAAM,KAAK,GAAQ;EACtB,IAAM,IAAI,EAAe,EAAE;AAC3B,MAAI,MAAM,KAAM;AAChB,IAAI,KAAK,EAAE;;AAEb,QAAO;;AAGT,SAAgB,EAAe,EAAE,aAAkC;CACjE,IAAM,IAAO,QAAc,EAAa,EAAO,EAAE,CAAC,EAAO,CAAC,EACpD,IAAO,QAAyC;EACpD,IAAM,IAAI,EAAS,EAAO,KAAK,EACzB,IAAU,EAAQ,EAAE,QAAQ,CAAC,IAAI,EAAa,CAAC,MAAM,GAAG,EAAY,EACpE,IAAU,EAAc,EAAE,SAAS,EAAY,CAAC,KAAK,OAAO;GAChE,QAAQ,EAAa,EAAE,OAAO;GAC9B,aAAa,EAAe,EAAE,YAAY,IAAI;GAC9C,QAAQ,EAAe,EAAQ,EAAE,OAAO,CAAC,MAAM,GAAG,EAAY,CAAC;GAChE,EAAE;AACH,SAAO,EAAQ,SAAS,KAAK,EAAQ,SAAS,IAAI;GAAE;GAAS;GAAS,GAAG,KAAA;IACxE,CAAC,EAAO,KAAK,CAAC;AACjB,QACE,kBAAC,GAAD;EAAY,OAAO,EAAO;YACxB,kBAAC,GAAD;GAAiB,QAAQ;GAAY;GAAQ,CAAA;EAClC,CAAA"}
@@ -0,0 +1,40 @@
1
+ import { asFiniteNumber as e, asRecord as t, asRecordArray as n, displayValue as r, safeColor as i } from "./shared.js";
2
+ import { toWidgetShim as a } from "../widgetShim.js";
3
+ import { EmbedFrame as o } from "./EmbedFrame.js";
4
+ import s from "../../widgets/timeline/TimelineWidget.js";
5
+ import { useMemo as c } from "react";
6
+ import { jsx as l } from "react/jsx-runtime";
7
+ //#region src/bigconsole/components/embed/widgets/TimelineEmbed.tsx
8
+ var u = 1e3;
9
+ function d({ widget: d }) {
10
+ let f = c(() => a(d), [d]), p = c(() => {
11
+ let a = t(d.data), o = n(a.tasks, u).map((t, n) => ({
12
+ id: `t${n}`,
13
+ name: r(t.name),
14
+ start: r(t.start),
15
+ end: r(t.end),
16
+ progress: e(t.progress) ?? void 0,
17
+ color: i(t.color)
18
+ })).filter((e) => e.start !== "" && e.end !== ""), s = n(a.milestones, u).map((e, t) => ({
19
+ id: `m${t}`,
20
+ name: r(e.name),
21
+ date: r(e.date),
22
+ color: i(e.color)
23
+ })).filter((e) => e.date !== "");
24
+ return o.length > 0 || s.length > 0 ? {
25
+ tasks: o,
26
+ milestones: s
27
+ } : void 0;
28
+ }, [d.data]);
29
+ return /* @__PURE__ */ l(o, {
30
+ title: d.title,
31
+ children: /* @__PURE__ */ l(s, {
32
+ widget: f,
33
+ data: p
34
+ })
35
+ });
36
+ }
37
+ //#endregion
38
+ export { d as TimelineEmbed };
39
+
40
+ //# sourceMappingURL=TimelineEmbed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimelineEmbed.js","names":[],"sources":["../../../../../src/bigconsole/components/embed/widgets/TimelineEmbed.tsx"],"sourcesContent":["/**\n * Read-only timeline embed renderer (BOFF-7926).\n *\n * Reuses the in-app TimelineWidget. The service projects\n * `{ tasks: [{id, name, start, end, progress?, color?}], milestones: [{name, date, color?}] }`\n * (ids are positional indexes; dependencies are dropped). Colors are re-checked.\n */\nimport { useMemo } from 'react';\nimport { TimelineWidget, type TimelineData } from '../../widgets/timeline/TimelineWidget';\nimport type { EmbedWidgetRenderProps } from '../types';\nimport { toWidgetShim } from '../widgetShim';\nimport { EmbedFrame } from './EmbedFrame';\nimport { asFiniteNumber, asRecord, asRecordArray, displayValue, safeColor } from './shared';\n\nconst MAX_ITEMS = 1000;\n\nexport function TimelineEmbed({ widget }: EmbedWidgetRenderProps) {\n const shim = useMemo(() => toWidgetShim(widget), [widget]);\n const data = useMemo((): TimelineData | undefined => {\n const d = asRecord(widget.data);\n const tasks = asRecordArray(d.tasks, MAX_ITEMS)\n .map((t, i) => ({\n id: `t${i}`,\n name: displayValue(t.name),\n start: displayValue(t.start),\n end: displayValue(t.end),\n progress: asFiniteNumber(t.progress) ?? undefined,\n color: safeColor(t.color),\n }))\n .filter((t) => t.start !== '' && t.end !== '');\n const milestones = asRecordArray(d.milestones, MAX_ITEMS)\n .map((m, i) => ({\n id: `m${i}`,\n name: displayValue(m.name),\n date: displayValue(m.date),\n color: safeColor(m.color),\n }))\n .filter((m) => m.date !== '');\n return tasks.length > 0 || milestones.length > 0 ? { tasks, milestones } : undefined;\n }, [widget.data]);\n return (\n <EmbedFrame title={widget.title}>\n <TimelineWidget widget={shim} data={data} />\n </EmbedFrame>\n );\n}\n"],"mappings":";;;;;;;AAcA,IAAM,IAAY;AAElB,SAAgB,EAAc,EAAE,aAAkC;CAChE,IAAM,IAAO,QAAc,EAAa,EAAO,EAAE,CAAC,EAAO,CAAC,EACpD,IAAO,QAAwC;EACnD,IAAM,IAAI,EAAS,EAAO,KAAK,EACzB,IAAQ,EAAc,EAAE,OAAO,EAAU,CAC5C,KAAK,GAAG,OAAO;GACd,IAAI,IAAI;GACR,MAAM,EAAa,EAAE,KAAK;GAC1B,OAAO,EAAa,EAAE,MAAM;GAC5B,KAAK,EAAa,EAAE,IAAI;GACxB,UAAU,EAAe,EAAE,SAAS,IAAI,KAAA;GACxC,OAAO,EAAU,EAAE,MAAM;GAC1B,EAAE,CACF,QAAQ,MAAM,EAAE,UAAU,MAAM,EAAE,QAAQ,GAAG,EAC1C,IAAa,EAAc,EAAE,YAAY,EAAU,CACtD,KAAK,GAAG,OAAO;GACd,IAAI,IAAI;GACR,MAAM,EAAa,EAAE,KAAK;GAC1B,MAAM,EAAa,EAAE,KAAK;GAC1B,OAAO,EAAU,EAAE,MAAM;GAC1B,EAAE,CACF,QAAQ,MAAM,EAAE,SAAS,GAAG;AAC/B,SAAO,EAAM,SAAS,KAAK,EAAW,SAAS,IAAI;GAAE;GAAO;GAAY,GAAG,KAAA;IAC1E,CAAC,EAAO,KAAK,CAAC;AACjB,QACE,kBAAC,GAAD;EAAY,OAAO,EAAO;YACxB,kBAAC,GAAD;GAAgB,QAAQ;GAAY;GAAQ,CAAA;EACjC,CAAA"}
@@ -68,7 +68,19 @@ function l(e) {
68
68
  function u(e) {
69
69
  return typeof e == "number" && Number.isFinite(e);
70
70
  }
71
+ var d = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i, f = /^(?:rgb|rgba|hsl|hsla)\(\s*[0-9.%\s,/deg-]+\)$/i, p = /^var\(--[a-z0-9-]+\)$/i;
72
+ function m(e) {
73
+ if (typeof e != "string") return;
74
+ let t = e.trim();
75
+ if (!(t.length === 0 || t.length > 64)) return d.test(t) || f.test(t) || p.test(t) ? t : void 0;
76
+ }
77
+ function h(e, t = 500) {
78
+ return typeof e == "string" && e.length > 0 ? e.slice(0, t) : void 0;
79
+ }
80
+ function g(e, t = 5e3) {
81
+ return n(e).slice(0, t).filter((e) => !!e && typeof e == "object" && !Array.isArray(e));
82
+ }
71
83
  //#endregion
72
- export { n as asArray, t as asFiniteNumber, r as asRecord, e as asString, l as formatCell, a as formatNumber, u as isNumeric };
84
+ export { n as asArray, t as asFiniteNumber, h as asOptionalString, r as asRecord, g as asRecordArray, e as asString, i as displayValue, l as formatCell, a as formatNumber, u as isNumeric, m as safeColor };
73
85
 
74
86
  //# sourceMappingURL=shared.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","names":[],"sources":["../../../../../src/bigconsole/components/embed/widgets/shared.ts"],"sourcesContent":["/**\n * Defensive value coercion for embed widget renderers (BOFF-2986).\n *\n * The render model is sanitized by the publication builder, but the renderer\n * re-narrows every value it reads: an embedded widget is anonymous, untrusted\n * customer-facing surface, so we never assume a field is the type we expect.\n */\n\nexport function asString(value: unknown, fallback = ''): string {\n return typeof value === 'string' ? value : fallback;\n}\n\nexport function asFiniteNumber(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string' && value.trim() !== '') {\n const n = Number(value);\n if (Number.isFinite(n)) return n;\n }\n return null;\n}\n\nexport function asArray(value: unknown): unknown[] {\n return Array.isArray(value) ? value : [];\n}\n\nexport function asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {};\n}\n\n/**\n * Render an arbitrary cell value as a display string. Objects/arrays collapse to\n * an empty string rather than `[object Object]`, so no internal structure leaks.\n */\nexport function displayValue(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? String(value) : '';\n if (typeof value === 'boolean') return value ? 'true' : 'false';\n return '';\n}\n\n/**\n * Format a number with the widget's optional format hints, safely — mirrors the\n * in-app MetricCardWidget `formatValue` so embedded numbers read identically\n * (thousands separators, currency, percentage, compact notation) instead of a\n * bare `1284530.5`. `config.format` is the same enum the in-app widgets use.\n */\nexport function formatNumber(value: number, config: Record<string, unknown>): string {\n const format = asString(config.format, 'number');\n const decimals = asFiniteNumber(config.decimals);\n const prefix = asString(config.prefix);\n const suffix = asString(config.suffix);\n\n let formatted: string;\n switch (format) {\n case 'currency': {\n // `Intl.NumberFormat` throws `RangeError` on a currency code that isn't a\n // well-formed ISO 4217 string (e.g. '', 'US', 'ABCD'). This is untrusted\n // customer-facing config, so validate the shape and fall back to USD;\n // a defensive try/catch keeps a malformed value from crashing the widget.\n const currency = resolveCurrencyCode(config.currency);\n const maximumFractionDigits = decimals !== null ? clampDecimals(decimals) : 2;\n try {\n formatted = new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency,\n minimumFractionDigits: 0,\n maximumFractionDigits,\n }).format(value);\n } catch {\n formatted = new Intl.NumberFormat('en-US', { maximumFractionDigits }).format(value);\n }\n break;\n }\n case 'percentage': {\n const pctDecimals = decimals !== null ? clampDecimals(decimals) : 1;\n formatted = `${new Intl.NumberFormat('en-US', {\n minimumFractionDigits: pctDecimals,\n maximumFractionDigits: pctDecimals,\n }).format(value * 100)}%`;\n break;\n }\n case 'compact':\n formatted = new Intl.NumberFormat('en-US', {\n notation: 'compact',\n maximumFractionDigits: decimals !== null ? clampDecimals(decimals) : 1,\n }).format(value);\n break;\n case 'number':\n default:\n formatted = new Intl.NumberFormat('en-US', {\n maximumFractionDigits: decimals !== null ? clampDecimals(decimals) : 2,\n }).format(value);\n }\n\n // `currency` already carries its own symbol; only apply an explicit prefix to\n // the other formats so we never render e.g. \"$$1,284,530\".\n if (prefix && format !== 'currency') formatted = `${prefix}${formatted}`;\n if (suffix) formatted = `${formatted}${suffix}`;\n return formatted;\n}\n\nfunction clampDecimals(decimals: number): number {\n return Math.min(Math.max(Math.trunc(decimals), 0), 10);\n}\n\n/**\n * Narrow an untrusted `currency` config value to a well-formed ISO 4217 alpha\n * code (three letters), defaulting to USD. Prevents the `RangeError` that\n * `Intl.NumberFormat({ style: 'currency' })` throws on a malformed code.\n */\nfunction resolveCurrencyCode(value: unknown): string {\n const code = asString(value, 'USD').toUpperCase();\n return /^[A-Z]{3}$/.test(code) ? code : 'USD';\n}\n\n/**\n * Shared cell number formatter — constructed once (a table can render hundreds of\n * cells, and `new Intl.NumberFormat` per call is a measurable hot-path cost).\n *\n * `maximumFractionDigits: 20` (the API max) is LOSSLESS for JS doubles (~17\n * significant digits), so this only adds grouping separators — it never rounds\n * away precision. A blanket round (e.g. 4 dp) would corrupt customer-facing\n * high-precision ratios/coordinates/measurements (`0.0000123` → `0`), whereas\n * embed cells with no explicit column format must preserve the raw value.\n */\nconst CELL_NUMBER_FORMAT = new Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });\n\n/**\n * Format an arbitrary table cell: finite numbers get thousands separators\n * (matching the in-app TableCell) at full precision, everything else falls back\n * to the safe string coercion that never leaks internal object structure.\n */\nexport function formatCell(value: unknown): string {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return CELL_NUMBER_FORMAT.format(value);\n }\n return displayValue(value);\n}\n\n/** True when a value is a finite number — used for right-aligning numeric columns. */\nexport function isNumeric(value: unknown): boolean {\n return typeof value === 'number' && Number.isFinite(value);\n}\n"],"mappings":";AAQA,SAAgB,EAAS,GAAgB,IAAW,IAAY;AAC9D,QAAO,OAAO,KAAU,WAAW,IAAQ;;AAG7C,SAAgB,EAAe,GAA+B;AAC5D,KAAI,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,CAAE,QAAO;AAChE,KAAI,OAAO,KAAU,YAAY,EAAM,MAAM,KAAK,IAAI;EACpD,IAAM,IAAI,OAAO,EAAM;AACvB,MAAI,OAAO,SAAS,EAAE,CAAE,QAAO;;AAEjC,QAAO;;AAGT,SAAgB,EAAQ,GAA2B;AACjD,QAAO,MAAM,QAAQ,EAAM,GAAG,IAAQ,EAAE;;AAG1C,SAAgB,EAAS,GAAyC;AAChE,QAAO,KAAS,OAAO,KAAU,YAAY,CAAC,MAAM,QAAQ,EAAM,GAAI,IAAoC,EAAE;;AAO9G,SAAgB,EAAa,GAAwB;AAKnD,QAJI,KAAU,OAAoC,KAC9C,OAAO,KAAU,WAAiB,IAClC,OAAO,KAAU,WAAiB,OAAO,SAAS,EAAM,GAAG,OAAO,EAAM,GAAG,KAC3E,OAAO,KAAU,YAAkB,IAAQ,SAAS,UACjD;;AAST,SAAgB,EAAa,GAAe,GAAyC;CACnF,IAAM,IAAS,EAAS,EAAO,QAAQ,SAAS,EAC1C,IAAW,EAAe,EAAO,SAAS,EAC1C,IAAS,EAAS,EAAO,OAAO,EAChC,IAAS,EAAS,EAAO,OAAO,EAElC;AACJ,SAAQ,GAAR;EACE,KAAK,YAAY;GAKf,IAAM,IAAW,EAAoB,EAAO,SAAS,EAC/C,IAAwB,MAAa,OAAiC,IAA1B,EAAc,EAAS;AACzE,OAAI;AACF,QAAY,IAAI,KAAK,aAAa,SAAS;KACzC,OAAO;KACP;KACA,uBAAuB;KACvB;KACD,CAAC,CAAC,OAAO,EAAM;WACV;AACN,QAAY,IAAI,KAAK,aAAa,SAAS,EAAE,0BAAuB,CAAC,CAAC,OAAO,EAAM;;AAErF;;EAEF,KAAK,cAAc;GACjB,IAAM,IAAc,MAAa,OAAiC,IAA1B,EAAc,EAAS;AAC/D,OAAY,GAAG,IAAI,KAAK,aAAa,SAAS;IAC5C,uBAAuB;IACvB,uBAAuB;IACxB,CAAC,CAAC,OAAO,IAAQ,IAAI,CAAC;AACvB;;EAEF,KAAK;AACH,OAAY,IAAI,KAAK,aAAa,SAAS;IACzC,UAAU;IACV,uBAAuB,MAAa,OAAiC,IAA1B,EAAc,EAAS;IACnE,CAAC,CAAC,OAAO,EAAM;AAChB;EAEF,QACE,KAAY,IAAI,KAAK,aAAa,SAAS,EACzC,uBAAuB,MAAa,OAAiC,IAA1B,EAAc,EAAS,EACnE,CAAC,CAAC,OAAO,EAAM;;AAOpB,QAFI,KAAU,MAAW,eAAY,IAAY,GAAG,IAAS,MACzD,MAAQ,IAAY,GAAG,IAAY,MAChC;;AAGT,SAAS,EAAc,GAA0B;AAC/C,QAAO,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,EAAS,EAAE,EAAE,EAAE,GAAG;;AAQxD,SAAS,EAAoB,GAAwB;CACnD,IAAM,IAAO,EAAS,GAAO,MAAM,CAAC,aAAa;AACjD,QAAO,aAAa,KAAK,EAAK,GAAG,IAAO;;AAa1C,IAAM,IAAqB,IAAI,KAAK,aAAa,SAAS,EAAE,uBAAuB,IAAI,CAAC;AAOxF,SAAgB,EAAW,GAAwB;AAIjD,QAHI,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,GAC9C,EAAmB,OAAO,EAAM,GAElC,EAAa,EAAM;;AAI5B,SAAgB,EAAU,GAAyB;AACjD,QAAO,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM"}
1
+ {"version":3,"file":"shared.js","names":[],"sources":["../../../../../src/bigconsole/components/embed/widgets/shared.ts"],"sourcesContent":["/**\n * Defensive value coercion for embed widget renderers (BOFF-2986).\n *\n * The render model is sanitized by the publication builder, but the renderer\n * re-narrows every value it reads: an embedded widget is anonymous, untrusted\n * customer-facing surface, so we never assume a field is the type we expect.\n */\n\nexport function asString(value: unknown, fallback = ''): string {\n return typeof value === 'string' ? value : fallback;\n}\n\nexport function asFiniteNumber(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string' && value.trim() !== '') {\n const n = Number(value);\n if (Number.isFinite(n)) return n;\n }\n return null;\n}\n\nexport function asArray(value: unknown): unknown[] {\n return Array.isArray(value) ? value : [];\n}\n\nexport function asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {};\n}\n\n/**\n * Render an arbitrary cell value as a display string. Objects/arrays collapse to\n * an empty string rather than `[object Object]`, so no internal structure leaks.\n */\nexport function displayValue(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? String(value) : '';\n if (typeof value === 'boolean') return value ? 'true' : 'false';\n return '';\n}\n\n/**\n * Format a number with the widget's optional format hints, safely — mirrors the\n * in-app MetricCardWidget `formatValue` so embedded numbers read identically\n * (thousands separators, currency, percentage, compact notation) instead of a\n * bare `1284530.5`. `config.format` is the same enum the in-app widgets use.\n */\nexport function formatNumber(value: number, config: Record<string, unknown>): string {\n const format = asString(config.format, 'number');\n const decimals = asFiniteNumber(config.decimals);\n const prefix = asString(config.prefix);\n const suffix = asString(config.suffix);\n\n let formatted: string;\n switch (format) {\n case 'currency': {\n // `Intl.NumberFormat` throws `RangeError` on a currency code that isn't a\n // well-formed ISO 4217 string (e.g. '', 'US', 'ABCD'). This is untrusted\n // customer-facing config, so validate the shape and fall back to USD;\n // a defensive try/catch keeps a malformed value from crashing the widget.\n const currency = resolveCurrencyCode(config.currency);\n const maximumFractionDigits = decimals !== null ? clampDecimals(decimals) : 2;\n try {\n formatted = new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency,\n minimumFractionDigits: 0,\n maximumFractionDigits,\n }).format(value);\n } catch {\n formatted = new Intl.NumberFormat('en-US', { maximumFractionDigits }).format(value);\n }\n break;\n }\n case 'percentage': {\n const pctDecimals = decimals !== null ? clampDecimals(decimals) : 1;\n formatted = `${new Intl.NumberFormat('en-US', {\n minimumFractionDigits: pctDecimals,\n maximumFractionDigits: pctDecimals,\n }).format(value * 100)}%`;\n break;\n }\n case 'compact':\n formatted = new Intl.NumberFormat('en-US', {\n notation: 'compact',\n maximumFractionDigits: decimals !== null ? clampDecimals(decimals) : 1,\n }).format(value);\n break;\n case 'number':\n default:\n formatted = new Intl.NumberFormat('en-US', {\n maximumFractionDigits: decimals !== null ? clampDecimals(decimals) : 2,\n }).format(value);\n }\n\n // `currency` already carries its own symbol; only apply an explicit prefix to\n // the other formats so we never render e.g. \"$$1,284,530\".\n if (prefix && format !== 'currency') formatted = `${prefix}${formatted}`;\n if (suffix) formatted = `${formatted}${suffix}`;\n return formatted;\n}\n\nfunction clampDecimals(decimals: number): number {\n return Math.min(Math.max(Math.trunc(decimals), 0), 10);\n}\n\n/**\n * Narrow an untrusted `currency` config value to a well-formed ISO 4217 alpha\n * code (three letters), defaulting to USD. Prevents the `RangeError` that\n * `Intl.NumberFormat({ style: 'currency' })` throws on a malformed code.\n */\nfunction resolveCurrencyCode(value: unknown): string {\n const code = asString(value, 'USD').toUpperCase();\n return /^[A-Z]{3}$/.test(code) ? code : 'USD';\n}\n\n/**\n * Shared cell number formatter — constructed once (a table can render hundreds of\n * cells, and `new Intl.NumberFormat` per call is a measurable hot-path cost).\n *\n * `maximumFractionDigits: 20` (the API max) is LOSSLESS for JS doubles (~17\n * significant digits), so this only adds grouping separators — it never rounds\n * away precision. A blanket round (e.g. 4 dp) would corrupt customer-facing\n * high-precision ratios/coordinates/measurements (`0.0000123` → `0`), whereas\n * embed cells with no explicit column format must preserve the raw value.\n */\nconst CELL_NUMBER_FORMAT = new Intl.NumberFormat('en-US', { maximumFractionDigits: 20 });\n\n/**\n * Format an arbitrary table cell: finite numbers get thousands separators\n * (matching the in-app TableCell) at full precision, everything else falls back\n * to the safe string coercion that never leaks internal object structure.\n */\nexport function formatCell(value: unknown): string {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return CELL_NUMBER_FORMAT.format(value);\n }\n return displayValue(value);\n}\n\n/** True when a value is a finite number — used for right-aligning numeric columns. */\nexport function isNumeric(value: unknown): boolean {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\n/**\n * Allow only inert CSS color values (defense in depth — the service already runs\n * its own safeColor): hex, rgb/rgba/hsl/hsla with numeric arguments, or a\n * `var(--token)` reference. Anything else (`url(...)`, `expression(...)`, named\n * colors carrying odd payloads) is dropped so it can never reach an SVG `fill`\n * or a style attribute.\n */\nconst HEX_COLOR = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;\nconst FUNC_COLOR = /^(?:rgb|rgba|hsl|hsla)\\(\\s*[0-9.%\\s,/deg-]+\\)$/i;\nconst VAR_COLOR = /^var\\(--[a-z0-9-]+\\)$/i;\n\nexport function safeColor(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n const v = value.trim();\n if (v.length === 0 || v.length > 64) return undefined;\n return HEX_COLOR.test(v) || FUNC_COLOR.test(v) || VAR_COLOR.test(v) ? v : undefined;\n}\n\n/** A string, trimmed and length-capped, or undefined. */\nexport function asOptionalString(value: unknown, max = 500): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value.slice(0, max) : undefined;\n}\n\n/** Array of records only (drops scalars/arrays), capped. */\nexport function asRecordArray(value: unknown, max = 5000): Record<string, unknown>[] {\n return asArray(value)\n .slice(0, max)\n .filter((v): v is Record<string, unknown> => !!v && typeof v === 'object' && !Array.isArray(v));\n}\n"],"mappings":"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"}
@@ -0,0 +1,152 @@
1
+ import { memo as e, useMemo as t } from "react";
2
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
3
+ //#region src/bigconsole/components/widgets/calendar/CalendarView.tsx
4
+ function i(e, t) {
5
+ for (let n of t) if (e[n] !== void 0 && e[n] !== null) return e[n];
6
+ }
7
+ function a(e) {
8
+ let t = Array.isArray(e) ? e : Array.isArray(e?.events) ? e.events : Array.isArray(e?.items) ? e.items : Array.isArray(e?.data) ? e.data : void 0;
9
+ return Array.isArray(t) ? t.map((e, t) => {
10
+ if (!e || typeof e != "object") return null;
11
+ let n = e, r = i(n, [
12
+ "start",
13
+ "startDate",
14
+ "date",
15
+ "startTime"
16
+ ]);
17
+ if (!r) return null;
18
+ let a = i(n, [
19
+ "end",
20
+ "endDate",
21
+ "endTime"
22
+ ]) ?? r, o = i(n, [
23
+ "title",
24
+ "name",
25
+ "label",
26
+ "summary"
27
+ ]) ?? "Untitled event", s = i(n, [
28
+ "id",
29
+ "eventId",
30
+ "_id"
31
+ ]) ?? `${o}-${r}-${t}`;
32
+ return {
33
+ id: String(s),
34
+ title: String(o),
35
+ start: String(r),
36
+ end: String(a),
37
+ color: typeof n.color == "string" ? n.color : void 0,
38
+ allDay: typeof n.allDay == "boolean" ? n.allDay : void 0,
39
+ category: i(n, [
40
+ "category",
41
+ "group",
42
+ "type"
43
+ ])
44
+ };
45
+ }).filter((e) => e !== null) : [];
46
+ }
47
+ var o = e(function({ widget: e, data: i, onClick: o, t: s }) {
48
+ let c = t(() => a(i), [i]), l = i && !Array.isArray(i) && typeof i.eventCount == "number" ? i.eventCount : void 0, u = Math.max(l ?? 0, c.length), d = t(() => {
49
+ let e = /* @__PURE__ */ new Date(), t = new Date(e.getTime() + 10080 * 60 * 1e3);
50
+ return c.filter((n) => {
51
+ let r = new Date(n.start);
52
+ return r >= e && r <= t;
53
+ }).sort((e, t) => new Date(e.start).getTime() - new Date(t.start).getTime()).slice(0, 8);
54
+ }, [c]);
55
+ return c.length ? /* @__PURE__ */ r("div", {
56
+ className: "flex flex-col h-full p-4 overflow-auto",
57
+ children: [
58
+ /* @__PURE__ */ r("div", {
59
+ className: "flex items-center justify-between mb-4",
60
+ children: [/* @__PURE__ */ n("h3", {
61
+ className: "text-sm font-medium text-text-primary",
62
+ children: s("bigconsole.widget.calendar.upcomingEvents", "Upcoming Events")
63
+ }), /* @__PURE__ */ n("span", {
64
+ className: "text-xs text-text-secondary",
65
+ children: s("bigconsole.widget.calendar.eventCount", "{{count}} events", { count: u })
66
+ })]
67
+ }),
68
+ /* @__PURE__ */ n("div", {
69
+ className: "flex-1 space-y-2 overflow-auto",
70
+ children: d.map((e) => {
71
+ let t = new Date(e.start), i = t.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
72
+ return /* @__PURE__ */ n("div", {
73
+ className: `
74
+ p-3 rounded-lg border border-border-default
75
+ ${o ? "cursor-pointer hover:border-action-primary-bg" : ""}
76
+ transition-colors
77
+ `,
78
+ onClick: () => o?.(e),
79
+ children: /* @__PURE__ */ r("div", {
80
+ className: "flex items-start gap-3",
81
+ children: [/* @__PURE__ */ n("div", {
82
+ className: "w-1 h-full min-h-[40px] rounded-full flex-shrink-0",
83
+ style: { backgroundColor: e.color || "var(--color-action-primary-bg)" }
84
+ }), /* @__PURE__ */ r("div", {
85
+ className: "flex-1 min-w-0",
86
+ children: [
87
+ /* @__PURE__ */ n("p", {
88
+ className: "text-sm font-medium text-text-primary truncate",
89
+ children: e.title
90
+ }),
91
+ /* @__PURE__ */ r("p", {
92
+ className: "text-xs text-text-secondary mt-1",
93
+ children: [
94
+ i ? /* @__PURE__ */ n("span", {
95
+ className: "text-status-success-text font-medium",
96
+ children: s("bigconsole.widget.calendar.today", "Today")
97
+ }) : t.toLocaleDateString("en-US", {
98
+ weekday: "short",
99
+ month: "short",
100
+ day: "numeric"
101
+ }),
102
+ !e.allDay && /* @__PURE__ */ n("span", {
103
+ className: "ml-2",
104
+ children: t.toLocaleTimeString("en-US", {
105
+ hour: "numeric",
106
+ minute: "2-digit"
107
+ })
108
+ }),
109
+ e.allDay && /* @__PURE__ */ n("span", {
110
+ className: "ml-2",
111
+ children: s("bigconsole.widget.calendar.allDay", "All day")
112
+ })
113
+ ]
114
+ }),
115
+ e.category && /* @__PURE__ */ n("span", {
116
+ className: "inline-block mt-1 px-2 py-0.5 text-[10px] rounded bg-bg-sunken text-text-secondary",
117
+ children: e.category
118
+ })
119
+ ]
120
+ })]
121
+ })
122
+ }, e.id);
123
+ })
124
+ }),
125
+ u > d.length && /* @__PURE__ */ n("p", {
126
+ className: "text-xs text-text-muted text-center mt-3",
127
+ children: s("bigconsole.widget.calendar.moreEvents", "+{{count}} more events", { count: u - d.length })
128
+ })
129
+ ]
130
+ }) : /* @__PURE__ */ n("div", {
131
+ className: "flex items-center justify-center h-full text-text-secondary text-sm",
132
+ children: /* @__PURE__ */ r("div", {
133
+ className: "text-center",
134
+ children: [/* @__PURE__ */ n("svg", {
135
+ className: "w-12 h-12 mx-auto mb-2 text-text-muted",
136
+ fill: "none",
137
+ stroke: "currentColor",
138
+ viewBox: "0 0 24 24",
139
+ children: /* @__PURE__ */ n("path", {
140
+ strokeLinecap: "round",
141
+ strokeLinejoin: "round",
142
+ strokeWidth: 1.5,
143
+ d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
144
+ })
145
+ }), /* @__PURE__ */ n("p", { children: s("bigconsole.widget.calendar.emptyState", "No events to display") })]
146
+ })
147
+ });
148
+ });
149
+ //#endregion
150
+ export { o as default, a as normalizeCalendarEvents };
151
+
152
+ //# sourceMappingURL=CalendarView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CalendarView.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/calendar/CalendarView.tsx"],"sourcesContent":["/**\n * CalendarWidget Component\n *\n * Simple calendar view showing events.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport type { Widget } from '../../../types';\nimport type { WidgetTranslateFn } from '../utils/translate';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CalendarViewProps extends CalendarWidgetProps {\n /** Translator — the app passes useI18n().t; the public embed passes a static fallback translator. */\n t: WidgetTranslateFn;\n}\n\nexport interface CalendarWidgetProps {\n widget: Widget;\n data?: CalendarData;\n onClick?: (event: CalendarEvent) => void;\n}\n\nexport interface CalendarEvent {\n id: string;\n title: string;\n start: string;\n end: string;\n color?: string;\n allDay?: boolean;\n category?: string;\n}\n\nexport interface CalendarData {\n events?: CalendarEvent[];\n /** Total event count when `events` is a capped subset (public embeds). */\n eventCount?: number;\n currentDate?: string;\n view?: 'month' | 'week' | 'day';\n}\n\n// ============================================================================\n// Normalization\n// ============================================================================\n\nfunction firstDefined<T = unknown>(obj: Record<string, unknown>, keys: string[]): T | undefined {\n for (const key of keys) {\n if (obj[key] !== undefined && obj[key] !== null) return obj[key] as T;\n }\n return undefined;\n}\n\n/**\n * Normalize whatever the widget is bound to into `CalendarEvent[]`. Accepts the\n * documented `{ events, ... }` shape, an events array under a different common\n * key, or a bare array of event-like records — a DataSink parser naturally\n * outputs a bare array, and WidgetWrapper has no calendar-specific reshaping\n * step (unlike table/list/chart), so this widget has to tolerate it itself, the\n * same way MapWidget tolerates a bare marker array instead of requiring\n * `{ markers }`. Field names are matched against common aliases so a parser\n * doesn't have to emit the exact `title`/`start`/`end` keys.\n */\nexport function normalizeCalendarEvents(\n data: CalendarData | CalendarEvent[] | Record<string, unknown> | undefined\n): CalendarEvent[] {\n const rawEvents: unknown = Array.isArray(data)\n ? data\n : Array.isArray((data as CalendarData | undefined)?.events)\n ? (data as CalendarData).events\n : Array.isArray((data as Record<string, unknown> | undefined)?.items)\n ? (data as Record<string, unknown>).items\n : Array.isArray((data as Record<string, unknown> | undefined)?.data)\n ? (data as Record<string, unknown>).data\n : undefined;\n\n if (!Array.isArray(rawEvents)) return [];\n\n return rawEvents\n .map((raw, i): CalendarEvent | null => {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n const start = firstDefined<string>(r, ['start', 'startDate', 'date', 'startTime']);\n if (!start) return null;\n const end = firstDefined<string>(r, ['end', 'endDate', 'endTime']) ?? start;\n const title = firstDefined<string>(r, ['title', 'name', 'label', 'summary']) ?? 'Untitled event';\n const id = firstDefined<string>(r, ['id', 'eventId', '_id']) ?? `${title}-${start}-${i}`;\n return {\n id: String(id),\n title: String(title),\n start: String(start),\n end: String(end),\n color: typeof r.color === 'string' ? r.color : undefined,\n allDay: typeof r.allDay === 'boolean' ? r.allDay : undefined,\n category: firstDefined<string>(r, ['category', 'group', 'type']),\n };\n })\n .filter((e): e is CalendarEvent => e !== null);\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const CalendarView: FC<CalendarViewProps> = memo(function CalendarView({ widget: _widget, data, onClick, t }) {\n const events = useMemo(() => normalizeCalendarEvents(data), [data]);\n const declaredCount =\n data && !Array.isArray(data) && typeof (data as CalendarData).eventCount === 'number'\n ? (data as CalendarData).eventCount\n : undefined;\n const totalCount = Math.max(declaredCount ?? 0, events.length);\n\n // Get upcoming events (next 7 days)\n const upcomingEvents = useMemo(() => {\n const now = new Date();\n const weekFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);\n return events\n .filter((e) => {\n const eventDate = new Date(e.start);\n return eventDate >= now && eventDate <= weekFromNow;\n })\n .sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime())\n .slice(0, 8);\n }, [events]);\n\n if (!events.length) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n <div className=\"text-center\">\n <svg className=\"w-12 h-12 mx-auto mb-2 text-text-muted\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n <p>{t('bigconsole.widget.calendar.emptyState', 'No events to display')}</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 overflow-auto\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-sm font-medium text-text-primary\">\n {t('bigconsole.widget.calendar.upcomingEvents', 'Upcoming Events')}\n </h3>\n <span className=\"text-xs text-text-secondary\">\n {t('bigconsole.widget.calendar.eventCount', '{{count}} events', { count: totalCount })}\n </span>\n </div>\n\n {/* Event List */}\n <div className=\"flex-1 space-y-2 overflow-auto\">\n {upcomingEvents.map((event) => {\n const eventDate = new Date(event.start);\n const isToday = eventDate.toDateString() === new Date().toDateString();\n\n return (\n <div\n key={event.id}\n className={`\n p-3 rounded-lg border border-border-default\n ${onClick ? 'cursor-pointer hover:border-action-primary-bg' : ''}\n transition-colors\n `}\n onClick={() => onClick?.(event)}\n >\n <div className=\"flex items-start gap-3\">\n {/* Color indicator */}\n <div\n className=\"w-1 h-full min-h-[40px] rounded-full flex-shrink-0\"\n style={{ backgroundColor: event.color || 'var(--color-action-primary-bg)' }}\n />\n\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{event.title}</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {isToday ? (\n <span className=\"text-status-success-text font-medium\">\n {t('bigconsole.widget.calendar.today', 'Today')}\n </span>\n ) : (\n eventDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })\n )}\n {!event.allDay && (\n <span className=\"ml-2\">\n {eventDate.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}\n </span>\n )}\n {event.allDay && <span className=\"ml-2\">{t('bigconsole.widget.calendar.allDay', 'All day')}</span>}\n </p>\n {event.category && (\n <span className=\"inline-block mt-1 px-2 py-0.5 text-[10px] rounded bg-bg-sunken text-text-secondary\">\n {event.category}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {totalCount > upcomingEvents.length && (\n <p className=\"text-xs text-text-muted text-center mt-3\">\n {t('bigconsole.widget.calendar.moreEvents', '+{{count}} more events', {\n count: totalCount - upcomingEvents.length,\n })}\n </p>\n )}\n </div>\n );\n});\n\nexport default CalendarView;\n"],"mappings":"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"}