@almadar/ui 4.51.14 → 4.51.16

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.
@@ -3933,6 +3933,67 @@ var init_Radio = __esm({
3933
3933
  Radio.displayName = "Radio";
3934
3934
  }
3935
3935
  });
3936
+ var COLOR_VAR, Sparkline;
3937
+ var init_Sparkline = __esm({
3938
+ "components/atoms/Sparkline.tsx"() {
3939
+ init_cn();
3940
+ COLOR_VAR = {
3941
+ primary: "var(--color-primary)",
3942
+ success: "var(--color-success)",
3943
+ warning: "var(--color-warning)",
3944
+ error: "var(--color-error)",
3945
+ info: "var(--color-info)",
3946
+ muted: "var(--color-muted-foreground)"
3947
+ };
3948
+ Sparkline = ({
3949
+ data,
3950
+ color = "auto",
3951
+ width = 80,
3952
+ height = 32,
3953
+ strokeWidth = 2,
3954
+ fill = false,
3955
+ className
3956
+ }) => {
3957
+ if (data.length < 2) return null;
3958
+ const pad = 2;
3959
+ const min = Math.min(...data);
3960
+ const max = Math.max(...data);
3961
+ const range = max - min || 1;
3962
+ const points = data.map((v, i) => {
3963
+ const x = pad + i / (data.length - 1) * (width - pad * 2);
3964
+ const y = pad + (1 - (v - min) / range) * (height - pad * 2);
3965
+ return `${x},${y}`;
3966
+ }).join(" ");
3967
+ const resolvedColor = color === "auto" ? data[data.length - 1] >= data[0] ? COLOR_VAR.success : COLOR_VAR.error : COLOR_VAR[color];
3968
+ const areaPath = fill ? `M ${pad},${height - pad} L ${points.split(" ").join(" L ")} L ${width - pad},${height - pad} Z` : null;
3969
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3970
+ "svg",
3971
+ {
3972
+ width,
3973
+ height,
3974
+ viewBox: `0 0 ${width} ${height}`,
3975
+ className: cn("flex-shrink-0", className),
3976
+ "aria-hidden": "true",
3977
+ children: [
3978
+ areaPath && /* @__PURE__ */ jsxRuntime.jsx("path", { d: areaPath, fill: resolvedColor, opacity: 0.15 }),
3979
+ /* @__PURE__ */ jsxRuntime.jsx(
3980
+ "polyline",
3981
+ {
3982
+ fill: "none",
3983
+ stroke: resolvedColor,
3984
+ strokeWidth,
3985
+ strokeLinecap: "round",
3986
+ strokeLinejoin: "round",
3987
+ points
3988
+ }
3989
+ )
3990
+ ]
3991
+ }
3992
+ );
3993
+ };
3994
+ Sparkline.displayName = "Sparkline";
3995
+ }
3996
+ });
3936
3997
  var Switch;
