@open-mercato/ui 0.6.5-develop.5382.1.f542de69af → 0.6.5

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 (35) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/backend/AppShell.js +15 -8
  3. package/dist/backend/AppShell.js.map +2 -2
  4. package/dist/backend/DataTable.js +10 -9
  5. package/dist/backend/DataTable.js.map +2 -2
  6. package/dist/backend/charts/KpiCard.js +22 -14
  7. package/dist/backend/charts/KpiCard.js.map +2 -2
  8. package/dist/backend/charts/Sparkline.js +75 -0
  9. package/dist/backend/charts/Sparkline.js.map +7 -0
  10. package/dist/backend/charts/index.js +4 -1
  11. package/dist/backend/charts/index.js.map +2 -2
  12. package/dist/backend/dashboard/DashboardScreen.js +1 -2
  13. package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
  14. package/dist/backend/injection/InjectionSpot.js +23 -6
  15. package/dist/backend/injection/InjectionSpot.js.map +2 -2
  16. package/dist/primitives/avatar.js +2 -2
  17. package/dist/primitives/avatar.js.map +2 -2
  18. package/dist/primitives/pagination.js +3 -3
  19. package/dist/primitives/pagination.js.map +2 -2
  20. package/package.json +5 -6
  21. package/src/backend/AppShell.tsx +18 -8
  22. package/src/backend/DataTable.tsx +16 -9
  23. package/src/backend/__tests__/AppShell.test.tsx +61 -1
  24. package/src/backend/__tests__/DataTable.stickyResponsive.test.tsx +99 -0
  25. package/src/backend/charts/KpiCard.tsx +22 -12
  26. package/src/backend/charts/Sparkline.tsx +87 -0
  27. package/src/backend/charts/index.ts +2 -1
  28. package/src/backend/dashboard/DashboardScreen.tsx +1 -2
  29. package/src/backend/injection/InjectionSpot.tsx +27 -7
  30. package/src/backend/injection/__tests__/InjectionSpot.remount.test.tsx +140 -0
  31. package/src/primitives/__tests__/alert.test.tsx +16 -0
  32. package/src/primitives/__tests__/avatar.test.tsx +36 -1
  33. package/src/primitives/__tests__/pagination.test.tsx +31 -0
  34. package/src/primitives/avatar.tsx +8 -2
  35. package/src/primitives/pagination.tsx +3 -3
@@ -10,11 +10,12 @@ function defaultFormatValue(value) {
10
10
  }
11
11
  return value.toLocaleString(void 0, { maximumFractionDigits: 2 });
12
12
  }
13
- function formatPercentageChange(value) {
14
- const formatted = Math.abs(value).toFixed(1);
15
- return `${formatted}%`;
13
+ function formatPercentageChange(value, unit = "%") {
14
+ const abs = Math.abs(value);
15
+ const formatted = Number.isInteger(abs) ? String(abs) : abs.toFixed(1);
16
+ return `${formatted}${unit}`;
16
17
  }
17
- function BadgeDelta({ direction, value }) {
18
+ function BadgeDelta({ direction, value, unit = "%", className = "", title = "Compared to previous period" }) {
18
19
  const baseClasses = "inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium";
19
20
  const directionClasses = {
20
21
  up: "bg-status-success-bg text-status-success-text",
@@ -29,15 +30,16 @@ function BadgeDelta({ direction, value }) {
29
30
  return /* @__PURE__ */ jsxs(
30
31
  "span",
31
32
  {
32
- className: `${baseClasses} ${directionClasses[direction]}`,
33
- title: "Compared to previous period",
33
+ className: `${baseClasses} ${directionClasses[direction]}${className ? ` ${className}` : ""}`,
34
+ title,
34
35
  children: [
35
36
  icons[direction],
36
- formatPercentageChange(value)
37
+ formatPercentageChange(value, unit)
37
38
  ]
38
39
  }
39
40
  );
40
41
  }
42
+ const DeltaBadge = BadgeDelta;
41
43
  function KpiCard({
42
44
  title,
43
45
  value,
@@ -49,12 +51,14 @@ function KpiCard({
49
51
  prefix = "",
50
52
  suffix = "",
51
53
  className = "",
52
- headerAction
54
+ headerAction,
55
+ footer,
56
+ titleClassName
53
57
  }) {
54
58
  const hasWrapper = !!title;
55
59
  const wrapperClass = hasWrapper ? `rounded-lg border bg-card p-4 ${className}` : className;
56
60
  const headerRow = title || headerAction ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 mb-2", children: [
57
- title && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: title }),
61
+ title && /* @__PURE__ */ jsx("p", { className: titleClassName ?? "text-sm font-medium text-muted-foreground", children: title }),
58
62
  headerAction
59
63
  ] }) : null;
60
64
  if (error) {
@@ -78,18 +82,22 @@ function KpiCard({
78
82
  return /* @__PURE__ */ jsxs("div", { className: wrapperClass, children: [
79
83
  headerRow,
80
84
  /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-3", children: [
81
- /* @__PURE__ */ jsxs("p", { className: "text-2xl sm:text-3xl font-semibold tracking-tight text-card-foreground", children: [
82
- prefix,
83
- formatValue(value),
84
- suffix
85
+ /* @__PURE__ */ jsxs("p", { className: "flex items-baseline gap-1.5 text-2xl sm:text-3xl font-semibold tracking-tight text-card-foreground", children: [
86
+ /* @__PURE__ */ jsxs("span", { children: [
87
+ prefix,
88
+ formatValue(value)
89
+ ] }),
90
+ suffix ? /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-muted-foreground", children: suffix }) : null
85
91
  ] }),
86
92
  trend && /* @__PURE__ */ jsx(BadgeDelta, { direction: trend.direction, value: trend.value })
87
93
  ] }),
88
- trend && comparisonLabel && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: comparisonLabel })
94
+ trend && comparisonLabel && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: comparisonLabel }),
95
+ footer != null && /* @__PURE__ */ jsx("div", { className: "mt-3", children: footer })
89
96
  ] });
90
97
  }
91
98
  var KpiCard_default = KpiCard;