3937
3998
  var init_Switch = __esm({
3938
3999
  "components/atoms/Switch.tsx"() {
@@ -9210,7 +9271,7 @@ var init_MapView = __esm({
9210
9271
  shadowSize: [41, 41]
9211
9272
  });
9212
9273
  L.Marker.prototype.options.icon = defaultIcon;
9213
- const { useEffect: useEffect68, useRef: useRef65, useCallback: useCallback110, useState: useState98 } = React80__namespace.default;
9274
+ const { useEffect: useEffect68, useRef: useRef65, useCallback: useCallback112, useState: useState99 } = React80__namespace.default;
9214
9275
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
9215
9276
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
9216
9277
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -9255,8 +9316,8 @@ var init_MapView = __esm({
9255
9316
  showAttribution = true
9256
9317
  }) {
9257
9318
  const eventBus = useEventBus2();
9258
- const [clickedPosition, setClickedPosition] = useState98(null);
9259
- const handleMapClick = useCallback110((lat, lng) => {
9319
+ const [clickedPosition, setClickedPosition] = useState99(null);
9320
+ const handleMapClick = useCallback112((lat, lng) => {
9260
9321
  if (showClickedPin) {
9261
9322
  setClickedPosition({ lat, lng });
9262
9323
  }
@@ -9265,7 +9326,7 @@ var init_MapView = __esm({
9265
9326
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
9266
9327
  }
9267
9328
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
9268
- const handleMarkerClick = useCallback110((marker) => {
9329
+ const handleMarkerClick = useCallback112((marker) => {
9269
9330
  onMarkerClick?.(marker);
9270
9331
  if (markerClickEvent) {
9271
9332
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -19060,7 +19121,7 @@ var init_CastleTemplate = __esm({
19060
19121
  CastleTemplate.displayName = "CastleTemplate";
19061
19122
  }
19062
19123
  });
19063
- var CHART_COLORS, BarChart, PieChart, LineChart, Chart;
19124
+ var CHART_COLORS, seriesColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, Chart;
19064
19125
  var init_Chart = __esm({
19065
19126
  "components/organisms/Chart.tsx"() {
19066
19127
  "use client";
@@ -19080,38 +19141,159 @@ var init_Chart = __esm({
19080
19141
  "var(--color-info)",
19081
19142
  "var(--color-accent)"
19082
19143
  ];
19083
- BarChart = ({ data, height, showValues }) => {
19084
- const maxValue = Math.max(...data.map((d) => d.value), 1);
19085
- return /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", align: "end", className: "w-full", style: { height }, children: data.map((point, idx) => {
19086
- const barHeight = point.value / maxValue * 100;
19087
- const color = point.color || CHART_COLORS[idx % CHART_COLORS.length];
19088
- return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", align: "center", flex: true, className: "min-w-0", children: [
19089
- showValues && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: point.value }),
19090
- /* @__PURE__ */ jsxRuntime.jsx(
19091
- Box,
19092
- {
19093
- className: cn(
19094
- "w-full rounded-t-sm transition-all duration-500 ease-out min-h-[4px]"
19095
- ),
19096
- style: {
19097
- height: `${barHeight}%`,
19098
- backgroundColor: color
19099
- }
19144
+ seriesColor = (series, idx) => series.color ?? CHART_COLORS[idx % CHART_COLORS.length];
19145
+ monthFormatter = new Intl.DateTimeFormat(void 0, {
19146
+ month: "short",
19147
+ year: "2-digit"
19148
+ });
19149
+ formatTimeLabel = (raw) => {
19150
+ const parsed = new Date(raw);
19151
+ if (Number.isNaN(parsed.getTime())) return raw;
19152
+ return monthFormatter.format(parsed);
19153
+ };
19154
+ BarChart = ({ series, height, showValues, stack, timeAxis, histogram = false, onPointClick }) => {
19155
+ const categories = React80.useMemo(() => {
19156
+ const set = [];
19157
+ const seen = /* @__PURE__ */ new Set();
19158
+ for (const s of series) {
19159
+ for (const p2 of s.data) {
19160
+ if (!seen.has(p2.label)) {
19161
+ seen.add(p2.label);
19162
+ set.push(p2.label);
19100
19163
  }
19101
- ),
19102
- /* @__PURE__ */ jsxRuntime.jsx(
19103
- Typography,
19104
- {
19105
- variant: "caption",
19106
- color: "secondary",
19107
- className: "truncate w-full text-center",
19108
- children: point.label
19164
+ }
19165
+ }
19166
+ return set;
19167
+ }, [series]);
19168
+ const valueAt = React80.useCallback(
19169
+ (s, label) => {
19170
+ const p2 = s.data.find((d) => d.label === label);
19171
+ return p2 ? p2.value : 0;
19172
+ },
19173
+ []
19174
+ );
19175
+ const columnTotals = React80.useMemo(() => {
19176
+ if (stack === "none") return null;
19177
+ return categories.map(
19178
+ (label) => series.reduce((sum, s) => sum + valueAt(s, label), 0)
19179
+ );
19180
+ }, [categories, series, stack, valueAt]);
19181
+ const maxValue = React80.useMemo(() => {
19182
+ if (stack === "normalize") return 100;
19183
+ if (stack === "stack" && columnTotals) {
19184
+ return Math.max(...columnTotals, 1);
19185
+ }
19186
+ let m = 1;
19187
+ for (const s of series) {
19188
+ for (const p2 of s.data) if (p2.value > m) m = p2.value;
19189
+ }
19190
+ return m;
19191
+ }, [series, stack, columnTotals]);
19192
+ return /* @__PURE__ */ jsxRuntime.jsx(
19193
+ HStack,
19194
+ {
19195
+ gap: histogram ? "none" : "xs",
19196
+ align: "end",
19197
+ className: "w-full",
19198
+ style: { height },
19199
+ children: categories.map((label, catIdx) => {
19200
+ const displayLabel = timeAxis ? formatTimeLabel(label) : label;
19201
+ if (stack === "none") {
19202
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19203
+ VStack,
19204
+ {
19205
+ gap: "xs",
19206
+ align: "center",
19207
+ flex: true,
19208
+ className: "min-w-0",
19209
+ children: [
19210
+ /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: histogram ? "none" : "xs", align: "end", className: "w-full", style: { height: "100%" }, children: series.map((s, sIdx) => {
19211
+ const value = valueAt(s, label);
19212
+ const barHeight = value / maxValue * 100;
19213
+ const color = seriesColor(s, sIdx);
19214
+ return /* @__PURE__ */ jsxRuntime.jsx(
19215
+ Box,
19216
+ {
19217
+ className: cn(
19218
+ "rounded-t-sm transition-all duration-500 ease-out min-h-[4px] cursor-pointer hover:opacity-80",
19219
+ histogram ? "flex-1 mx-0" : "flex-1"
19220
+ ),
19221
+ style: {
19222
+ height: `${barHeight}%`,
19223
+ backgroundColor: color
19224
+ },
19225
+ onClick: () => onPointClick?.(
19226
+ { label, value, color },
19227
+ s.name
19228
+ ),
19229
+ title: `${s.name}: ${value}`
19230
+ },
19231
+ s.name
19232
+ );
19233
+ }) }),
19234
+ showValues && series.length === 1 && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: valueAt(series[0], label) }),
19235
+ /* @__PURE__ */ jsxRuntime.jsx(
19236
+ Typography,
19237
+ {
19238
+ variant: "caption",
19239
+ color: "secondary",
19240
+ className: "truncate w-full text-center",
19241
+ children: displayLabel
19242
+ }
19243
+ )
19244
+ ]
19245
+ },
19246
+ label
19247
+ );
19109
19248
  }
19110
- )
19111
- ] }, point.label);
19112
- }) });
19249
+ const total = columnTotals?.[catIdx] ?? 1;
19250
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19251
+ VStack,
19252
+ {
19253
+ gap: "xs",
19254
+ align: "center",
19255
+ flex: true,
19256
+ className: "min-w-0",
19257
+ children: [
19258
+ /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", className: "w-full", style: { height: "100%" }, justify: "end", children: series.map((s, sIdx) => {
19259
+ const value = valueAt(s, label);
19260
+ const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
19261
+ const color = seriesColor(s, sIdx);
19262
+ return /* @__PURE__ */ jsxRuntime.jsx(
19263
+ Box,
19264
+ {
19265
+ className: "w-full transition-all duration-500 ease-out cursor-pointer hover:opacity-80",
19266
+ style: {
19267
+ height: `${ratio}%`,
19268
+ backgroundColor: color
19269
+ },
19270
+ onClick: () => onPointClick?.(
19271
+ { label, value, color },
19272
+ s.name
19273
+ ),
19274
+ title: `${s.name}: ${value}`
19275
+ },
19276
+ s.name
19277
+ );
19278
+ }) }),
19279
+ /* @__PURE__ */ jsxRuntime.jsx(
19280
+ Typography,
19281
+ {
19282
+ variant: "caption",
19283
+ color: "secondary",
19284
+ className: "truncate w-full text-center",
19285
+ children: displayLabel
19286
+ }
19287
+ )
19288
+ ]
19289
+ },
19290
+ label
19291
+ );
19292
+ })
19293
+ }
19294
+ );
19113
19295
  };
19114
- PieChart = ({ data, height, showValues, donut = false }) => {
19296
+ PieChart = ({ data, height, showValues, donut = false, onPointClick }) => {
19115
19297
  const total = data.reduce((sum, d) => sum + d.value, 0);
19116
19298
  const size = Math.min(height, 200);
19117
19299
  const radius = size / 2 - 8;
@@ -19157,7 +19339,11 @@ var init_Chart = __esm({
19157
19339
  fill: seg.color,
19158
19340
  stroke: "var(--color-card)",
19159
19341
  strokeWidth: "2",
19160
- className: "transition-opacity duration-200 hover:opacity-80"
19342
+ className: "transition-opacity duration-200 hover:opacity-80 cursor-pointer",
19343
+ onClick: () => onPointClick?.(
19344
+ { label: seg.label, value: seg.value, color: seg.color },
19345
+ "default"
19346
+ )
19161
19347
  },
19162
19348
  idx
19163
19349
  )),
@@ -19192,56 +19378,243 @@ var init_Chart = __esm({
19192
19378
  ] }, idx)) })
19193
19379
  ] });
19194
19380
  };
19195
- LineChart = ({ data, height, showValues, fill = false }) => {
19196
- const maxValue = Math.max(...data.map((d) => d.value), 1);
19381
+ LineChart = ({ series, height, showValues, fill = false, timeAxis, onPointClick }) => {
19197
19382
  const width = 400;
19198
19383
  const padding = { top: 20, right: 20, bottom: 30, left: 40 };
19199
19384
  const chartWidth = width - padding.left - padding.right;
19200
19385
  const chartHeight = height - padding.top - padding.bottom;
19201
- const points = React80.useMemo(() => {
19202
- return data.map((point, idx) => ({
19203
- x: padding.left + idx / Math.max(data.length - 1, 1) * chartWidth,
19204
- y: padding.top + chartHeight - point.value / maxValue * chartHeight,
19205
- ...point
19206
- }));
19207
- }, [data, maxValue, chartWidth, chartHeight, padding]);
19208
- const linePath = points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
19209
- const areaPath = `${linePath} L ${points[points.length - 1]?.x ?? 0} ${padding.top + chartHeight} L ${padding.left} ${padding.top + chartHeight} Z`;
19210
- return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "100%", height, viewBox: `0 0 ${width} ${height}`, preserveAspectRatio: "xMidYMid meet", children: [
19211
- [0, 0.25, 0.5, 0.75, 1].map((frac) => {
19212
- const y = padding.top + chartHeight * (1 - frac);
19213
- return /* @__PURE__ */ jsxRuntime.jsx(
19214
- "line",
19215
- {
19216
- x1: padding.left,
19217
- y1: y,
19218
- x2: width - padding.right,
19219
- y2: y,
19220
- stroke: "var(--color-border)",
19221
- strokeDasharray: "4 4",
19222
- opacity: 0.5
19223
- },
19224
- frac
19225
- );
19226
- }),
19227
- fill && /* @__PURE__ */ jsxRuntime.jsx("path", { d: areaPath, fill: "var(--color-primary)", opacity: 0.1 }),
19228
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: linePath, fill: "none", stroke: "var(--color-primary)", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
19229
- points.map((p2, idx) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
19230
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: p2.x, cy: p2.y, r: "4", fill: "var(--color-card)", stroke: "var(--color-primary)", strokeWidth: "2" }),
19231
- showValues && /* @__PURE__ */ jsxRuntime.jsx("text", { x: p2.x, y: p2.y - 10, textAnchor: "middle", fill: "var(--color-foreground)", fontSize: "10", fontWeight: "500", children: p2.value }),
19232
- /* @__PURE__ */ jsxRuntime.jsx(
19233
- "text",
19234
- {
19235
- x: p2.x,
19236
- y: height - 8,
19237
- textAnchor: "middle",
19238
- fill: "var(--color-muted-foreground)",
19239
- fontSize: "9",
19240
- children: p2.label
19386
+ const labels = React80.useMemo(() => {
19387
+ const seen = /* @__PURE__ */ new Set();
19388
+ const out = [];
19389
+ for (const s of series) {
19390
+ for (const p2 of s.data) {
19391
+ if (!seen.has(p2.label)) {
19392
+ seen.add(p2.label);
19393
+ out.push(p2.label);
19241
19394
  }
19242
- )
19243
- ] }, idx))
19244
- ] });
19395
+ }
19396
+ }
19397
+ return out;
19398
+ }, [series]);
19399
+ const maxValue = React80.useMemo(() => {
19400
+ let m = 1;
19401
+ for (const s of series) {
19402
+ for (const p2 of s.data) if (p2.value > m) m = p2.value;
19403
+ }
19404
+ return m;
19405
+ }, [series]);
19406
+ const xFor = React80.useCallback(
19407
+ (idx) => padding.left + idx / Math.max(labels.length - 1, 1) * chartWidth,
19408
+ [labels.length, chartWidth, padding.left]
19409
+ );
19410
+ const yFor = React80.useCallback(
19411
+ (value) => padding.top + chartHeight - value / maxValue * chartHeight,
19412
+ [maxValue, chartHeight, padding.top]
19413
+ );
19414
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19415
+ "svg",
19416
+ {
19417
+ width: "100%",
19418
+ height,
19419
+ viewBox: `0 0 ${width} ${height}`,
19420
+ preserveAspectRatio: "xMidYMid meet",
19421
+ children: [
19422
+ [0, 0.25, 0.5, 0.75, 1].map((frac) => {
19423
+ const y = padding.top + chartHeight * (1 - frac);
19424
+ return /* @__PURE__ */ jsxRuntime.jsx(
19425
+ "line",
19426
+ {
19427
+ x1: padding.left,
19428
+ y1: y,
19429
+ x2: width - padding.right,
19430
+ y2: y,
19431
+ stroke: "var(--color-border)",
19432
+ strokeDasharray: "4 4",
19433
+ opacity: 0.5
19434
+ },
19435
+ frac
19436
+ );
19437
+ }),
19438
+ series.map((s, sIdx) => {
19439
+ const color = seriesColor(s, sIdx);
19440
+ const points = labels.map((label, idx) => {
19441
+ const point = s.data.find((d) => d.label === label);
19442
+ return {
19443
+ x: xFor(idx),
19444
+ y: yFor(point ? point.value : 0),
19445
+ value: point ? point.value : 0,
19446
+ label
19447
+ };
19448
+ });
19449
+ const linePath = points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
19450
+ const areaPath = `${linePath} L ${points[points.length - 1]?.x ?? 0} ${padding.top + chartHeight} L ${padding.left} ${padding.top + chartHeight} Z`;
19451
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
19452
+ fill && /* @__PURE__ */ jsxRuntime.jsx(
19453
+ "path",
19454
+ {
19455
+ d: areaPath,
19456
+ fill: color,
19457
+ opacity: series.length > 1 ? 0.08 : 0.1
19458
+ }
19459
+ ),
19460
+ /* @__PURE__ */ jsxRuntime.jsx(
19461
+ "path",
19462
+ {
19463
+ d: linePath,
19464
+ fill: "none",
19465
+ stroke: color,
19466
+ strokeWidth: "2",
19467
+ strokeLinecap: "round",
19468
+ strokeLinejoin: "round",
19469
+ strokeDasharray: s.dashed ? "6 4" : void 0
19470
+ }
19471
+ ),
19472
+ points.map((p2, idx) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
19473
+ /* @__PURE__ */ jsxRuntime.jsx(
19474
+ "circle",
19475
+ {
19476
+ cx: p2.x,
19477
+ cy: p2.y,
19478
+ r: "4",
19479
+ fill: "var(--color-card)",
19480
+ stroke: color,
19481
+ strokeWidth: "2",
19482
+ className: "cursor-pointer",
19483
+ onClick: () => onPointClick?.(
19484
+ { label: p2.label, value: p2.value, color },
19485
+ s.name
19486
+ )
19487
+ }
19488
+ ),
19489
+ showValues && series.length === 1 && /* @__PURE__ */ jsxRuntime.jsx(
19490
+ "text",
19491
+ {
19492
+ x: p2.x,
19493
+ y: p2.y - 10,
19494
+ textAnchor: "middle",
19495
+ fill: "var(--color-foreground)",
19496
+ fontSize: "10",
19497
+ fontWeight: "500",
19498
+ children: p2.value
19499
+ }
19500
+ )
19501
+ ] }, idx))
19502
+ ] }, s.name);
19503
+ }),
19504
+ labels.map((label, idx) => /* @__PURE__ */ jsxRuntime.jsx(
19505
+ "text",
19506
+ {
19507
+ x: xFor(idx),
19508
+ y: height - 8,
19509
+ textAnchor: "middle",
19510
+ fill: "var(--color-muted-foreground)",
19511
+ fontSize: "9",
19512
+ children: timeAxis ? formatTimeLabel(label) : label
19513
+ },
19514
+ label
19515
+ ))
19516
+ ]
19517
+ }
19518
+ );
19519
+ };
19520
+ ScatterChart = ({ data, height, onPointClick }) => {
19521
+ const width = 400;
19522
+ const padding = { top: 20, right: 20, bottom: 30, left: 40 };
19523
+ const chartWidth = width - padding.left - padding.right;
19524
+ const chartHeight = height - padding.top - padding.bottom;
19525
+ const { minX, maxX, minY, maxY } = React80.useMemo(() => {
19526
+ if (data.length === 0) {
19527
+ return { minX: 0, maxX: 1, minY: 0, maxY: 1 };
19528
+ }
19529
+ let mnX = data[0].x;
19530
+ let mxX = data[0].x;
19531
+ let mnY = data[0].y;
19532
+ let mxY = data[0].y;
19533
+ for (const p2 of data) {
19534
+ if (p2.x < mnX) mnX = p2.x;
19535
+ if (p2.x > mxX) mxX = p2.x;
19536
+ if (p2.y < mnY) mnY = p2.y;
19537
+ if (p2.y > mxY) mxY = p2.y;
19538
+ }
19539
+ return { minX: mnX, maxX: mxX, minY: mnY, maxY: mxY };
19540
+ }, [data]);
19541
+ const rangeX = maxX - minX || 1;
19542
+ const rangeY = maxY - minY || 1;
19543
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19544
+ "svg",
19545
+ {
19546
+ width: "100%",
19547
+ height,
19548
+ viewBox: `0 0 ${width} ${height}`,
19549
+ preserveAspectRatio: "xMidYMid meet",
19550
+ children: [
19551
+ [0, 0.25, 0.5, 0.75, 1].map((frac) => {
19552
+ const y = padding.top + chartHeight * (1 - frac);
19553
+ return /* @__PURE__ */ jsxRuntime.jsx(
19554
+ "line",
19555
+ {
19556
+ x1: padding.left,
19557
+ y1: y,
19558
+ x2: width - padding.right,
19559
+ y2: y,
19560
+ stroke: "var(--color-border)",
19561
+ strokeDasharray: "4 4",
19562
+ opacity: 0.5
19563
+ },
19564
+ frac
19565
+ );
19566
+ }),
19567
+ data.map((p2, idx) => {
19568
+ const cx = padding.left + (p2.x - minX) / rangeX * chartWidth;
19569
+ const cy = padding.top + chartHeight - (p2.y - minY) / rangeY * chartHeight;
19570
+ const r = p2.size ?? 5;
19571
+ const color = p2.color ?? CHART_COLORS[idx % CHART_COLORS.length];
19572
+ return /* @__PURE__ */ jsxRuntime.jsx(
19573
+ "circle",
19574
+ {
19575
+ cx,
19576
+ cy,
19577
+ r,
19578
+ fill: color,
19579
+ opacity: 0.7,
19580
+ className: "cursor-pointer hover:opacity-100",
19581
+ onClick: () => onPointClick?.(
19582
+ {
19583
+ label: p2.label ?? `(${p2.x}, ${p2.y})`,
19584
+ value: p2.y,
19585
+ color
19586
+ },
19587
+ "default"
19588
+ ),
19589
+ children: /* @__PURE__ */ jsxRuntime.jsx("title", { children: p2.label ?? `(${p2.x}, ${p2.y})` })
19590
+ },
19591
+ idx
19592
+ );
19593
+ }),
19594
+ /* @__PURE__ */ jsxRuntime.jsx(
19595
+ "text",
19596
+ {
19597
+ x: padding.left,
19598
+ y: height - 8,
19599
+ fill: "var(--color-muted-foreground)",
19600
+ fontSize: "9",
19601
+ children: minX.toFixed(1)
19602
+ }
19603
+ ),
19604
+ /* @__PURE__ */ jsxRuntime.jsx(
19605
+ "text",
19606
+ {
19607
+ x: width - padding.right,
19608
+ y: height - 8,
19609
+ textAnchor: "end",
19610
+ fill: "var(--color-muted-foreground)",
19611
+ fontSize: "9",
19612
+ children: maxX.toFixed(1)
19613
+ }
19614
+ )
19615
+ ]
19616
+ }
19617
+ );
19245
19618
  };