92
99
  export {
100
+ DeltaBadge,
93
101
  KpiCard,
94
102
  KpiCard_default as default
95
103
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/charts/KpiCard.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\n\nexport type KpiTrend = {\n value: number\n direction: 'up' | 'down' | 'unchanged'\n}\n\nexport type KpiCardProps = {\n title?: string\n value: number | null\n trend?: KpiTrend\n comparisonLabel?: string\n loading?: boolean\n error?: string | null\n formatValue?: (value: number) => string\n prefix?: string\n suffix?: string\n className?: string\n headerAction?: React.ReactNode\n}\n\nfunction defaultFormatValue(value: number): string {\n if (Math.abs(value) >= 1_000_000) {\n return `${(value / 1_000_000).toFixed(1)}M`\n }\n if (Math.abs(value) >= 1_000) {\n return `${(value / 1_000).toFixed(1)}K`\n }\n return value.toLocaleString(undefined, { maximumFractionDigits: 2 })\n}\n\nfunction formatPercentageChange(value: number): string {\n const formatted = Math.abs(value).toFixed(1)\n return `${formatted}%`\n}\n\ntype BadgeDeltaProps = {\n direction: 'up' | 'down' | 'unchanged'\n value: number\n}\n\nfunction BadgeDelta({ direction, value }: BadgeDeltaProps) {\n const baseClasses = 'inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium'\n\n const directionClasses = {\n up: 'bg-status-success-bg text-status-success-text',\n down: 'bg-status-error-bg text-status-error-text',\n unchanged: 'bg-status-neutral-bg text-status-neutral-text',\n }\n\n const icons = {\n up: (\n <svg className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 10l7-7m0 0l7 7m-7-7v18\" />\n </svg>\n ),\n down: (\n <svg className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M19 14l-7 7m0 0l-7-7m7 7V3\" />\n </svg>\n ),\n unchanged: (\n <svg className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 12h14\" />\n </svg>\n ),\n }\n\n return (\n <span\n className={`${baseClasses} ${directionClasses[direction]}`}\n title=\"Compared to previous period\"\n >\n {icons[direction]}\n {formatPercentageChange(value)}\n </span>\n )\n}\n\nexport function KpiCard({\n title,\n value,\n trend,\n comparisonLabel,\n loading,\n error,\n formatValue = defaultFormatValue,\n prefix = '',\n suffix = '',\n className = '',\n headerAction,\n}: KpiCardProps) {\n const hasWrapper = !!title\n const wrapperClass = hasWrapper ? `rounded-lg border bg-card p-4 ${className}` : className\n\n const headerRow = (title || headerAction) ? (\n <div className=\"flex items-center justify-between gap-2 mb-2\">\n {title && <p className=\"text-sm font-medium text-muted-foreground\">{title}</p>}\n {headerAction}\n </div>\n ) : null\n\n if (error) {\n return (\n <div className={wrapperClass}>\n {headerRow}\n <p className=\"text-sm text-destructive\">{error}</p>\n </div>\n )\n }\n\n if (loading) {\n return (\n <div className={wrapperClass}>\n {headerRow}\n <div className=\"flex items-center justify-center py-4\">\n <Spinner className=\"h-6 w-6 text-muted-foreground\" />\n </div>\n </div>\n )\n }\n\n if (value === null) {\n return (\n <div className={wrapperClass}>\n {headerRow}\n <p className=\"text-2xl sm:text-3xl font-semibold tracking-tight text-card-foreground\">--</p>\n </div>\n )\n }\n\n return (\n <div className={wrapperClass}>\n {headerRow}\n <div className=\"flex items-baseline gap-3\">\n <p className=\"text-2xl sm:text-3xl font-semibold tracking-tight text-card-foreground\">\n {prefix}\n {formatValue(value)}\n {suffix}\n </p>\n {trend && (\n <BadgeDelta direction={trend.direction} value={trend.value} />\n )}\n </div>\n {trend && comparisonLabel && (\n <p className=\"mt-1 text-xs text-muted-foreground\">{comparisonLabel}</p>\n )}\n </div>\n )\n}\n\nexport default KpiCard\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\n\nexport type KpiTrend = {\n value: number\n direction: 'up' | 'down' | 'unchanged'\n}\n\nexport type KpiCardProps = {\n title?: string\n value: number | null\n trend?: KpiTrend\n comparisonLabel?: string\n loading?: boolean\n error?: string | null\n formatValue?: (value: number) => string\n prefix?: string\n suffix?: string\n className?: string\n headerAction?: React.ReactNode\n footer?: React.ReactNode\n titleClassName?: string\n}\n\nfunction defaultFormatValue(value: number): string {\n if (Math.abs(value) >= 1_000_000) {\n return `${(value / 1_000_000).toFixed(1)}M`\n }\n if (Math.abs(value) >= 1_000) {\n return `${(value / 1_000).toFixed(1)}K`\n }\n return value.toLocaleString(undefined, { maximumFractionDigits: 2 })\n}\n\nfunction formatPercentageChange(value: number, unit: string = '%'): string {\n const abs = Math.abs(value)\n const formatted = Number.isInteger(abs) ? String(abs) : abs.toFixed(1)\n return `${formatted}${unit}`\n}\n\ntype BadgeDeltaProps = {\n direction: 'up' | 'down' | 'unchanged'\n value: number\n unit?: string\n className?: string\n title?: string\n}\n\nfunction BadgeDelta({ direction, value, unit = '%', className = '', title = 'Compared to previous period' }: BadgeDeltaProps) {\n const baseClasses = 'inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium'\n\n const directionClasses = {\n up: 'bg-status-success-bg text-status-success-text',\n down: 'bg-status-error-bg text-status-error-text',\n unchanged: 'bg-status-neutral-bg text-status-neutral-text',\n }\n\n const icons = {\n up: (\n <svg className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 10l7-7m0 0l7 7m-7-7v18\" />\n </svg>\n ),\n down: (\n <svg className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M19 14l-7 7m0 0l-7-7m7 7V3\" />\n </svg>\n ),\n unchanged: (\n <svg className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 12h14\" />\n </svg>\n ),\n }\n\n return (\n <span\n className={`${baseClasses} ${directionClasses[direction]}${className ? ` ${className}` : ''}`}\n title={title}\n >\n {icons[direction]}\n {formatPercentageChange(value, unit)}\n </span>\n )\n}\n\nexport const DeltaBadge = BadgeDelta\n\nexport function KpiCard({\n title,\n value,\n trend,\n comparisonLabel,\n loading,\n error,\n formatValue = defaultFormatValue,\n prefix = '',\n suffix = '',\n className = '',\n headerAction,\n footer,\n titleClassName,\n}: KpiCardProps) {\n const hasWrapper = !!title\n const wrapperClass = hasWrapper ? `rounded-lg border bg-card p-4 ${className}` : className\n\n const headerRow = (title || headerAction) ? (\n <div className=\"flex items-center justify-between gap-2 mb-2\">\n {title && <p className={titleClassName ?? 'text-sm font-medium text-muted-foreground'}>{title}</p>}\n {headerAction}\n </div>\n ) : null\n\n if (error) {\n return (\n <div className={wrapperClass}>\n {headerRow}\n <p className=\"text-sm text-destructive\">{error}</p>\n </div>\n )\n }\n\n if (loading) {\n return (\n <div className={wrapperClass}>\n {headerRow}\n <div className=\"flex items-center justify-center py-4\">\n <Spinner className=\"h-6 w-6 text-muted-foreground\" />\n </div>\n </div>\n )\n }\n\n if (value === null) {\n return (\n <div className={wrapperClass}>\n {headerRow}\n <p className=\"text-2xl sm:text-3xl font-semibold tracking-tight text-card-foreground\">--</p>\n </div>\n )\n }\n\n return (\n <div className={wrapperClass}>\n {headerRow}\n <div className=\"flex items-baseline gap-3\">\n <p className=\"flex items-baseline gap-1.5 text-2xl sm:text-3xl font-semibold tracking-tight text-card-foreground\">\n <span>{prefix}{formatValue(value)}</span>\n {suffix ? <span className=\"text-sm font-medium text-muted-foreground\">{suffix}</span> : null}\n </p>\n {trend && (\n <BadgeDelta direction={trend.direction} value={trend.value} />\n )}\n </div>\n {trend && comparisonLabel && (\n <p className=\"mt-1 text-xs text-muted-foreground\">{comparisonLabel}</p>\n )}\n {footer != null && <div className=\"mt-3\">{footer}</div>}\n </div>\n )\n}\n\nexport default KpiCard\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,75 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ function buildPoints(values, width, height) {
4
+ const count = values.length;
5
+ const padding = 2;
6
+ const usableHeight = Math.max(height - padding * 2, 0);
7
+ if (count === 1) {
8
+ return [{ x: width / 2, y: height / 2 }];
9
+ }
10
+ let min = values[0];
11
+ let max = values[0];
12
+ for (const value of values) {
13
+ if (value < min) min = value;
14
+ if (value > max) max = value;
15
+ }
16
+ const range = max - min;
17
+ const stepX = count > 1 ? width / (count - 1) : 0;
18
+ return values.map((value, index) => {
19
+ const x = stepX * index;
20
+ const ratio = range === 0 ? 0.5 : (value - min) / range;
21
+ const y = padding + (1 - ratio) * usableHeight;
22
+ return { x, y };
23
+ });
24
+ }
25
+ function toPath(points) {
26
+ return points.map((point, index) => `${index === 0 ? "M" : "L"} ${point.x.toFixed(2)} ${point.y.toFixed(2)}`).join(" ");
27
+ }
28
+ function Sparkline({
29
+ values,
30
+ ariaLabel,
31
+ className = "",
32
+ width = 96,
33
+ height = 28
34
+ }) {
35
+ if (!values || values.length === 0) {
36
+ return null;
37
+ }
38
+ const points = buildPoints(values, width, height);
39
+ const linePath = toPath(points);
40
+ const firstPoint = points[0];
41
+ const lastPoint = points[points.length - 1];
42
+ const areaPath = `${linePath} L ${lastPoint.x.toFixed(2)} ${height} L ${firstPoint.x.toFixed(2)} ${height} Z`;
43
+ return /* @__PURE__ */ jsxs(
44
+ "svg",
45
+ {
46
+ role: "img",
47
+ "aria-label": ariaLabel,
48
+ width,
49
+ height,
50
+ viewBox: `0 0 ${width} ${height}`,
51
+ preserveAspectRatio: "none",
52
+ className,
53
+ children: [
54
+ /* @__PURE__ */ jsx("path", { d: areaPath, fill: "currentColor", fillOpacity: 0.12, stroke: "none" }),
55
+ /* @__PURE__ */ jsx(
56
+ "path",
57
+ {
58
+ d: linePath,
59
+ fill: "none",
60
+ stroke: "currentColor",
61
+ strokeWidth: 1.5,
62
+ strokeLinecap: "round",
63
+ strokeLinejoin: "round"
64
+ }
65
+ )
66
+ ]
67
+ }
68
+ );
69
+ }
70
+ var Sparkline_default = Sparkline;
71
+ export {
72
+ Sparkline,
73
+ Sparkline_default as default
74
+ };
75
+ //# sourceMappingURL=Sparkline.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/backend/charts/Sparkline.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\n\nexport type SparklineProps = {\n values: number[]\n ariaLabel: string\n className?: string\n width?: number\n height?: number\n}\n\nfunction buildPoints(values: number[], width: number, height: number): Array<{ x: number; y: number }> {\n const count = values.length\n const padding = 2\n const usableHeight = Math.max(height - padding * 2, 0)\n\n if (count === 1) {\n return [{ x: width / 2, y: height / 2 }]\n }\n\n let min = values[0]\n let max = values[0]\n for (const value of values) {\n if (value < min) min = value\n if (value > max) max = value\n }\n const range = max - min\n\n const stepX = count > 1 ? width / (count - 1) : 0\n\n return values.map((value, index) => {\n const x = stepX * index\n const ratio = range === 0 ? 0.5 : (value - min) / range\n const y = padding + (1 - ratio) * usableHeight\n return { x, y }\n })\n}\n\nfunction toPath(points: Array<{ x: number; y: number }>): string {\n return points\n .map((point, index) => `${index === 0 ? 'M' : 'L'} ${point.x.toFixed(2)} ${point.y.toFixed(2)}`)\n .join(' ')\n}\n\nexport function Sparkline({\n values,\n ariaLabel,\n className = '',\n width = 96,\n height = 28,\n}: SparklineProps) {\n if (!values || values.length === 0) {\n return null\n }\n\n const points = buildPoints(values, width, height)\n const linePath = toPath(points)\n\n const firstPoint = points[0]\n const lastPoint = points[points.length - 1]\n const areaPath = `${linePath} L ${lastPoint.x.toFixed(2)} ${height} L ${firstPoint.x.toFixed(2)} ${height} Z`\n\n return (\n <svg\n role=\"img\"\n aria-label={ariaLabel}\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n preserveAspectRatio=\"none\"\n className={className}\n >\n <path d={areaPath} fill=\"currentColor\" fillOpacity={0.12} stroke=\"none\" />\n <path\n d={linePath}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )\n}\n\nexport default Sparkline\n"],
5
+ "mappings": ";AAgEI,SASE,KATF;AApDJ,SAAS,YAAY,QAAkB,OAAe,QAAiD;AACrG,QAAM,QAAQ,OAAO;AACrB,QAAM,UAAU;AAChB,QAAM,eAAe,KAAK,IAAI,SAAS,UAAU,GAAG,CAAC;AAErD,MAAI,UAAU,GAAG;AACf,WAAO,CAAC,EAAE,GAAG,QAAQ,GAAG,GAAG,SAAS,EAAE,CAAC;AAAA,EACzC;AAEA,MAAI,MAAM,OAAO,CAAC;AAClB,MAAI,MAAM,OAAO,CAAC;AAClB,aAAW,SAAS,QAAQ;AAC1B,QAAI,QAAQ,IAAK,OAAM;AACvB,QAAI,QAAQ,IAAK,OAAM;AAAA,EACzB;AACA,QAAM,QAAQ,MAAM;AAEpB,QAAM,QAAQ,QAAQ,IAAI,SAAS,QAAQ,KAAK;AAEhD,SAAO,OAAO,IAAI,CAAC,OAAO,UAAU;AAClC,UAAM,IAAI,QAAQ;AAClB,UAAM,QAAQ,UAAU,IAAI,OAAO,QAAQ,OAAO;AAClD,UAAM,IAAI,WAAW,IAAI,SAAS;AAClC,WAAO,EAAE,GAAG,EAAE;AAAA,EAChB,CAAC;AACH;AAEA,SAAS,OAAO,QAAiD;AAC/D,SAAO,OACJ,IAAI,CAAC,OAAO,UAAU,GAAG,UAAU,IAAI,MAAM,GAAG,IAAI,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,EAC9F,KAAK,GAAG;AACb;AAEO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,SAAS;AACX,GAAmB;AACjB,MAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,YAAY,QAAQ,OAAO,MAAM;AAChD,QAAM,WAAW,OAAO,MAAM;AAE9B,QAAM,aAAa,OAAO,CAAC;AAC3B,QAAM,YAAY,OAAO,OAAO,SAAS,CAAC;AAC1C,QAAM,WAAW,GAAG,QAAQ,MAAM,UAAU,EAAE,QAAQ,CAAC,CAAC,IAAI,MAAM,MAAM,WAAW,EAAE,QAAQ,CAAC,CAAC,IAAI,MAAM;AAEzG,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,SAAS,OAAO,KAAK,IAAI,MAAM;AAAA,MAC/B,qBAAoB;AAAA,MACpB;AAAA,MAEA;AAAA,4BAAC,UAAK,GAAG,UAAU,MAAK,gBAAe,aAAa,MAAM,QAAO,QAAO;AAAA,QACxE;AAAA,UAAC;AAAA;AAAA,YACC,GAAG;AAAA,YACH,MAAK;AAAA,YACL,QAAO;AAAA,YACP,aAAa;AAAA,YACb,eAAc;AAAA,YACd,gBAAe;AAAA;AAAA,QACjB;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,oBAAQ;",
6
+ "names": []
7
+ }
@@ -1,4 +1,5 @@
1
- import { KpiCard } from "./KpiCard.js";
1
+ import { KpiCard, DeltaBadge } from "./KpiCard.js";
2
+ import { Sparkline } from "./Sparkline.js";
2
3
  import { BarChart } from "./BarChart.js";
3
4
  import { LineChart } from "./LineChart.js";
4
5
  import { PieChart } from "./PieChart.js";
@@ -9,9 +10,11 @@ export {
9
10
  CHART_COLORS,
10
11
  ChartContainer,
11
12
  ChartTooltipContent,
13
+ DeltaBadge,
12
14
  KpiCard,
13
15
  LineChart,
14
16
  PieChart,
17
+ Sparkline,
15
18
  TopNTable,
16
19
  getChartColor
17
20
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/charts/index.ts"],
4
- "sourcesContent": ["export { KpiCard, type KpiCardProps, type KpiTrend } from './KpiCard'\nexport { BarChart, type BarChartProps, type BarChartDataItem } from './BarChart'\nexport { LineChart, type LineChartProps, type LineChartDataItem } from './LineChart'\nexport { PieChart, type PieChartProps, type PieChartDataItem } from './PieChart'\nexport { TopNTable, type TopNTableProps, type TopNTableColumn } from './TopNTable'\nexport { ChartContainer, ChartTooltipContent, CHART_COLORS, getChartColor, type ChartConfig } from './ChartUtils'\n"],
5
- "mappings": "AAAA,SAAS,eAAiD;AAC1D,SAAS,gBAA2D;AACpE,SAAS,iBAA8D;AACvE,SAAS,gBAA2D;AACpE,SAAS,iBAA4D;AACrE,SAAS,gBAAgB,qBAAqB,cAAc,qBAAuC;",
4
+ "sourcesContent": ["export { KpiCard, DeltaBadge, type KpiCardProps, type KpiTrend } from './KpiCard'\nexport { Sparkline, type SparklineProps } from './Sparkline'\nexport { BarChart, type BarChartProps, type BarChartDataItem } from './BarChart'\nexport { LineChart, type LineChartProps, type LineChartDataItem } from './LineChart'\nexport { PieChart, type PieChartProps, type PieChartDataItem } from './PieChart'\nexport { TopNTable, type TopNTableProps, type TopNTableColumn } from './TopNTable'\nexport { ChartContainer, ChartTooltipContent, CHART_COLORS, getChartColor, type ChartConfig } from './ChartUtils'\n"],
5
+ "mappings": "AAAA,SAAS,SAAS,kBAAoD;AACtE,SAAS,iBAAsC;AAC/C,SAAS,gBAA2D;AACpE,SAAS,iBAA8D;AACvE,SAAS,gBAA2D;AACpE,SAAS,iBAA4D;AACrE,SAAS,gBAAgB,qBAAqB,cAAc,qBAAuC;",
6
6
  "names": []
7
7
  }
@@ -9,7 +9,7 @@ import { Alert, AlertDescription, AlertTitle } from "@open-mercato/ui/primitives
9
9
  import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
10
10
  import { getDashboardWidgets, loadDashboardWidgetModule } from "./widgetRegistry.js";
11
11
  import { cn } from "@open-mercato/shared/lib/utils";
12
- import { GripVertical, Info, Plus, RefreshCw, Settings2, Trash2, X, Loader2 } from "lucide-react";
12
+ import { GripVertical, Plus, RefreshCw, Settings2, Trash2, X, Loader2 } from "lucide-react";
13
13
  import { useT } from "@open-mercato/shared/lib/i18n/context";
14
14
  import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
15
15
  import { InjectionSpot } from "../injection/InjectionSpot.js";
@@ -290,7 +290,6 @@ function DashboardScreen() {
290
290
  }
291
291
  if (!hasRegisteredWidgets && layout.length === 0) {
292
292
  return /* @__PURE__ */ jsxs(Alert, { variant: "info", children: [
293
- /* @__PURE__ */ jsx(Info, { className: "h-4 w-4", "aria-hidden": true }),
294
293
  /* @__PURE__ */ jsx(AlertTitle, { children: t("dashboard.empty.noWidgets.title", "No dashboard widgets yet") }),
295
294
  /* @__PURE__ */ jsx(AlertDescription, { children: t(
296
295
  "dashboard.empty.noWidgets.description",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/backend/dashboard/DashboardScreen.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { getDashboardWidgets, loadDashboardWidgetModule } from './widgetRegistry'\nimport type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { GripVertical, Info, Plus, RefreshCw, Settings2, Trash2, X, Loader2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { InjectionSpot } from '../injection/InjectionSpot'\nimport { WidgetDataBatchProvider } from './widgetData'\n\ntype DashboardWidgetSize = 'sm' | 'md' | 'lg'\n\ntype LayoutItem = {\n id: string\n widgetId: string\n order: number\n priority?: number\n size?: DashboardWidgetSize\n settings?: unknown\n}\n\ntype WidgetMeta = {\n id: string\n title: string\n description: string | null\n defaultSize: DashboardWidgetSize\n defaultEnabled: boolean\n defaultSettings: unknown\n features: string[]\n moduleId: string\n icon: string | null\n loaderKey: string\n supportsRefresh: boolean\n}\n\ntype LayoutContext = {\n userId: string\n tenantId: string | null\n organizationId: string | null\n userName: string | null\n userEmail: string | null\n userLabel: string | null\n}\n\ntype LayoutResponse = {\n layout: { items: LayoutItem[] }\n widgets: WidgetMeta[]\n allowedWidgetIds: string[]\n canConfigure: boolean\n context: LayoutContext\n}\n\ntype WidgetModule = DashboardWidgetModule<any>\n\nfunction sizeClass(size: DashboardWidgetSize | undefined) {\n switch (size) {\n case 'lg':\n return 'md:col-span-2'\n case 'md':\n return 'md:col-span-1'\n case 'sm':\n default:\n return 'md:col-span-1'\n }\n}\n\nfunction sortLayout(items: LayoutItem[]): LayoutItem[] {\n return [...items]\n .sort((a, b) => {\n const aOrder = a.order ?? a.priority ?? 0\n const bOrder = b.order ?? b.priority ?? 0\n return aOrder - bOrder\n })\n .map((item, index) => ({ ...item, order: index, priority: index }))\n}\n\nconst DEFAULT_SIZE: DashboardWidgetSize = 'md'\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n // Fallback: timestamp + random for better uniqueness\n return Date.now().toString(36) + Math.random().toString(36).slice(2)\n}\n\nexport function DashboardScreen() {\n const t = useT()\n const organizationScopeVersion = useOrganizationScopeVersion()\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [hasRegisteredWidgets, setHasRegisteredWidgets] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [layout, setLayout] = React.useState<LayoutItem[]>([])\n const [widgetCatalog, setWidgetCatalog] = React.useState<WidgetMeta[]>([])\n const [allowedWidgetIds, setAllowedWidgetIds] = React.useState<string[]>([])\n const [canConfigure, setCanConfigure] = React.useState(false)\n const [context, setContext] = React.useState<LayoutContext | null>(null)\n const [editing, setEditing] = React.useState(false)\n const [settingsId, setSettingsId] = React.useState<string | null>(null)\n const pendingOpsRef = React.useRef(0)\n const saveQueueRef = React.useRef(Promise.resolve())\n const draggingIdRef = React.useRef<string | null>(null)\n\n const adjustSaving = React.useCallback((delta: number) => {\n pendingOpsRef.current = Math.max(0, pendingOpsRef.current + delta)\n setSaving(pendingOpsRef.current > 0)\n }, [])\n\n const load = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const call = await apiCall<LayoutResponse>('/api/dashboards/layout')\n if (!call.ok || !call.result) {\n throw new Error(`Failed with status ${call.status}`)\n }\n const data = call.result\n const registeredWidgetCount = getDashboardWidgets().length\n const normalizedLayout = sortLayout(data.layout?.items ?? [])\n setLayout(normalizedLayout)\n setWidgetCatalog(data.widgets ?? [])\n setHasRegisteredWidgets(registeredWidgetCount > 0 || (data.widgets ?? []).length > 0)\n setAllowedWidgetIds(data.allowedWidgetIds ?? [])\n setCanConfigure(!!data.canConfigure)\n if (data.context) {\n setContext({\n userId: data.context.userId,\n tenantId: data.context.tenantId ?? null,\n organizationId: data.context.organizationId ?? null,\n userName: data.context.userName ?? null,\n userEmail: data.context.userEmail ?? null,\n userLabel: data.context.userLabel ?? null,\n })\n } else {\n setContext(null)\n }\n if (!data.canConfigure) {\n setEditing(false)\n setSettingsId(null)\n }\n } catch (err) {\n console.error('Failed to load dashboard layout', err)\n if (getDashboardWidgets().length === 0) {\n setHasRegisteredWidgets(false)\n setLayout([])\n setWidgetCatalog([])\n setAllowedWidgetIds([])\n setCanConfigure(false)\n setContext(null)\n setEditing(false)\n setSettingsId(null)\n return\n }\n setError(t('dashboard.loadError'))\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n load()\n }, [load, organizationScopeVersion])\n\n const metaById = React.useMemo(() => {\n const map = new Map<string, WidgetMeta>()\n for (const meta of widgetCatalog) map.set(meta.id, meta)\n return map\n }, [widgetCatalog])\n\n const availableWidgets = React.useMemo(() => {\n const currentIds = new Set(layout.map((item) => item.widgetId))\n return widgetCatalog.filter((meta) => !currentIds.has(meta.id))\n }, [layout, widgetCatalog])\n\n const resolveWidgetTitle = React.useCallback((meta: WidgetMeta): string => {\n const keys = [\n `${meta.id}.title`,\n `dashboard.widgets.${meta.id}.title`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.title`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.title\n }, [t])\n\n const resolveWidgetDescription = React.useCallback((meta: WidgetMeta): string | null => {\n if (!meta.description) return null\n const keys = [\n `${meta.id}.description`,\n `dashboard.widgets.${meta.id}.description`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.description`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.description\n }, [t])\n\n const queueLayoutSave = React.useCallback((items: LayoutItem[]) => {\n saveQueueRef.current = saveQueueRef.current.then(async () => {\n adjustSaving(1)\n try {\n const payload = {\n items: items.map((item, index) => ({\n id: item.id,\n widgetId: item.widgetId,\n order: index,\n priority: index,\n size: item.size ?? DEFAULT_SIZE,\n settings: item.settings ?? null,\n })),\n }\n const call = await apiCall('/api/dashboards/layout', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n console.error('Failed to save layout', err)\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n })\n }, [adjustSaving, t])\n\n const patchWidgetSettings = React.useCallback(async (itemId: string, nextSettings: unknown) => {\n adjustSaving(1)\n try {\n const call = await apiCall(`/api/dashboards/layout/${encodeURIComponent(itemId)}`, {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ settings: nextSettings }),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n console.error('Failed to update widget settings', err)\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n }, [adjustSaving, t])\n\n const handleAddWidget = React.useCallback((widgetId: string) => {\n const meta = metaById.get(widgetId)\n if (!meta) return\n setLayout((prev) => {\n const next: LayoutItem[] = sortLayout([\n ...prev,\n {\n id: generateId(),\n widgetId: meta.id,\n order: prev.length,\n priority: prev.length,\n size: meta.defaultSize ?? DEFAULT_SIZE,\n settings: meta.defaultSettings ?? null,\n },\n ])\n queueLayoutSave(next)\n return next\n })\n setSettingsId(null)\n }, [metaById, queueLayoutSave])\n\n const handleRemoveWidget = React.useCallback((itemId: string) => {\n setLayout((prev) => {\n const next = sortLayout(prev.filter((item) => item.id !== itemId))\n queueLayoutSave(next)\n return next\n })\n if (settingsId === itemId) setSettingsId(null)\n }, [queueLayoutSave, settingsId])\n\n const handleReorder = React.useCallback((dragId: string | null, targetId: string) => {\n if (!dragId || dragId === targetId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((item) => item.id === dragId)\n const to = items.findIndex((item) => item.id === targetId)\n if (from === -1 || to === -1) return prev\n const [moved] = items.splice(from, 1)\n items.splice(to, 0, moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n }, [queueLayoutSave])\n\n const handleSettingsChange = React.useCallback((itemId: string, nextSettings: unknown) => {\n setLayout((prev) => prev.map((item) => (item.id === itemId ? { ...item, settings: nextSettings } : item)))\n void patchWidgetSettings(itemId, nextSettings)\n }, [patchWidgetSettings])\n\n const toggleEditing = React.useCallback(() => {\n if (!canConfigure) return\n setEditing((prev) => {\n const next = !prev\n if (!next) setSettingsId(null)\n return next\n })\n }, [canConfigure])\n\n const handleRefresh = React.useCallback(() => {\n load()\n }, [load])\n\n const injectionContext = React.useMemo(\n () => ({\n layout,\n widgetCatalog,\n allowedWidgetIds,\n canConfigure,\n editing,\n userContext: context,\n }),\n [allowedWidgetIds, canConfigure, context, editing, layout, widgetCatalog],\n )\n const dashboardBeforeSpotId = 'dashboard:before'\n const dashboardAfterSpotId = 'dashboard:after'\n\n if (loading) {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n )\n }\n\n if (error && layout.length === 0) {\n return (\n <ErrorNotice\n title={t('dashboard.unavailable')}\n message={error}\n action={<Button variant=\"outline\" onClick={handleRefresh}>{t('dashboard.retry')}</Button>}\n />\n )\n }\n\n if (!hasRegisteredWidgets && layout.length === 0) {\n return (\n <Alert variant=\"info\">\n <Info className=\"h-4 w-4\" aria-hidden />\n <AlertTitle>{t('dashboard.empty.noWidgets.title', 'No dashboard widgets yet')}</AlertTitle>\n <AlertDescription>\n {t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">{t('dashboard.title')}</h1>\n <p className=\"text-sm text-muted-foreground\">{t('dashboard.subtitle')}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n {saving && (\n <div className=\"flex items-center gap-1 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n <span>{t('dashboard.saving')}</span>\n </div>\n )}\n {canConfigure && (\n <Button variant={editing ? 'secondary' : 'outline'} size=\"sm\" onClick={toggleEditing}>\n <Settings2 className=\"h-4 w-4\" />\n <span>{editing ? t('dashboard.action.done') : t('dashboard.action.customize')}</span>\n </Button>\n )}\n </div>\n </div>\n\n {error && layout.length > 0 && (\n <ErrorNotice\n title={t('dashboard.error.partial')}\n message={error}\n action={<Button variant=\"ghost\" onClick={handleRefresh}>{t('dashboard.error.reload')}</Button>}\n />\n )}\n\n <InjectionSpot spotId={dashboardBeforeSpotId} context={injectionContext} />\n\n {editing && availableWidgets.length > 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/50 p-4\">\n <div className=\"mb-2 text-sm font-medium text-muted-foreground\">{t('dashboard.addWidget')}</div>\n <div className=\"flex flex-wrap gap-2\">\n {availableWidgets.map((meta) => (\n <Button\n key={meta.id}\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleAddWidget(meta.id)}\n >\n <Plus className=\"h-4 w-4\" />\n {resolveWidgetTitle(meta)}\n </Button>\n ))}\n </div>\n </div>\n )}\n\n <WidgetDataBatchProvider>\n <div className={cn(\n 'grid gap-3 sm:gap-4 md:gap-6',\n 'grid-cols-1',\n 'md:grid-cols-2',\n 'xl:grid-cols-3'\n )}\n onDragOver={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n }}\n onDrop={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n if (!dragId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((entry) => entry.id === dragId)\n if (from === -1) return prev\n const [moved] = items.splice(from, 1)\n items.push(moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n draggingIdRef.current = null\n }}>\n {layout.map((item) => {\n const meta = metaById.get(item.widgetId)\n if (!meta) return null\n const title = resolveWidgetTitle(meta)\n const description = resolveWidgetDescription(meta)\n return (\n <DashboardWidgetCard\n key={item.id}\n item={item}\n meta={meta}\n title={title}\n description={description}\n context={context}\n editing={editing && canConfigure}\n activeSettings={settingsId === item.id}\n onToggleSettings={() => setSettingsId((current) => (current === item.id ? null : item.id))}\n onRemove={() => handleRemoveWidget(item.id)}\n onSettingsChange={(settings) => handleSettingsChange(item.id, settings)}\n onDragStart={() => { draggingIdRef.current = item.id }}\n onDragEnd={() => { draggingIdRef.current = null }}\n onDrop={(event) => {\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n handleReorder(dragId, item.id)\n draggingIdRef.current = null\n }}\n onDragEnter={() => {}}\n onDragLeave={() => {}}\n sizeClass={sizeClass(item.size)}\n />\n )\n })}\n </div>\n </WidgetDataBatchProvider>\n\n {layout.length === 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/30 p-10 text-center text-sm text-muted-foreground\">\n {!hasRegisteredWidgets\n ? t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )\n : canConfigure ? t('dashboard.empty.configurable') : t('dashboard.empty.readonly')}\n </div>\n )}\n\n <InjectionSpot spotId={dashboardAfterSpotId} context={injectionContext} />\n </div>\n )\n}\n\ntype DashboardWidgetCardProps = {\n item: LayoutItem\n meta: WidgetMeta\n title: string\n description: string | null\n context: LayoutContext | null\n editing: boolean\n activeSettings: boolean\n onToggleSettings: () => void\n onRemove: () => void\n onSettingsChange: (next: unknown) => void\n onDragStart: () => void\n onDragEnd: () => void\n onDrop: (event: React.DragEvent<HTMLDivElement>) => void\n onDragEnter: () => void\n onDragLeave: () => void\n sizeClass: string\n}\n\nfunction DashboardWidgetCard({\n item,\n meta,\n title,\n description,\n context,\n editing,\n activeSettings,\n onToggleSettings,\n onRemove,\n onSettingsChange,\n onDragStart,\n onDragEnd,\n onDrop,\n onDragEnter,\n onDragLeave,\n sizeClass,\n}: DashboardWidgetCardProps) {\n const t = useT()\n const [module, setModule] = React.useState<WidgetModule | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const [isDragOver, setIsDragOver] = React.useState(false)\n const [refreshToken, setRefreshToken] = React.useState(0)\n const [refreshing, setRefreshing] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n setLoading(true)\n setLoadError(null)\n loadDashboardWidgetModule(meta.loaderKey)\n .then((loaded) => {\n if (cancelled) return\n if (!loaded) {\n setModule(null)\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n return\n }\n setModule(loaded)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n console.error('Failed to load widget module', err)\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n })\n return () => { cancelled = true }\n }, [meta.loaderKey, t])\n\n React.useEffect(() => {\n if (!meta.supportsRefresh) {\n setRefreshing(false)\n }\n }, [meta.supportsRefresh])\n\n React.useEffect(() => {\n if (activeSettings) {\n setRefreshing(false)\n }\n }, [activeSettings])\n\n React.useEffect(() => {\n if (loadError) {\n setRefreshing(false)\n }\n }, [loadError])\n\n const handleRefreshStateChange = React.useCallback((next: boolean) => {\n setRefreshing(next)\n }, [])\n\n const triggerRefresh = React.useCallback(() => {\n if (loading || !!loadError) return\n setRefreshing(true)\n setRefreshToken((current) => current + 1)\n }, [loadError, loading])\n\n const hydratedSettings = React.useMemo(() => {\n const raw = item.settings ?? meta.defaultSettings ?? null\n if (module?.hydrateSettings) {\n try {\n return module.hydrateSettings(raw)\n } catch (err) {\n console.warn('Failed to hydrate widget settings', err)\n return raw\n }\n }\n return raw\n }, [item.settings, meta.defaultSettings, module])\n\n const handleSettingsChange = React.useCallback((next: unknown) => {\n let raw = next\n if (module?.dehydrateSettings) {\n try {\n raw = module.dehydrateSettings(next as never)\n } catch (err) {\n console.warn('Failed to dehydrate widget settings', err)\n }\n }\n onSettingsChange(raw)\n }, [module, onSettingsChange])\n\n const WidgetComponent = module?.Widget\n const mode = activeSettings ? 'settings' : 'view'\n\n return (\n <div\n className={cn(\n 'group relative flex h-full flex-col rounded-lg border bg-background shadow-sm transition',\n isDragOver ? 'border-primary ring-2 ring-primary/20' : 'hover:border-primary/40',\n editing ? 'cursor-grab' : 'cursor-default',\n sizeClass\n )}\n draggable={editing}\n onDragStart={(event) => {\n if (!editing) return\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', item.id)\n onDragStart()\n }}\n onDragEnd={() => {\n if (!editing) return\n onDragEnd()\n }}\n onDragOver={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n event.dataTransfer.dropEffect = 'move'\n if (!isDragOver) {\n setIsDragOver(true)\n onDragEnter()\n }\n }}\n onDrop={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n onDrop(event)\n setIsDragOver(false)\n onDragLeave()\n }}\n onDragLeave={(event) => {\n if (!editing) return\n event.stopPropagation()\n if (event.currentTarget.contains(event.relatedTarget as Node)) return\n setIsDragOver(false)\n onDragLeave()\n }}\n >\n <div className=\"flex items-center justify-between gap-2 border-b px-3 py-2\">\n <div className=\"flex items-center gap-2\">\n {editing && <GripVertical className=\"h-4 w-4 text-muted-foreground\" />}\n <div>\n <div className=\"text-sm font-medium leading-none\">{title}</div>\n {description ? <div className=\"mt-1 text-xs text-muted-foreground\">{description}</div> : null}\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {!editing && meta.supportsRefresh && (\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n disabled={refreshing || loading || !!loadError}\n onClick={triggerRefresh}\n aria-label={t('dashboard.widget.refresh')}\n >\n {refreshing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n </IconButton>\n )}\n {editing && (\n <>\n <IconButton\n variant={activeSettings ? 'outline' : 'ghost'}\n size=\"sm\"\n onClick={onToggleSettings}\n aria-label={activeSettings ? t('dashboard.widget.closeSettings') : t('dashboard.widget.editSettings')}\n >\n {activeSettings ? <X className=\"h-4 w-4\" /> : <Settings2 className=\"h-4 w-4\" />}\n </IconButton>\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n onClick={onRemove}\n aria-label={t('dashboard.widget.remove')}\n >\n <Trash2 className=\"h-4 w-4\" />\n </IconButton>\n </>\n )}\n </div>\n </div>\n <div className=\"flex-1 p-4\">\n {loading && (\n <div className=\"flex h-full min-h-[120px] items-center justify-center\">\n <Spinner />\n </div>\n )}\n {loadError && !loading && (\n <div className=\"text-sm text-muted-foreground\">{loadError}</div>\n )}\n {!loading && !loadError && WidgetComponent && (\n <WidgetComponent\n mode={mode as 'view' | 'settings'}\n layout={item}\n context={context ?? { userId: '', tenantId: null, organizationId: null, userName: null, userEmail: null, userLabel: null }}\n settings={hydratedSettings}\n onSettingsChange={handleSettingsChange}\n refreshToken={refreshToken}\n onRefreshStateChange={handleRefreshStateChange}\n />\n )}\n </div>\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { getDashboardWidgets, loadDashboardWidgetModule } from './widgetRegistry'\nimport type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { GripVertical, Plus, RefreshCw, Settings2, Trash2, X, Loader2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { InjectionSpot } from '../injection/InjectionSpot'\nimport { WidgetDataBatchProvider } from './widgetData'\n\ntype DashboardWidgetSize = 'sm' | 'md' | 'lg'\n\ntype LayoutItem = {\n id: string\n widgetId: string\n order: number\n priority?: number\n size?: DashboardWidgetSize\n settings?: unknown\n}\n\ntype WidgetMeta = {\n id: string\n title: string\n description: string | null\n defaultSize: DashboardWidgetSize\n defaultEnabled: boolean\n defaultSettings: unknown\n features: string[]\n moduleId: string\n icon: string | null\n loaderKey: string\n supportsRefresh: boolean\n}\n\ntype LayoutContext = {\n userId: string\n tenantId: string | null\n organizationId: string | null\n userName: string | null\n userEmail: string | null\n userLabel: string | null\n}\n\ntype LayoutResponse = {\n layout: { items: LayoutItem[] }\n widgets: WidgetMeta[]\n allowedWidgetIds: string[]\n canConfigure: boolean\n context: LayoutContext\n}\n\ntype WidgetModule = DashboardWidgetModule<any>\n\nfunction sizeClass(size: DashboardWidgetSize | undefined) {\n switch (size) {\n case 'lg':\n return 'md:col-span-2'\n case 'md':\n return 'md:col-span-1'\n case 'sm':\n default:\n return 'md:col-span-1'\n }\n}\n\nfunction sortLayout(items: LayoutItem[]): LayoutItem[] {\n return [...items]\n .sort((a, b) => {\n const aOrder = a.order ?? a.priority ?? 0\n const bOrder = b.order ?? b.priority ?? 0\n return aOrder - bOrder\n })\n .map((item, index) => ({ ...item, order: index, priority: index }))\n}\n\nconst DEFAULT_SIZE: DashboardWidgetSize = 'md'\n\nfunction generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n // Fallback: timestamp + random for better uniqueness\n return Date.now().toString(36) + Math.random().toString(36).slice(2)\n}\n\nexport function DashboardScreen() {\n const t = useT()\n const organizationScopeVersion = useOrganizationScopeVersion()\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [hasRegisteredWidgets, setHasRegisteredWidgets] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [layout, setLayout] = React.useState<LayoutItem[]>([])\n const [widgetCatalog, setWidgetCatalog] = React.useState<WidgetMeta[]>([])\n const [allowedWidgetIds, setAllowedWidgetIds] = React.useState<string[]>([])\n const [canConfigure, setCanConfigure] = React.useState(false)\n const [context, setContext] = React.useState<LayoutContext | null>(null)\n const [editing, setEditing] = React.useState(false)\n const [settingsId, setSettingsId] = React.useState<string | null>(null)\n const pendingOpsRef = React.useRef(0)\n const saveQueueRef = React.useRef(Promise.resolve())\n const draggingIdRef = React.useRef<string | null>(null)\n\n const adjustSaving = React.useCallback((delta: number) => {\n pendingOpsRef.current = Math.max(0, pendingOpsRef.current + delta)\n setSaving(pendingOpsRef.current > 0)\n }, [])\n\n const load = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const call = await apiCall<LayoutResponse>('/api/dashboards/layout')\n if (!call.ok || !call.result) {\n throw new Error(`Failed with status ${call.status}`)\n }\n const data = call.result\n const registeredWidgetCount = getDashboardWidgets().length\n const normalizedLayout = sortLayout(data.layout?.items ?? [])\n setLayout(normalizedLayout)\n setWidgetCatalog(data.widgets ?? [])\n setHasRegisteredWidgets(registeredWidgetCount > 0 || (data.widgets ?? []).length > 0)\n setAllowedWidgetIds(data.allowedWidgetIds ?? [])\n setCanConfigure(!!data.canConfigure)\n if (data.context) {\n setContext({\n userId: data.context.userId,\n tenantId: data.context.tenantId ?? null,\n organizationId: data.context.organizationId ?? null,\n userName: data.context.userName ?? null,\n userEmail: data.context.userEmail ?? null,\n userLabel: data.context.userLabel ?? null,\n })\n } else {\n setContext(null)\n }\n if (!data.canConfigure) {\n setEditing(false)\n setSettingsId(null)\n }\n } catch (err) {\n console.error('Failed to load dashboard layout', err)\n if (getDashboardWidgets().length === 0) {\n setHasRegisteredWidgets(false)\n setLayout([])\n setWidgetCatalog([])\n setAllowedWidgetIds([])\n setCanConfigure(false)\n setContext(null)\n setEditing(false)\n setSettingsId(null)\n return\n }\n setError(t('dashboard.loadError'))\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n load()\n }, [load, organizationScopeVersion])\n\n const metaById = React.useMemo(() => {\n const map = new Map<string, WidgetMeta>()\n for (const meta of widgetCatalog) map.set(meta.id, meta)\n return map\n }, [widgetCatalog])\n\n const availableWidgets = React.useMemo(() => {\n const currentIds = new Set(layout.map((item) => item.widgetId))\n return widgetCatalog.filter((meta) => !currentIds.has(meta.id))\n }, [layout, widgetCatalog])\n\n const resolveWidgetTitle = React.useCallback((meta: WidgetMeta): string => {\n const keys = [\n `${meta.id}.title`,\n `dashboard.widgets.${meta.id}.title`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.title`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.title\n }, [t])\n\n const resolveWidgetDescription = React.useCallback((meta: WidgetMeta): string | null => {\n if (!meta.description) return null\n const keys = [\n `${meta.id}.description`,\n `dashboard.widgets.${meta.id}.description`,\n ]\n if (meta.id.includes('.')) {\n const parts = meta.id.split('.')\n const lastPart = parts.pop()\n keys.unshift(`${parts.join('.')}.widgets.${lastPart}.description`)\n }\n for (const key of keys) {\n const translated = t(key)\n if (translated !== key) return translated\n }\n return meta.description\n }, [t])\n\n const queueLayoutSave = React.useCallback((items: LayoutItem[]) => {\n saveQueueRef.current = saveQueueRef.current.then(async () => {\n adjustSaving(1)\n try {\n const payload = {\n items: items.map((item, index) => ({\n id: item.id,\n widgetId: item.widgetId,\n order: index,\n priority: index,\n size: item.size ?? DEFAULT_SIZE,\n settings: item.settings ?? null,\n })),\n }\n const call = await apiCall('/api/dashboards/layout', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n console.error('Failed to save layout', err)\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n })\n }, [adjustSaving, t])\n\n const patchWidgetSettings = React.useCallback(async (itemId: string, nextSettings: unknown) => {\n adjustSaving(1)\n try {\n const call = await apiCall(`/api/dashboards/layout/${encodeURIComponent(itemId)}`, {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ settings: nextSettings }),\n })\n if (!call.ok) throw new Error(`Failed with status ${call.status}`)\n setError(null)\n } catch (err) {\n console.error('Failed to update widget settings', err)\n setError(t('dashboard.saveError'))\n } finally {\n adjustSaving(-1)\n }\n }, [adjustSaving, t])\n\n const handleAddWidget = React.useCallback((widgetId: string) => {\n const meta = metaById.get(widgetId)\n if (!meta) return\n setLayout((prev) => {\n const next: LayoutItem[] = sortLayout([\n ...prev,\n {\n id: generateId(),\n widgetId: meta.id,\n order: prev.length,\n priority: prev.length,\n size: meta.defaultSize ?? DEFAULT_SIZE,\n settings: meta.defaultSettings ?? null,\n },\n ])\n queueLayoutSave(next)\n return next\n })\n setSettingsId(null)\n }, [metaById, queueLayoutSave])\n\n const handleRemoveWidget = React.useCallback((itemId: string) => {\n setLayout((prev) => {\n const next = sortLayout(prev.filter((item) => item.id !== itemId))\n queueLayoutSave(next)\n return next\n })\n if (settingsId === itemId) setSettingsId(null)\n }, [queueLayoutSave, settingsId])\n\n const handleReorder = React.useCallback((dragId: string | null, targetId: string) => {\n if (!dragId || dragId === targetId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((item) => item.id === dragId)\n const to = items.findIndex((item) => item.id === targetId)\n if (from === -1 || to === -1) return prev\n const [moved] = items.splice(from, 1)\n items.splice(to, 0, moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n }, [queueLayoutSave])\n\n const handleSettingsChange = React.useCallback((itemId: string, nextSettings: unknown) => {\n setLayout((prev) => prev.map((item) => (item.id === itemId ? { ...item, settings: nextSettings } : item)))\n void patchWidgetSettings(itemId, nextSettings)\n }, [patchWidgetSettings])\n\n const toggleEditing = React.useCallback(() => {\n if (!canConfigure) return\n setEditing((prev) => {\n const next = !prev\n if (!next) setSettingsId(null)\n return next\n })\n }, [canConfigure])\n\n const handleRefresh = React.useCallback(() => {\n load()\n }, [load])\n\n const injectionContext = React.useMemo(\n () => ({\n layout,\n widgetCatalog,\n allowedWidgetIds,\n canConfigure,\n editing,\n userContext: context,\n }),\n [allowedWidgetIds, canConfigure, context, editing, layout, widgetCatalog],\n )\n const dashboardBeforeSpotId = 'dashboard:before'\n const dashboardAfterSpotId = 'dashboard:after'\n\n if (loading) {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n )\n }\n\n if (error && layout.length === 0) {\n return (\n <ErrorNotice\n title={t('dashboard.unavailable')}\n message={error}\n action={<Button variant=\"outline\" onClick={handleRefresh}>{t('dashboard.retry')}</Button>}\n />\n )\n }\n\n if (!hasRegisteredWidgets && layout.length === 0) {\n return (\n <Alert variant=\"info\">\n <AlertTitle>{t('dashboard.empty.noWidgets.title', 'No dashboard widgets yet')}</AlertTitle>\n <AlertDescription>\n {t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">{t('dashboard.title')}</h1>\n <p className=\"text-sm text-muted-foreground\">{t('dashboard.subtitle')}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n {saving && (\n <div className=\"flex items-center gap-1 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n <span>{t('dashboard.saving')}</span>\n </div>\n )}\n {canConfigure && (\n <Button variant={editing ? 'secondary' : 'outline'} size=\"sm\" onClick={toggleEditing}>\n <Settings2 className=\"h-4 w-4\" />\n <span>{editing ? t('dashboard.action.done') : t('dashboard.action.customize')}</span>\n </Button>\n )}\n </div>\n </div>\n\n {error && layout.length > 0 && (\n <ErrorNotice\n title={t('dashboard.error.partial')}\n message={error}\n action={<Button variant=\"ghost\" onClick={handleRefresh}>{t('dashboard.error.reload')}</Button>}\n />\n )}\n\n <InjectionSpot spotId={dashboardBeforeSpotId} context={injectionContext} />\n\n {editing && availableWidgets.length > 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/50 p-4\">\n <div className=\"mb-2 text-sm font-medium text-muted-foreground\">{t('dashboard.addWidget')}</div>\n <div className=\"flex flex-wrap gap-2\">\n {availableWidgets.map((meta) => (\n <Button\n key={meta.id}\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleAddWidget(meta.id)}\n >\n <Plus className=\"h-4 w-4\" />\n {resolveWidgetTitle(meta)}\n </Button>\n ))}\n </div>\n </div>\n )}\n\n <WidgetDataBatchProvider>\n <div className={cn(\n 'grid gap-3 sm:gap-4 md:gap-6',\n 'grid-cols-1',\n 'md:grid-cols-2',\n 'xl:grid-cols-3'\n )}\n onDragOver={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n }}\n onDrop={(event) => {\n if (!editing || !canConfigure) return\n event.preventDefault()\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n if (!dragId) return\n setLayout((prev) => {\n const items = [...prev]\n const from = items.findIndex((entry) => entry.id === dragId)\n if (from === -1) return prev\n const [moved] = items.splice(from, 1)\n items.push(moved)\n const next = items.map((item, index) => ({\n ...item,\n order: index,\n priority: index,\n }))\n queueLayoutSave(next)\n return next\n })\n draggingIdRef.current = null\n }}>\n {layout.map((item) => {\n const meta = metaById.get(item.widgetId)\n if (!meta) return null\n const title = resolveWidgetTitle(meta)\n const description = resolveWidgetDescription(meta)\n return (\n <DashboardWidgetCard\n key={item.id}\n item={item}\n meta={meta}\n title={title}\n description={description}\n context={context}\n editing={editing && canConfigure}\n activeSettings={settingsId === item.id}\n onToggleSettings={() => setSettingsId((current) => (current === item.id ? null : item.id))}\n onRemove={() => handleRemoveWidget(item.id)}\n onSettingsChange={(settings) => handleSettingsChange(item.id, settings)}\n onDragStart={() => { draggingIdRef.current = item.id }}\n onDragEnd={() => { draggingIdRef.current = null }}\n onDrop={(event) => {\n const dragId = event.dataTransfer.getData('text/plain') || draggingIdRef.current\n handleReorder(dragId, item.id)\n draggingIdRef.current = null\n }}\n onDragEnter={() => {}}\n onDragLeave={() => {}}\n sizeClass={sizeClass(item.size)}\n />\n )\n })}\n </div>\n </WidgetDataBatchProvider>\n\n {layout.length === 0 && (\n <div className=\"rounded-lg border border-dashed bg-muted/30 p-10 text-center text-sm text-muted-foreground\">\n {!hasRegisteredWidgets\n ? t(\n 'dashboard.empty.noWidgets.description',\n 'After you add the first module that exposes dashboard widgets, they will appear here.',\n )\n : canConfigure ? t('dashboard.empty.configurable') : t('dashboard.empty.readonly')}\n </div>\n )}\n\n <InjectionSpot spotId={dashboardAfterSpotId} context={injectionContext} />\n </div>\n )\n}\n\ntype DashboardWidgetCardProps = {\n item: LayoutItem\n meta: WidgetMeta\n title: string\n description: string | null\n context: LayoutContext | null\n editing: boolean\n activeSettings: boolean\n onToggleSettings: () => void\n onRemove: () => void\n onSettingsChange: (next: unknown) => void\n onDragStart: () => void\n onDragEnd: () => void\n onDrop: (event: React.DragEvent<HTMLDivElement>) => void\n onDragEnter: () => void\n onDragLeave: () => void\n sizeClass: string\n}\n\nfunction DashboardWidgetCard({\n item,\n meta,\n title,\n description,\n context,\n editing,\n activeSettings,\n onToggleSettings,\n onRemove,\n onSettingsChange,\n onDragStart,\n onDragEnd,\n onDrop,\n onDragEnter,\n onDragLeave,\n sizeClass,\n}: DashboardWidgetCardProps) {\n const t = useT()\n const [module, setModule] = React.useState<WidgetModule | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [loadError, setLoadError] = React.useState<string | null>(null)\n const [isDragOver, setIsDragOver] = React.useState(false)\n const [refreshToken, setRefreshToken] = React.useState(0)\n const [refreshing, setRefreshing] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n setLoading(true)\n setLoadError(null)\n loadDashboardWidgetModule(meta.loaderKey)\n .then((loaded) => {\n if (cancelled) return\n if (!loaded) {\n setModule(null)\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n return\n }\n setModule(loaded)\n setLoading(false)\n })\n .catch((err) => {\n if (cancelled) return\n console.error('Failed to load widget module', err)\n setLoadError(t('dashboard.widget.loadError'))\n setLoading(false)\n })\n return () => { cancelled = true }\n }, [meta.loaderKey, t])\n\n React.useEffect(() => {\n if (!meta.supportsRefresh) {\n setRefreshing(false)\n }\n }, [meta.supportsRefresh])\n\n React.useEffect(() => {\n if (activeSettings) {\n setRefreshing(false)\n }\n }, [activeSettings])\n\n React.useEffect(() => {\n if (loadError) {\n setRefreshing(false)\n }\n }, [loadError])\n\n const handleRefreshStateChange = React.useCallback((next: boolean) => {\n setRefreshing(next)\n }, [])\n\n const triggerRefresh = React.useCallback(() => {\n if (loading || !!loadError) return\n setRefreshing(true)\n setRefreshToken((current) => current + 1)\n }, [loadError, loading])\n\n const hydratedSettings = React.useMemo(() => {\n const raw = item.settings ?? meta.defaultSettings ?? null\n if (module?.hydrateSettings) {\n try {\n return module.hydrateSettings(raw)\n } catch (err) {\n console.warn('Failed to hydrate widget settings', err)\n return raw\n }\n }\n return raw\n }, [item.settings, meta.defaultSettings, module])\n\n const handleSettingsChange = React.useCallback((next: unknown) => {\n let raw = next\n if (module?.dehydrateSettings) {\n try {\n raw = module.dehydrateSettings(next as never)\n } catch (err) {\n console.warn('Failed to dehydrate widget settings', err)\n }\n }\n onSettingsChange(raw)\n }, [module, onSettingsChange])\n\n const WidgetComponent = module?.Widget\n const mode = activeSettings ? 'settings' : 'view'\n\n return (\n <div\n className={cn(\n 'group relative flex h-full flex-col rounded-lg border bg-background shadow-sm transition',\n isDragOver ? 'border-primary ring-2 ring-primary/20' : 'hover:border-primary/40',\n editing ? 'cursor-grab' : 'cursor-default',\n sizeClass\n )}\n draggable={editing}\n onDragStart={(event) => {\n if (!editing) return\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', item.id)\n onDragStart()\n }}\n onDragEnd={() => {\n if (!editing) return\n onDragEnd()\n }}\n onDragOver={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n event.dataTransfer.dropEffect = 'move'\n if (!isDragOver) {\n setIsDragOver(true)\n onDragEnter()\n }\n }}\n onDrop={(event) => {\n if (!editing) return\n event.preventDefault()\n event.stopPropagation()\n onDrop(event)\n setIsDragOver(false)\n onDragLeave()\n }}\n onDragLeave={(event) => {\n if (!editing) return\n event.stopPropagation()\n if (event.currentTarget.contains(event.relatedTarget as Node)) return\n setIsDragOver(false)\n onDragLeave()\n }}\n >\n <div className=\"flex items-center justify-between gap-2 border-b px-3 py-2\">\n <div className=\"flex items-center gap-2\">\n {editing && <GripVertical className=\"h-4 w-4 text-muted-foreground\" />}\n <div>\n <div className=\"text-sm font-medium leading-none\">{title}</div>\n {description ? <div className=\"mt-1 text-xs text-muted-foreground\">{description}</div> : null}\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {!editing && meta.supportsRefresh && (\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n disabled={refreshing || loading || !!loadError}\n onClick={triggerRefresh}\n aria-label={t('dashboard.widget.refresh')}\n >\n {refreshing ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n </IconButton>\n )}\n {editing && (\n <>\n <IconButton\n variant={activeSettings ? 'outline' : 'ghost'}\n size=\"sm\"\n onClick={onToggleSettings}\n aria-label={activeSettings ? t('dashboard.widget.closeSettings') : t('dashboard.widget.editSettings')}\n >\n {activeSettings ? <X className=\"h-4 w-4\" /> : <Settings2 className=\"h-4 w-4\" />}\n </IconButton>\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n onClick={onRemove}\n aria-label={t('dashboard.widget.remove')}\n >\n <Trash2 className=\"h-4 w-4\" />\n </IconButton>\n </>\n )}\n </div>\n </div>\n <div className=\"flex-1 p-4\">\n {loading && (\n <div className=\"flex h-full min-h-[120px] items-center justify-center\">\n <Spinner />\n </div>\n )}\n {loadError && !loading && (\n <div className=\"text-sm text-muted-foreground\">{loadError}</div>\n )}\n {!loading && !loadError && WidgetComponent && (\n <WidgetComponent\n mode={mode as 'view' | 'settings'}\n layout={item}\n context={context ?? { userId: '', tenantId: null, organizationId: null, userName: null, userEmail: null, userLabel: null }}\n settings={hydratedSettings}\n onSettingsChange={handleSettingsChange}\n refreshToken={refreshToken}\n onRefreshStateChange={handleRefreshStateChange}\n />\n )}\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": ";AA8VQ,SAoWI,UApWJ,KAiBF,YAjBE;AA5VR,YAAY,WAAW;AACvB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAC5B,SAAS,OAAO,kBAAkB,kBAAkB;AACpD,SAAS,eAAe;AACxB,SAAS,qBAAqB,iCAAiC;AAE/D,SAAS,UAAU;AACnB,SAAS,cAAc,MAAM,WAAW,WAAW,QAAQ,GAAG,eAAe;AAC7E,SAAS,YAAY;AACrB,SAAS,mCAAmC;AAC5C,SAAS,qBAAqB;AAC9B,SAAS,+BAA+B;AA8CxC,SAAS,UAAU,MAAuC;AACxD,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,WAAW,OAAmC;AACrD,SAAO,CAAC,GAAG,KAAK,EACb,KAAK,CAAC,GAAG,MAAM;AACd,UAAM,SAAS,EAAE,SAAS,EAAE,YAAY;AACxC,UAAM,SAAS,EAAE,SAAS,EAAE,YAAY;AACxC,WAAO,SAAS;AAAA,EAClB,CAAC,EACA,IAAI,CAAC,MAAM,WAAW,EAAE,GAAG,MAAM,OAAO,OAAO,UAAU,MAAM,EAAE;AACtE;AAEA,MAAM,eAAoC;AAE1C,SAAS,aAAqB;AAC5B,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO,KAAK,IAAI,EAAE,SAAS,EAAE,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC;AACrE;AAEO,SAAS,kBAAkB;AAChC,QAAM,IAAI,KAAK;AACf,QAAM,2BAA2B,4BAA4B;AAC7D,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,sBAAsB,uBAAuB,IAAI,MAAM,SAAS,IAAI;AAC3E,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAuB,CAAC,CAAC;AAC3D,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAuB,CAAC,CAAC;AACzE,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAmB,CAAC,CAAC;AAC3E,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,KAAK;AAC5D,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAA+B,IAAI;AACvE,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAwB,IAAI;AACtE,QAAM,gBAAgB,MAAM,OAAO,CAAC;AACpC,QAAM,eAAe,MAAM,OAAO,QAAQ,QAAQ,CAAC;AACnD,QAAM,gBAAgB,MAAM,OAAsB,IAAI;AAEtD,QAAM,eAAe,MAAM,YAAY,CAAC,UAAkB;AACxD,kBAAc,UAAU,KAAK,IAAI,GAAG,cAAc,UAAU,KAAK;AACjE,cAAU,cAAc,UAAU,CAAC;AAAA,EACrC,GAAG,CAAC,CAAC;AAEL,QAAM,OAAO,MAAM,YAAY,YAAY;AACzC,eAAW,IAAI;AACf,aAAS,IAAI;AACb,QAAI;AACF,YAAM,OAAO,MAAM,QAAwB,wBAAwB;AACnE,UAAI,CAAC,KAAK,MAAM,CAAC,KAAK,QAAQ;AAC5B,cAAM,IAAI,MAAM,sBAAsB,KAAK,MAAM,EAAE;AAAA,MACrD;AACA,YAAM,OAAO,KAAK;AAClB,YAAM,wBAAwB,oBAAoB,EAAE;AACpD,YAAM,mBAAmB,WAAW,KAAK,QAAQ,SAAS,CAAC,CAAC;AAC5D,gBAAU,gBAAgB;AAC1B,uBAAiB,KAAK,WAAW,CAAC,CAAC;AACnC,8BAAwB,wBAAwB,MAAM,KAAK,WAAW,CAAC,GAAG,SAAS,CAAC;AACpF,0BAAoB,KAAK,oBAAoB,CAAC,CAAC;AAC/C,sBAAgB,CAAC,CAAC,KAAK,YAAY;AACnC,UAAI,KAAK,SAAS;AAChB,mBAAW;AAAA,UACT,QAAQ,KAAK,QAAQ;AAAA,UACrB,UAAU,KAAK,QAAQ,YAAY;AAAA,UACnC,gBAAgB,KAAK,QAAQ,kBAAkB;AAAA,UAC/C,UAAU,KAAK,QAAQ,YAAY;AAAA,UACnC,WAAW,KAAK,QAAQ,aAAa;AAAA,UACrC,WAAW,KAAK,QAAQ,aAAa;AAAA,QACvC,CAAC;AAAA,MACH,OAAO;AACL,mBAAW,IAAI;AAAA,MACjB;AACA,UAAI,CAAC,KAAK,cAAc;AACtB,mBAAW,KAAK;AAChB,sBAAc,IAAI;AAAA,MACpB;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,MAAM,mCAAmC,GAAG;AACpD,UAAI,oBAAoB,EAAE,WAAW,GAAG;AACtC,gCAAwB,KAAK;AAC7B,kBAAU,CAAC,CAAC;AACZ,yBAAiB,CAAC,CAAC;AACnB,4BAAoB,CAAC,CAAC;AACtB,wBAAgB,KAAK;AACrB,mBAAW,IAAI;AACf,mBAAW,KAAK;AAChB,sBAAc,IAAI;AAClB;AAAA,MACF;AACA,eAAS,EAAE,qBAAqB,CAAC;AAAA,IACnC,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,UAAU,MAAM;AACpB,SAAK;AAAA,EACP,GAAG,CAAC,MAAM,wBAAwB,CAAC;AAEnC,QAAM,WAAW,MAAM,QAAQ,MAAM;AACnC,UAAM,MAAM,oBAAI,IAAwB;AACxC,eAAW,QAAQ,cAAe,KAAI,IAAI,KAAK,IAAI,IAAI;AACvD,WAAO;AAAA,EACT,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,mBAAmB,MAAM,QAAQ,MAAM;AAC3C,UAAM,aAAa,IAAI,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC;AAC9D,WAAO,cAAc,OAAO,CAAC,SAAS,CAAC,WAAW,IAAI,KAAK,EAAE,CAAC;AAAA,EAChE,GAAG,CAAC,QAAQ,aAAa,CAAC;AAE1B,QAAM,qBAAqB,MAAM,YAAY,CAAC,SAA6B;AACzE,UAAM,OAAO;AAAA,MACX,GAAG,KAAK,EAAE;AAAA,MACV,qBAAqB,KAAK,EAAE;AAAA,IAC9B;AACA,QAAI,KAAK,GAAG,SAAS,GAAG,GAAG;AACzB,YAAM,QAAQ,KAAK,GAAG,MAAM,GAAG;AAC/B,YAAM,WAAW,MAAM,IAAI;AAC3B,WAAK,QAAQ,GAAG,MAAM,KAAK,GAAG,CAAC,YAAY,QAAQ,QAAQ;AAAA,IAC7D;AACA,eAAW,OAAO,MAAM;AACtB,YAAM,aAAa,EAAE,GAAG;AACxB,UAAI,eAAe,IAAK,QAAO;AAAA,IACjC;AACA,WAAO,KAAK;AAAA,EACd,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,2BAA2B,MAAM,YAAY,CAAC,SAAoC;AACtF,QAAI,CAAC,KAAK,YAAa,QAAO;AAC9B,UAAM,OAAO;AAAA,MACX,GAAG,KAAK,EAAE;AAAA,MACV,qBAAqB,KAAK,EAAE;AAAA,IAC9B;AACA,QAAI,KAAK,GAAG,SAAS,GAAG,GAAG;AACzB,YAAM,QAAQ,KAAK,GAAG,MAAM,GAAG;AAC/B,YAAM,WAAW,MAAM,IAAI;AAC3B,WAAK,QAAQ,GAAG,MAAM,KAAK,GAAG,CAAC,YAAY,QAAQ,cAAc;AAAA,IACnE;AACA,eAAW,OAAO,MAAM;AACtB,YAAM,aAAa,EAAE,GAAG;AACxB,UAAI,eAAe,IAAK,QAAO;AAAA,IACjC;AACA,WAAO,KAAK;AAAA,EACd,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,kBAAkB,MAAM,YAAY,CAAC,UAAwB;AACjE,iBAAa,UAAU,aAAa,QAAQ,KAAK,YAAY;AAC3D,mBAAa,CAAC;AACd,UAAI;AACF,cAAM,UAAU;AAAA,UACd,OAAO,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,YACjC,IAAI,KAAK;AAAA,YACT,UAAU,KAAK;AAAA,YACf,OAAO;AAAA,YACP,UAAU;AAAA,YACV,MAAM,KAAK,QAAQ;AAAA,YACnB,UAAU,KAAK,YAAY;AAAA,UAC7B,EAAE;AAAA,QACJ;AACA,cAAM,OAAO,MAAM,QAAQ,0BAA0B;AAAA,UACnD,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,QAC9B,CAAC;AACD,YAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,sBAAsB,KAAK,MAAM,EAAE;AACjE,iBAAS,IAAI;AAAA,MACf,SAAS,KAAK;AACZ,gBAAQ,MAAM,yBAAyB,GAAG;AAC1C,iBAAS,EAAE,qBAAqB,CAAC;AAAA,MACnC,UAAE;AACA,qBAAa,EAAE;AAAA,MACjB;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,cAAc,CAAC,CAAC;AAEpB,QAAM,sBAAsB,MAAM,YAAY,OAAO,QAAgB,iBAA0B;AAC7F,iBAAa,CAAC;AACd,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,0BAA0B,mBAAmB,MAAM,CAAC,IAAI;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,UAAU,aAAa,CAAC;AAAA,MACjD,CAAC;AACD,UAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,sBAAsB,KAAK,MAAM,EAAE;AACjE,eAAS,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,cAAQ,MAAM,oCAAoC,GAAG;AACrD,eAAS,EAAE,qBAAqB,CAAC;AAAA,IACnC,UAAE;AACA,mBAAa,EAAE;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,cAAc,CAAC,CAAC;AAEpB,QAAM,kBAAkB,MAAM,YAAY,CAAC,aAAqB;AAC9D,UAAM,OAAO,SAAS,IAAI,QAAQ;AAClC,QAAI,CAAC,KAAM;AACX,cAAU,CAAC,SAAS;AAClB,YAAM,OAAqB,WAAW;AAAA,QACpC,GAAG;AAAA,QACH;AAAA,UACE,IAAI,WAAW;AAAA,UACf,UAAU,KAAK;AAAA,UACf,OAAO,KAAK;AAAA,UACZ,UAAU,KAAK;AAAA,UACf,MAAM,KAAK,eAAe;AAAA,UAC1B,UAAU,KAAK,mBAAmB;AAAA,QACpC;AAAA,MACF,CAAC;AACD,sBAAgB,IAAI;AACpB,aAAO;AAAA,IACT,CAAC;AACD,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,UAAU,eAAe,CAAC;AAE9B,QAAM,qBAAqB,MAAM,YAAY,CAAC,WAAmB;AAC/D,cAAU,CAAC,SAAS;AAClB,YAAM,OAAO,WAAW,KAAK,OAAO,CAAC,SAAS,KAAK,OAAO,MAAM,CAAC;AACjE,sBAAgB,IAAI;AACpB,aAAO;AAAA,IACT,CAAC;AACD,QAAI,eAAe,OAAQ,eAAc,IAAI;AAAA,EAC/C,GAAG,CAAC,iBAAiB,UAAU,CAAC;AAEhC,QAAM,gBAAgB,MAAM,YAAY,CAAC,QAAuB,aAAqB;AACnF,QAAI,CAAC,UAAU,WAAW,SAAU;AACpC,cAAU,CAAC,SAAS;AAClB,YAAM,QAAQ,CAAC,GAAG,IAAI;AACtB,YAAM,OAAO,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,MAAM;AACzD,YAAM,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,QAAQ;AACzD,UAAI,SAAS,MAAM,OAAO,GAAI,QAAO;AACrC,YAAM,CAAC,KAAK,IAAI,MAAM,OAAO,MAAM,CAAC;AACpC,YAAM,OAAO,IAAI,GAAG,KAAK;AACzB,YAAM,OAAO,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,QACvC,GAAG;AAAA,QACH,OAAO;AAAA,QACP,UAAU;AAAA,MACZ,EAAE;AACF,sBAAgB,IAAI;AACpB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,uBAAuB,MAAM,YAAY,CAAC,QAAgB,iBAA0B;AACxF,cAAU,CAAC,SAAS,KAAK,IAAI,CAAC,SAAU,KAAK,OAAO,SAAS,EAAE,GAAG,MAAM,UAAU,aAAa,IAAI,IAAK,CAAC;AACzG,SAAK,oBAAoB,QAAQ,YAAY;AAAA,EAC/C,GAAG,CAAC,mBAAmB,CAAC;AAExB,QAAM,gBAAgB,MAAM,YAAY,MAAM;AAC5C,QAAI,CAAC,aAAc;AACnB,eAAW,CAAC,SAAS;AACnB,YAAM,OAAO,CAAC;AACd,UAAI,CAAC,KAAM,eAAc,IAAI;AAC7B,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,gBAAgB,MAAM,YAAY,MAAM;AAC5C,SAAK;AAAA,EACP,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,mBAAmB,MAAM;AAAA,IAC7B,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,IACf;AAAA,IACA,CAAC,kBAAkB,cAAc,SAAS,SAAS,QAAQ,aAAa;AAAA,EAC1E;AACA,QAAM,wBAAwB;AAC9B,QAAM,uBAAuB;AAE7B,MAAI,SAAS;AACX,WACE,oBAAC,SAAI,WAAU,kDACb,8BAAC,WAAQ,MAAK,MAAK,GACrB;AAAA,EAEJ;AAEA,MAAI,SAAS,OAAO,WAAW,GAAG;AAChC,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,uBAAuB;AAAA,QAChC,SAAS;AAAA,QACT,QAAQ,oBAAC,UAAO,SAAQ,WAAU,SAAS,eAAgB,YAAE,iBAAiB,GAAE;AAAA;AAAA,IAClF;AAAA,EAEJ;AAEA,MAAI,CAAC,wBAAwB,OAAO,WAAW,GAAG;AAChD,WACE,qBAAC,SAAM,SAAQ,QACb;AAAA,0BAAC,cAAY,YAAE,mCAAmC,0BAA0B,GAAE;AAAA,MAC9E,oBAAC,oBACE;AAAA,QACC;AAAA,QACA;AAAA,MACF,GACF;AAAA,OACF;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SAAI,WAAU,qDACb;AAAA,2BAAC,SACC;AAAA,4BAAC,QAAG,WAAU,yCAAyC,YAAE,iBAAiB,GAAE;AAAA,QAC5E,oBAAC,OAAE,WAAU,iCAAiC,YAAE,oBAAoB,GAAE;AAAA,SACxE;AAAA,MACA,qBAAC,SAAI,WAAU,2BACZ;AAAA,kBACC,qBAAC,SAAI,WAAU,yDACb;AAAA,8BAAC,WAAQ,WAAU,wBAAuB;AAAA,UAC1C,oBAAC,UAAM,YAAE,kBAAkB,GAAE;AAAA,WAC/B;AAAA,QAED,gBACC,qBAAC,UAAO,SAAS,UAAU,cAAc,WAAW,MAAK,MAAK,SAAS,eACrE;AAAA,8BAAC,aAAU,WAAU,WAAU;AAAA,UAC/B,oBAAC,UAAM,oBAAU,EAAE,uBAAuB,IAAI,EAAE,4BAA4B,GAAE;AAAA,WAChF;AAAA,SAEJ;AAAA,OACF;AAAA,IAEC,SAAS,OAAO,SAAS,KACxB;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,yBAAyB;AAAA,QAClC,SAAS;AAAA,QACT,QAAQ,oBAAC,UAAO,SAAQ,SAAQ,SAAS,eAAgB,YAAE,wBAAwB,GAAE;AAAA;AAAA,IACvF;AAAA,IAGF,oBAAC,iBAAc,QAAQ,uBAAuB,SAAS,kBAAkB;AAAA,IAExE,WAAW,iBAAiB,SAAS,KACpC,qBAAC,SAAI,WAAU,mDACb;AAAA,0BAAC,SAAI,WAAU,kDAAkD,YAAE,qBAAqB,GAAE;AAAA,MAC1F,oBAAC,SAAI,WAAU,wBACZ,2BAAiB,IAAI,CAAC,SACrB;AAAA,QAAC;AAAA;AAAA,UAEC,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,SAAS,MAAM,gBAAgB,KAAK,EAAE;AAAA,UAEtC;AAAA,gCAAC,QAAK,WAAU,WAAU;AAAA,YACzB,mBAAmB,IAAI;AAAA;AAAA;AAAA,QANnB,KAAK;AAAA,MAOZ,CACD,GACH;AAAA,OACF;AAAA,IAGF,oBAAC,2BACD;AAAA,MAAC;AAAA;AAAA,QAAI,WAAW;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,YAAY,CAAC,UAAU;AACrB,cAAI,CAAC,WAAW,CAAC,aAAc;AAC/B,gBAAM,eAAe;AACrB,gBAAM,aAAa,aAAa;AAAA,QAClC;AAAA,QACA,QAAQ,CAAC,UAAU;AACjB,cAAI,CAAC,WAAW,CAAC,aAAc;AAC/B,gBAAM,eAAe;AACrB,gBAAM,SAAS,MAAM,aAAa,QAAQ,YAAY,KAAK,cAAc;AACzE,cAAI,CAAC,OAAQ;AACb,oBAAU,CAAC,SAAS;AAClB,kBAAM,QAAQ,CAAC,GAAG,IAAI;AACtB,kBAAM,OAAO,MAAM,UAAU,CAAC,UAAU,MAAM,OAAO,MAAM;AAC3D,gBAAI,SAAS,GAAI,QAAO;AACxB,kBAAM,CAAC,KAAK,IAAI,MAAM,OAAO,MAAM,CAAC;AACpC,kBAAM,KAAK,KAAK;AAChB,kBAAM,OAAO,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,cACvC,GAAG;AAAA,cACH,OAAO;AAAA,cACP,UAAU;AAAA,YACZ,EAAE;AACF,4BAAgB,IAAI;AACpB,mBAAO;AAAA,UACT,CAAC;AACD,wBAAc,UAAU;AAAA,QAC1B;AAAA,QACG,iBAAO,IAAI,CAAC,SAAS;AACpB,gBAAM,OAAO,SAAS,IAAI,KAAK,QAAQ;AACvC,cAAI,CAAC,KAAM,QAAO;AAClB,gBAAM,QAAQ,mBAAmB,IAAI;AACrC,gBAAM,cAAc,yBAAyB,IAAI;AACjD,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,SAAS,WAAW;AAAA,cACpB,gBAAgB,eAAe,KAAK;AAAA,cACpC,kBAAkB,MAAM,cAAc,CAAC,YAAa,YAAY,KAAK,KAAK,OAAO,KAAK,EAAG;AAAA,cACzF,UAAU,MAAM,mBAAmB,KAAK,EAAE;AAAA,cAC1C,kBAAkB,CAAC,aAAa,qBAAqB,KAAK,IAAI,QAAQ;AAAA,cACtE,aAAa,MAAM;AAAE,8BAAc,UAAU,KAAK;AAAA,cAAG;AAAA,cACrD,WAAW,MAAM;AAAE,8BAAc,UAAU;AAAA,cAAK;AAAA,cAChD,QAAQ,CAAC,UAAU;AACjB,sBAAM,SAAS,MAAM,aAAa,QAAQ,YAAY,KAAK,cAAc;AACzE,8BAAc,QAAQ,KAAK,EAAE;AAC7B,8BAAc,UAAU;AAAA,cAC1B;AAAA,cACA,aAAa,MAAM;AAAA,cAAC;AAAA,cACpB,aAAa,MAAM;AAAA,cAAC;AAAA,cACpB,WAAW,UAAU,KAAK,IAAI;AAAA;AAAA,YApBzB,KAAK;AAAA,UAqBZ;AAAA,QAEJ,CAAC;AAAA;AAAA,IACH,GACA;AAAA,IAEC,OAAO,WAAW,KACjB,oBAAC,SAAI,WAAU,8FACZ,WAAC,uBACE;AAAA,MACA;AAAA,MACA;AAAA,IACF,IACE,eAAe,EAAE,8BAA8B,IAAI,EAAE,0BAA0B,GACrF;AAAA,IAGF,oBAAC,iBAAc,QAAQ,sBAAsB,SAAS,kBAAkB;AAAA,KAC1E;AAEJ;AAqBA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAAA;AACF,GAA6B;AAC3B,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAA8B,IAAI;AACpE,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAwB,IAAI;AACpE,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,CAAC;AACxD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AAExD,QAAM,UAAU,MAAM;AACpB,QAAI,YAAY;AAChB,eAAW,IAAI;AACf,iBAAa,IAAI;AACjB,8BAA0B,KAAK,SAAS,EACrC,KAAK,CAAC,WAAW;AAChB,UAAI,UAAW;AACf,UAAI,CAAC,QAAQ;AACX,kBAAU,IAAI;AACd,qBAAa,EAAE,4BAA4B,CAAC;AAC5C,mBAAW,KAAK;AAChB;AAAA,MACF;AACA,gBAAU,MAAM;AAChB,iBAAW,KAAK;AAAA,IAClB,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,UAAI,UAAW;AACf,cAAQ,MAAM,gCAAgC,GAAG;AACjD,mBAAa,EAAE,4BAA4B,CAAC;AAC5C,iBAAW,KAAK;AAAA,IAClB,CAAC;AACH,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAK;AAAA,EAClC,GAAG,CAAC,KAAK,WAAW,CAAC,CAAC;AAEtB,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,KAAK,iBAAiB;AACzB,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,KAAK,eAAe,CAAC;AAEzB,QAAM,UAAU,MAAM;AACpB,QAAI,gBAAgB;AAClB,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,UAAU,MAAM;AACpB,QAAI,WAAW;AACb,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,2BAA2B,MAAM,YAAY,CAAC,SAAkB;AACpE,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAiB,MAAM,YAAY,MAAM;AAC7C,QAAI,WAAW,CAAC,CAAC,UAAW;AAC5B,kBAAc,IAAI;AAClB,oBAAgB,CAAC,YAAY,UAAU,CAAC;AAAA,EAC1C,GAAG,CAAC,WAAW,OAAO,CAAC;AAEvB,QAAM,mBAAmB,MAAM,QAAQ,MAAM;AAC3C,UAAM,MAAM,KAAK,YAAY,KAAK,mBAAmB;AACrD,QAAI,QAAQ,iBAAiB;AAC3B,UAAI;AACF,eAAO,OAAO,gBAAgB,GAAG;AAAA,MACnC,SAAS,KAAK;AACZ,gBAAQ,KAAK,qCAAqC,GAAG;AACrD,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT,GAAG,CAAC,KAAK,UAAU,KAAK,iBAAiB,MAAM,CAAC;AAEhD,QAAM,uBAAuB,MAAM,YAAY,CAAC,SAAkB;AAChE,QAAI,MAAM;AACV,QAAI,QAAQ,mBAAmB;AAC7B,UAAI;AACF,cAAM,OAAO,kBAAkB,IAAa;AAAA,MAC9C,SAAS,KAAK;AACZ,gBAAQ,KAAK,uCAAuC,GAAG;AAAA,MACzD;AAAA,IACF;AACA,qBAAiB,GAAG;AAAA,EACtB,GAAG,CAAC,QAAQ,gBAAgB,CAAC;AAE7B,QAAM,kBAAkB,QAAQ;AAChC,QAAM,OAAO,iBAAiB,aAAa;AAE3C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,aAAa,0CAA0C;AAAA,QACvD,UAAU,gBAAgB;AAAA,QAC1BA;AAAA,MACF;AAAA,MACA,WAAW;AAAA,MACX,aAAa,CAAC,UAAU;AACtB,YAAI,CAAC,QAAS;AACd,cAAM,aAAa,gBAAgB;AACnC,cAAM,aAAa,QAAQ,cAAc,KAAK,EAAE;AAChD,oBAAY;AAAA,MACd;AAAA,MACA,WAAW,MAAM;AACf,YAAI,CAAC,QAAS;AACd,kBAAU;AAAA,MACZ;AAAA,MACA,YAAY,CAAC,UAAU;AACrB,YAAI,CAAC,QAAS;AACd,cAAM,eAAe;AACrB,cAAM,gBAAgB;AACtB,cAAM,aAAa,aAAa;AAChC,YAAI,CAAC,YAAY;AACf,wBAAc,IAAI;AAClB,sBAAY;AAAA,QACd;AAAA,MACF;AAAA,MACA,QAAQ,CAAC,UAAU;AACjB,YAAI,CAAC,QAAS;AACd,cAAM,eAAe;AACrB,cAAM,gBAAgB;AACtB,eAAO,KAAK;AACZ,sBAAc,KAAK;AACnB,oBAAY;AAAA,MACd;AAAA,MACA,aAAa,CAAC,UAAU;AACtB,YAAI,CAAC,QAAS;AACd,cAAM,gBAAgB;AACtB,YAAI,MAAM,cAAc,SAAS,MAAM,aAAqB,EAAG;AAC/D,sBAAc,KAAK;AACnB,oBAAY;AAAA,MACd;AAAA,MAEA;AAAA,6BAAC,SAAI,WAAU,8DACb;AAAA,+BAAC,SAAI,WAAU,2BACZ;AAAA,uBAAW,oBAAC,gBAAa,WAAU,iCAAgC;AAAA,YACpE,qBAAC,SACC;AAAA,kCAAC,SAAI,WAAU,oCAAoC,iBAAM;AAAA,cACxD,cAAc,oBAAC,SAAI,WAAU,sCAAsC,uBAAY,IAAS;AAAA,eAC3F;AAAA,aACF;AAAA,UACA,qBAAC,SAAI,WAAU,2BACZ;AAAA,aAAC,WAAW,KAAK,mBAChB;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,UAAU,cAAc,WAAW,CAAC,CAAC;AAAA,gBACrC,SAAS;AAAA,gBACT,cAAY,EAAE,0BAA0B;AAAA,gBAEvC,uBAAa,oBAAC,WAAQ,WAAU,wBAAuB,IAAK,oBAAC,aAAU,WAAU,WAAU;AAAA;AAAA,YAC9F;AAAA,YAED,WACC,iCACE;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,iBAAiB,YAAY;AAAA,kBACtC,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,cAAY,iBAAiB,EAAE,gCAAgC,IAAI,EAAE,+BAA+B;AAAA,kBAEnG,2BAAiB,oBAAC,KAAE,WAAU,WAAU,IAAK,oBAAC,aAAU,WAAU,WAAU;AAAA;AAAA,cAC/E;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,cAAY,EAAE,yBAAyB;AAAA,kBAEvC,8BAAC,UAAO,WAAU,WAAU;AAAA;AAAA,cAC9B;AAAA,eACF;AAAA,aAEJ;AAAA,WACF;AAAA,QACA,qBAAC,SAAI,WAAU,cACZ;AAAA,qBACC,oBAAC,SAAI,WAAU,yDACb,8BAAC,WAAQ,GACX;AAAA,UAED,aAAa,CAAC,WACb,oBAAC,SAAI,WAAU,iCAAiC,qBAAU;AAAA,UAE3D,CAAC,WAAW,CAAC,aAAa,mBACzB;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,QAAQ;AAAA,cACR,SAAS,WAAW,EAAE,QAAQ,IAAI,UAAU,MAAM,gBAAgB,MAAM,UAAU,MAAM,WAAW,MAAM,WAAW,KAAK;AAAA,cACzH,UAAU;AAAA,cACV,kBAAkB;AAAA,cAClB;AAAA,cACA,sBAAsB;AAAA;AAAA,UACxB;AAAA,WAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;",
6
6
  "names": ["sizeClass"]
7
7
  }
@@ -31,6 +31,14 @@ function useInjectionWidgets(spotId, options) {
31
31
  const [error, setError] = React.useState(null);
32
32
  const [registryVersion, setRegistryVersion] = React.useState(() => getInjectionRegistryVersion());
33
33
  const loadedRef = React.useRef(false);
34
+ const contextRef = React.useRef(options?.context);
35
+ const triggerOnLoadRef = React.useRef(options?.triggerOnLoad);
36
+ const onEventRef = React.useRef(options?.onEvent);
37
+ React.useEffect(() => {
38
+ contextRef.current = options?.context;
39
+ triggerOnLoadRef.current = options?.triggerOnLoad;
40
+ onEventRef.current = options?.onEvent;
41
+ });
34
42
  React.useEffect(() => {
35
43
  return subscribeToInjectionRegistryChanges(() => {
36
44
  setRegistryVersion(getInjectionRegistryVersion());
@@ -58,14 +66,14 @@ function useInjectionWidgets(spotId, options) {
58
66
  placement: w.placement
59
67
  }));
60
68
  setWidgets(widgetList);
61
- if (!loadedRef.current && options?.triggerOnLoad) {
69
+ if (!loadedRef.current && triggerOnLoadRef.current) {
62
70
  loadedRef.current = true;
63
71
  for (const widget of widgetList) {
64
72
  if (widget.module.eventHandlers?.onLoad) {
65
73
  try {
66
- const widgetContext = injectSharedStateIntoContext(options.context, widget.moduleId);
74
+ const widgetContext = injectSharedStateIntoContext(contextRef.current, widget.moduleId);
67
75
  await widget.module.eventHandlers.onLoad(widgetContext);
68
- options.onEvent?.("onLoad", widget.widgetId);
76
+ onEventRef.current?.("onLoad", widget.widgetId);
69
77
  } catch (err) {
70
78
  console.error(`[InjectionSpot] Error in onLoad for widget ${widget.widgetId}:`, err);
71
79
  }
@@ -84,7 +92,7 @@ function useInjectionWidgets(spotId, options) {
84
92
  return () => {
85
93
  mounted = false;
86
94
  };
87
- }, [spotId, options?.context, options?.triggerOnLoad, options?.onEvent, registryVersion]);
95
+ }, [spotId, registryVersion]);
88
96
  return { widgets, loading, error };
89
97
  }
90
98
  function InjectionSpot({
@@ -97,15 +105,24 @@ function InjectionSpot({
97
105
  widgetsOverride
98
106
  }) {
99
107
  const useSpotId = widgetsOverride ? null : spotId;
108
+ const onEventRef = React.useRef(onEvent);
109
+ React.useEffect(() => {
110
+ onEventRef.current = onEvent;
111
+ });
112
+ const hasOnEvent = Boolean(onEvent);
113
+ const stableOnEvent = React.useMemo(
114
+ () => hasOnEvent ? (event, id) => onEventRef.current?.(event, id) : void 0,
115
+ [hasOnEvent]
116
+ );
100
117
  const { widgets, loading, error } = useInjectionWidgets(useSpotId, {
101
118
  context,
102
119
  triggerOnLoad: !widgetsOverride,
103
- onEvent: onEvent ? (event, id) => onEvent(event, id) : void 0
120
+ onEvent: stableOnEvent
104
121
  });
105
122
  const effectiveWidgets = widgetsOverride ?? widgets;
106
123
  const effectiveLoading = widgetsOverride ? false : loading;
107
124
  const effectiveError = widgetsOverride ? null : error;
108
- if (effectiveLoading) {
125
+ if (effectiveLoading && effectiveWidgets.length === 0) {
109
126
  return null;
110
127
  }
111
128
  if (effectiveError) {