19246
19619
  Chart = ({
19247
19620
  title,
@@ -19249,9 +19622,13 @@ var init_Chart = __esm({
19249
19622
  chartType = "bar",
19250
19623
  series,
19251
19624
  data: simpleData,
19625
+ scatterData,
19252
19626
  height = 200,
19253
19627
  showLegend = true,
19254
19628
  showValues = false,
19629
+ stack = "none",
19630
+ timeAxis = false,
19631
+ drillEvent,
19255
19632
  actions,
19256
19633
  entity,
19257
19634
  isLoading = false,
@@ -19268,11 +19645,25 @@ var init_Chart = __esm({
19268
19645
  },
19269
19646
  [eventBus]
19270
19647
  );
19271
- const normalizedData = React80.useMemo(() => {
19272
- if (simpleData) return simpleData;
19273
- if (series && series.length > 0) return series[0].data;
19648
+ const handlePointClick = React80.useCallback(
19649
+ (point, seriesName) => {
19650
+ if (drillEvent) {
19651
+ eventBus.emit(`UI:${drillEvent}`, {
19652
+ label: point.label,
19653
+ value: point.value,
19654
+ seriesLabel: seriesName === "default" ? void 0 : seriesName
19655
+ });
19656
+ }
19657
+ },
19658
+ [drillEvent, eventBus]
19659
+ );
19660
+ const normalizedSeries = React80.useMemo(() => {
19661
+ if (series && series.length > 0) return series;
19662
+ if (simpleData) return [{ name: "default", data: simpleData }];
19274
19663
  return [];
19275
19664
  }, [simpleData, series]);
19665
+ const firstSeriesData = normalizedSeries[0]?.data ?? [];
19666
+ const hasContent = chartType === "scatter" ? (scatterData?.length ?? 0) > 0 : normalizedSeries.some((s) => s.data.length > 0);
19276
19667
  if (isLoading) {
19277
19668
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingState, { message: "Loading chart...", className });
19278
19669
  }
@@ -19286,7 +19677,7 @@ var init_Chart = __esm({
19286
19677
  }
19287
19678
  );
19288
19679
  }
19289
- if (normalizedData.length === 0) {
19680
+ if (!hasContent) {
19290
19681
  return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { title: t("empty.noData"), description: t("empty.noData"), className });
19291
19682
  }
19292
19683
  return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("p-6", className), children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "md", children: [
@@ -19307,18 +19698,84 @@ var init_Chart = __esm({
19307
19698
  )) })
19308
19699
  ] }),
19309
19700
  /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "w-full", children: [
19310
- chartType === "bar" && /* @__PURE__ */ jsxRuntime.jsx(BarChart, { data: normalizedData, height, showValues }),
19311
- chartType === "line" && /* @__PURE__ */ jsxRuntime.jsx(LineChart, { data: normalizedData, height, showValues }),
19312
- chartType === "area" && /* @__PURE__ */ jsxRuntime.jsx(LineChart, { data: normalizedData, height, showValues, fill: true }),
19313
- chartType === "pie" && /* @__PURE__ */ jsxRuntime.jsx(PieChart, { data: normalizedData, height, showValues: showLegend }),
19314
- chartType === "donut" && /* @__PURE__ */ jsxRuntime.jsx(PieChart, { data: normalizedData, height, showValues: showLegend, donut: true })
19701
+ chartType === "bar" && /* @__PURE__ */ jsxRuntime.jsx(
19702
+ BarChart,
19703
+ {
19704
+ series: normalizedSeries,
19705
+ height,
19706
+ showValues,
19707
+ stack,
19708
+ timeAxis,
19709
+ onPointClick: handlePointClick
19710
+ }
19711
+ ),
19712
+ chartType === "histogram" && /* @__PURE__ */ jsxRuntime.jsx(
19713
+ BarChart,
19714
+ {
19715
+ series: normalizedSeries,
19716
+ height,
19717
+ showValues,
19718
+ stack: "none",
19719
+ timeAxis: false,
19720
+ histogram: true,
19721
+ onPointClick: handlePointClick
19722
+ }
19723
+ ),
19724
+ chartType === "line" && /* @__PURE__ */ jsxRuntime.jsx(
19725
+ LineChart,
19726
+ {
19727
+ series: normalizedSeries,
19728
+ height,
19729
+ showValues,
19730
+ timeAxis,
19731
+ onPointClick: handlePointClick
19732
+ }
19733
+ ),
19734
+ chartType === "area" && /* @__PURE__ */ jsxRuntime.jsx(
19735
+ LineChart,
19736
+ {
19737
+ series: normalizedSeries,
19738
+ height,
19739
+ showValues,
19740
+ timeAxis,
19741
+ fill: true,
19742
+ onPointClick: handlePointClick
19743
+ }
19744
+ ),
19745
+ chartType === "pie" && /* @__PURE__ */ jsxRuntime.jsx(
19746
+ PieChart,
19747
+ {
19748
+ data: firstSeriesData,
19749
+ height,
19750
+ showValues: showLegend,
19751
+ onPointClick: handlePointClick
19752
+ }
19753
+ ),
19754
+ chartType === "donut" && /* @__PURE__ */ jsxRuntime.jsx(
19755
+ PieChart,
19756
+ {
19757
+ data: firstSeriesData,
19758
+ height,
19759
+ showValues: showLegend,
19760
+ donut: true,
19761
+ onPointClick: handlePointClick
19762
+ }
19763
+ ),
19764
+ chartType === "scatter" && /* @__PURE__ */ jsxRuntime.jsx(
19765
+ ScatterChart,
19766
+ {
19767
+ data: scatterData ?? [],
19768
+ height,
19769
+ onPointClick: handlePointClick
19770
+ }
19771
+ )
19315
19772
  ] }),
19316
- showLegend && series && series.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "md", justify: "center", wrap: true, children: series.map((s, idx) => /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
19773
+ showLegend && normalizedSeries.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "md", justify: "center", wrap: true, children: normalizedSeries.map((s, idx) => /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
19317
19774
  /* @__PURE__ */ jsxRuntime.jsx(
19318
19775
  Box,
19319
19776
  {
19320
19777
  className: "w-3 h-3 rounded-full flex-shrink-0",
19321
- style: { backgroundColor: s.color || CHART_COLORS[idx % CHART_COLORS.length] }
19778
+ style: { backgroundColor: seriesColor(s, idx) }
19322
19779
  }
19323
19780
  ),
19324
19781
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: s.name })
@@ -21165,7 +21622,6 @@ function useDataDnd(args) {
21165
21622
  const raw = it[dndItemIdField];
21166
21623
  return raw ?? `__idx_${idx}`;
21167
21624
  }),
21168
- // eslint-disable-next-line react-hooks/exhaustive-deps
21169
21625
  [itemIdsSignature]
21170
21626
  );
21171
21627
  const itemsContentSig = items.map((it, idx) => String(it[dndItemIdField] ?? `__${idx}`)).join("|");
@@ -22695,7 +23151,16 @@ var init_FilterGroup = __esm({
22695
23151
  onClear: () => handleFilterSelect(`${filter.field}_to`, null)
22696
23152
  }
22697
23153
  )
22698
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
23154
+ ] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
23155
+ Input,
23156
+ {
23157
+ value: selectedValues[filter.field] || "",
23158
+ onChange: (e) => handleFilterSelect(filter.field, e.target.value || null),
23159
+ placeholder: filter.label,
23160
+ clearable: true,
23161
+ onClear: () => handleFilterSelect(filter.field, null)
23162
+ }
23163
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
22699
23164
  Select,
22700
23165
  {
22701
23166
  value: selectedValues[filter.field] || "all",
@@ -22762,7 +23227,17 @@ var init_FilterGroup = __esm({
22762
23227
  className: "text-sm min-w-[100px]"
22763
23228
  }
22764
23229
  )
22765
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
23230
+ ] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
23231
+ Input,
23232
+ {
23233
+ value: selectedValues[filter.field] || "",
23234
+ onChange: (e) => handleFilterSelect(filter.field, e.target.value || null),
23235
+ placeholder: filter.label,
23236
+ clearable: true,
23237
+ onClear: () => handleFilterSelect(filter.field, null),
23238
+ className: "text-sm"
23239
+ }
23240
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
22766
23241
  Select,
22767
23242
  {
22768
23243
  value: selectedValues[filter.field] || "all",
@@ -22867,7 +23342,17 @@ var init_FilterGroup = __esm({
22867
23342
  className: "min-w-[130px]"
22868
23343
  }
22869
23344
  )
22870
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
23345
+ ] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
23346
+ Input,
23347
+ {
23348
+ value: selectedValues[filter.field] || "",
23349
+ onChange: (e) => handleFilterSelect(filter.field, e.target.value || null),
23350
+ placeholder: filter.label,
23351
+ clearable: true,
23352
+ onClear: () => handleFilterSelect(filter.field, null),
23353
+ className: "min-w-[160px]"
23354
+ }
23355
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
22871
23356
  Select,
22872
23357
  {
22873
23358
  value: selectedValues[filter.field] || "all",
@@ -24156,6 +24641,151 @@ var init_FlipCard = __esm({
24156
24641
  FlipCard.displayName = "FlipCard";
24157
24642
  }
24158
24643
  });
24644
+ function toISODate(d) {
24645
+ return d.toISOString().slice(0, 10);
24646
+ }
24647
+ function startOfMonth(d) {
24648
+ return new Date(d.getFullYear(), d.getMonth(), 1);
24649
+ }
24650
+ function startOfQuarter(d) {
24651
+ return new Date(d.getFullYear(), Math.floor(d.getMonth() / 3) * 3, 1);
24652
+ }
24653
+ function startOfYear(d) {
24654
+ return new Date(d.getFullYear(), 0, 1);
24655
+ }
24656
+ function daysAgo(n) {
24657
+ const d = /* @__PURE__ */ new Date();
24658
+ d.setDate(d.getDate() - n);
24659
+ return d;
24660
+ }
24661
+ var DEFAULT_PRESETS, DateRangePicker;
24662
+ var init_DateRangePicker = __esm({
24663
+ "components/molecules/DateRangePicker.tsx"() {
24664
+ "use client";
24665
+ init_cn();
24666
+ init_Button();
24667
+ init_Input();
24668
+ init_Stack();
24669
+ init_Typography();
24670
+ init_useEventBus();
24671
+ DEFAULT_PRESETS = [
24672
+ {
24673
+ label: "Last 7 days",
24674
+ value: "7d",
24675
+ range: () => ({ from: toISODate(daysAgo(7)), to: toISODate(/* @__PURE__ */ new Date()) })
24676
+ },
24677
+ {
24678
+ label: "Last 30 days",
24679
+ value: "30d",
24680
+ range: () => ({ from: toISODate(daysAgo(30)), to: toISODate(/* @__PURE__ */ new Date()) })
24681
+ },
24682
+ {
24683
+ label: "This Month",
24684
+ value: "month",
24685
+ range: () => ({ from: toISODate(startOfMonth(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
24686
+ },
24687
+ {
24688
+ label: "This Quarter",
24689
+ value: "quarter",
24690
+ range: () => ({ from: toISODate(startOfQuarter(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
24691
+ },
24692
+ {
24693
+ label: "YTD",
24694
+ value: "ytd",
24695
+ range: () => ({ from: toISODate(startOfYear(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
24696
+ }
24697
+ ];
24698
+ DateRangePicker = ({
24699
+ from: fromProp,
24700
+ to: toProp,
24701
+ event,
24702
+ onChange,
24703
+ presets = DEFAULT_PRESETS,
24704
+ fromLabel = "From",
24705
+ toLabel = "To",
24706
+ className
24707
+ }) => {
24708
+ const eventBus = useEventBus();
24709
+ const [from, setFrom] = React80.useState(fromProp ?? "");
24710
+ const [to, setTo] = React80.useState(toProp ?? "");
24711
+ const [activePreset, setActivePreset] = React80.useState(null);
24712
+ const emit = React80.useCallback(
24713
+ (range) => {
24714
+ onChange?.(range);
24715
+ if (event) eventBus.emit(`UI:${event}`, range);
24716
+ },
24717
+ [onChange, event, eventBus]
24718
+ );
24719
+ const handleFromChange = React80.useCallback(
24720
+ (next) => {
24721
+ setFrom(next);
24722
+ setActivePreset(null);
24723
+ emit({ from: next, to });
24724
+ },
24725
+ [to, emit]
24726
+ );
24727
+ const handleToChange = React80.useCallback(
24728
+ (next) => {
24729
+ setTo(next);
24730
+ setActivePreset(null);
24731
+ emit({ from, to: next });
24732
+ },
24733
+ [from, emit]
24734
+ );
24735
+ const handlePreset = React80.useCallback(
24736
+ (preset) => {
24737
+ const range = preset.range();
24738
+ setFrom(range.from);
24739
+ setTo(range.to);
24740
+ setActivePreset(preset.value);
24741
+ emit(range);
24742
+ },
24743
+ [emit]
24744
+ );
24745
+ const presetButtons = React80.useMemo(
24746
+ () => presets.map((preset) => /* @__PURE__ */ jsxRuntime.jsx(
24747
+ Button,
24748
+ {
24749
+ variant: activePreset === preset.value ? "primary" : "ghost",
24750
+ size: "sm",
24751
+ onClick: () => handlePreset(preset),
24752
+ children: preset.label
24753
+ },
24754
+ preset.value
24755
+ )),
24756
+ [presets, activePreset, handlePreset]
24757
+ );
24758
+ return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", className: cn(className), children: [
24759
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "md", align: "end", children: [
24760
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
24761
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: fromLabel }),
24762
+ /* @__PURE__ */ jsxRuntime.jsx(
24763
+ Input,
24764
+ {
24765
+ type: "date",
24766
+ value: from,
24767
+ onChange: (e) => handleFromChange(e.target.value)
24768
+ }
24769
+ )
24770
+ ] }),
24771
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
24772
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: toLabel }),
24773
+ /* @__PURE__ */ jsxRuntime.jsx(
24774
+ Input,
24775
+ {
24776
+ type: "date",
24777
+ value: to,
24778
+ onChange: (e) => handleToChange(e.target.value)
24779
+ }
24780
+ )
24781
+ ] })
24782
+ ] }),
24783
+ presets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", wrap: true, children: presetButtons })
24784
+ ] });
24785
+ };
24786
+ DateRangePicker.displayName = "DateRangePicker";
24787
+ }
24788
+ });
24159
24789
  var DEFAULT_OPTIONS, DateRangeSelector;
24160
24790
  var init_DateRangeSelector = __esm({
24161
24791
  "components/molecules/DateRangeSelector.tsx"() {
@@ -27127,7 +27757,9 @@ var init_StatDisplay = __esm({
27127
27757
  init_Typography();
27128
27758
  init_Box();
27129
27759
  init_Stack();
27760
+ init_Sparkline();
27130
27761
  init_Icon();
27762
+ init_useEventBus();
27131
27763
  variantColor = {
27132
27764
  default: "text-foreground",
27133
27765
  primary: "text-primary",
@@ -27142,6 +27774,10 @@ var init_StatDisplay = __esm({
27142
27774
  max,
27143
27775
  target,
27144
27776
  trend,
27777
+ trendPolarity = "higher-is-better",
27778
+ trendFormat = "absolute",
27779
+ sparklineData,
27780
+ clickEvent,
27145
27781
  prefix,
27146
27782
  suffix,
27147
27783
  icon: iconProp,
@@ -27155,6 +27791,10 @@ var init_StatDisplay = __esm({
27155
27791
  isLoading = false,
27156
27792
  error = null
27157
27793
  }) => {
27794
+ const eventBus = useEventBus();
27795
+ const handleClick = React80.useCallback(() => {
27796
+ if (clickEvent) eventBus.emit(`UI:${clickEvent}`, { metricLabel: label });
27797
+ }, [clickEvent, eventBus, label]);
27158
27798
  const ResolvedIcon = typeof iconProp === "string" ? resolveIcon(iconProp) : null;
27159
27799
  const iconSizes3 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
27160
27800
  const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
@@ -27165,7 +27805,10 @@ var init_StatDisplay = __esm({
27165
27805
  const targetPct = showTarget ? Math.max(0, Math.min(100, numericValue / target * 100)) : 0;
27166
27806
  const showTrend = typeof trend === "number" && trend !== 0 && Number.isFinite(trend);
27167
27807
  const trendUp = showTrend && trend > 0;
27168
- const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${Math.abs(trend)}` : "";
27808
+ const trendIsGood = trendPolarity === "lower-is-better" ? !trendUp : trendUp;
27809
+ const trendMagnitude = Math.abs(trend);
27810
+ const trendSuffix = trendFormat === "percent" ? "%" : "";
27811
+ const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${trendMagnitude}${trendSuffix}` : "";
27169
27812
  if (error) {
27170
27813
  return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "error", children: error.message }) });
27171
27814
  }
@@ -27176,38 +27819,57 @@ var init_StatDisplay = __esm({
27176
27819
  ] }) });
27177
27820
  }
27178
27821
  if (compact) {
27179
- return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: cn("items-center", className), children: [
27180
- ResolvedIcon && /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }),
27181
- typeof iconProp !== "string" && iconProp,
27182
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: label }),
27183
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27184
- showTrend && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: cn("font-semibold", trendUp ? "text-success" : "text-error"), children: trendLabel })
27185
- ] });
27822
+ return /* @__PURE__ */ jsxRuntime.jsxs(
27823
+ HStack,
27824
+ {
27825
+ gap: "sm",
27826
+ className: cn("items-center", clickEvent && "cursor-pointer hover:opacity-80", className),
27827
+ onClick: clickEvent ? handleClick : void 0,
27828
+ children: [
27829
+ ResolvedIcon && /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }),
27830
+ typeof iconProp !== "string" && iconProp,
27831
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: label }),
27832
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27833
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: cn("font-semibold", trendIsGood ? "text-success" : "text-error"), children: trendLabel }),
27834
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Sparkline, { data: sparklineData, color: "auto", width: 60, height: 20 })
27835
+ ]
27836
+ }
27837
+ );
27186
27838
  }
27187
- return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "start", justify: "between", children: [
27188
- /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "space-y-1 flex-1", children: [
27189
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "overline", color: "secondary", children: label }),
27190
- /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "end", children: [
27191
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27192
- showTrend && /* @__PURE__ */ jsxRuntime.jsx(
27193
- Typography,
27194
- {
27195
- variant: "caption",
27196
- className: cn("font-semibold pb-1", trendUp ? "text-success" : "text-error"),
27197
- children: trendLabel
27198
- }
27199
- )
27200
- ] }),
27201
- showTarget && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
27202
- Box,
27203
- {
27204
- className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
27205
- style: { width: `${targetPct}%` }
27206
- }
27207
- ) })
27208
- ] }),
27209
- (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }) : iconProp })
27210
- ] }) });
27839
+ return /* @__PURE__ */ jsxRuntime.jsx(
27840
+ Card,
27841
+ {
27842
+ className: cn(padSizes[size], clickEvent && "cursor-pointer hover:shadow-md transition-shadow", className),
27843
+ onClick: clickEvent ? handleClick : void 0,
27844
+ children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "start", justify: "between", children: [
27845
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "space-y-1 flex-1", children: [
27846
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "overline", color: "secondary", children: label }),
27847
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "end", children: [
27848
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27849
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(
27850
+ Typography,
27851
+ {
27852
+ variant: "caption",
27853
+ className: cn("font-semibold pb-1", trendIsGood ? "text-success" : "text-error"),
27854
+ children: trendLabel
27855
+ }
27856
+ )
27857
+ ] }),
27858
+ showTarget && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
27859
+ Box,
27860
+ {
27861
+ className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
27862
+ style: { width: `${targetPct}%` }
27863
+ }
27864
+ ) })
27865
+ ] }),
27866
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", align: "end", children: [
27867
+ (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }) : iconProp }),
27868
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Sparkline, { data: sparklineData, color: "auto" })
27869
+ ] })
27870
+ ] })
27871
+ }
27872
+ );
27211
27873
  };
27212
27874
  StatDisplay.displayName = "StatDisplay";
27213
27875
  }
@@ -41551,6 +42213,7 @@ var init_StatCard = __esm({
41551
42213
  init_Box();
41552
42214
  init_Stack();
41553
42215
  init_Button();
42216
+ init_Sparkline();
41554
42217
  init_useEventBus();
41555
42218
  init_useTranslate();
41556
42219
  init_Icon();
@@ -41720,32 +42383,7 @@ var init_StatCard = __esm({
41720
42383
  ] }),
41721
42384
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", align: "end", children: [
41722
42385
  Icon3 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsxRuntime.jsx(Icon3, { className: cn("h-6 w-6", iconColor) }) }),
41723
- sparklineData && sparklineData.length > 1 && (() => {
41724
- const w = 80;
41725
- const h = 32;
41726
- const pad = 2;
41727
- const min = Math.min(...sparklineData);
41728
- const max = Math.max(...sparklineData);
41729
- const range = max - min || 1;
41730
- const points = sparklineData.map((v, i) => {
41731
- const x = pad + i / (sparklineData.length - 1) * (w - pad * 2);
41732
- const y = pad + (1 - (v - min) / range) * (h - pad * 2);
41733
- return `${x},${y}`;
41734
- }).join(" ");
41735
- const trending = sparklineData[sparklineData.length - 1] >= sparklineData[0];
41736
- const strokeColor = trending ? "var(--color-success)" : "var(--color-error)";
41737
- return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: w, height: h, viewBox: `0 0 ${w} ${h}`, className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
41738
- "polyline",
41739
- {
41740
- fill: "none",
41741
- stroke: strokeColor,
41742
- strokeWidth: "2",
41743
- strokeLinecap: "round",
41744
- strokeLinejoin: "round",
41745
- points
41746
- }
41747
- ) });
41748
- })()
42386
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Sparkline, { data: sparklineData, color: "auto" })
41749
42387
  ] })
41750
42388
  ] }),
41751
42389
  action && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -43643,6 +44281,7 @@ var init_component_registry_generated = __esm({
43643
44281
  init_DataGrid();
43644
44282
  init_DataList();
43645
44283
  init_DataTable();
44284
+ init_DateRangePicker();
43646
44285
  init_DateRangeSelector();
43647
44286
  init_DayCell();
43648
44287
  init_DebuggerBoard();
@@ -43775,6 +44414,7 @@ var init_component_registry_generated = __esm({
43775
44414
  init_Skeleton();
43776
44415
  init_SocialProof();
43777
44416
  init_SortableList();
44417
+ init_Sparkline();
43778
44418
  init_Split();
43779
44419
  init_SplitPane();
43780
44420
  init_SplitSection();
@@ -43923,6 +44563,7 @@ var init_component_registry_generated = __esm({
43923
44563
  "DataGrid": DataGrid,
43924
44564
  "DataList": DataList,
43925
44565
  "DataTable": DataTable,
44566
+ "DateRangePicker": DateRangePicker,
43926
44567
  "DateRangeSelector": DateRangeSelector,
43927
44568
  "DayCell": DayCell,
43928
44569
  "DebuggerBoard": DebuggerBoard,
@@ -44084,6 +44725,7 @@ var init_component_registry_generated = __esm({
44084
44725
  "SortableList": SortableList,
44085
44726
  "Spacer": SpacerPattern,
44086
44727
  "SpacerPattern": SpacerPattern,
44728
+ "Sparkline": Sparkline,
44087
44729
  "Spinner": SpinnerPattern,
44088
44730
  "SpinnerPattern": SpinnerPattern,
44089
44731
  "Split": Split,