@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.
@@ -2681,6 +2681,67 @@ var init_Radio = __esm({
2681
2681
  exports.Radio.displayName = "Radio";
2682
2682
  }
2683
2683
  });
2684
+ var COLOR_VAR; exports.Sparkline = void 0;
2685
+ var init_Sparkline = __esm({
2686
+ "components/atoms/Sparkline.tsx"() {
2687
+ init_cn();
2688
+ COLOR_VAR = {
2689
+ primary: "var(--color-primary)",
2690
+ success: "var(--color-success)",
2691
+ warning: "var(--color-warning)",
2692
+ error: "var(--color-error)",
2693
+ info: "var(--color-info)",
2694
+ muted: "var(--color-muted-foreground)"
2695
+ };
2696
+ exports.Sparkline = ({
2697
+ data,
2698
+ color = "auto",
2699
+ width = 80,
2700
+ height = 32,
2701
+ strokeWidth = 2,
2702
+ fill = false,
2703
+ className
2704
+ }) => {
2705
+ if (data.length < 2) return null;
2706
+ const pad = 2;
2707
+ const min = Math.min(...data);
2708
+ const max = Math.max(...data);
2709
+ const range = max - min || 1;
2710
+ const points = data.map((v, i) => {
2711
+ const x = pad + i / (data.length - 1) * (width - pad * 2);
2712
+ const y = pad + (1 - (v - min) / range) * (height - pad * 2);
2713
+ return `${x},${y}`;
2714
+ }).join(" ");
2715
+ const resolvedColor = color === "auto" ? data[data.length - 1] >= data[0] ? COLOR_VAR.success : COLOR_VAR.error : COLOR_VAR[color];
2716
+ const areaPath = fill ? `M ${pad},${height - pad} L ${points.split(" ").join(" L ")} L ${width - pad},${height - pad} Z` : null;
2717
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2718
+ "svg",
2719
+ {
2720
+ width,
2721
+ height,
2722
+ viewBox: `0 0 ${width} ${height}`,
2723
+ className: cn("flex-shrink-0", className),
2724
+ "aria-hidden": "true",
2725
+ children: [
2726
+ areaPath && /* @__PURE__ */ jsxRuntime.jsx("path", { d: areaPath, fill: resolvedColor, opacity: 0.15 }),
2727
+ /* @__PURE__ */ jsxRuntime.jsx(
2728
+ "polyline",
2729
+ {
2730
+ fill: "none",
2731
+ stroke: resolvedColor,
2732
+ strokeWidth,
2733
+ strokeLinecap: "round",
2734
+ strokeLinejoin: "round",
2735
+ points
2736
+ }
2737
+ )
2738
+ ]
2739
+ }
2740
+ );
2741
+ };
2742
+ exports.Sparkline.displayName = "Sparkline";
2743
+ }
2744
+ });
2684
2745
  exports.Switch = void 0;
2685
2746
  var init_Switch = __esm({
2686
2747
  "components/atoms/Switch.tsx"() {
@@ -7754,7 +7815,7 @@ var init_MapView = __esm({
7754
7815
  shadowSize: [41, 41]
7755
7816
  });
7756
7817
  L.Marker.prototype.options.icon = defaultIcon;
7757
- const { useEffect: useEffect70, useRef: useRef66, useCallback: useCallback124, useState: useState106 } = React75__namespace.default;
7818
+ const { useEffect: useEffect70, useRef: useRef66, useCallback: useCallback126, useState: useState107 } = React75__namespace.default;
7758
7819
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
7759
7820
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
7760
7821
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -7799,8 +7860,8 @@ var init_MapView = __esm({
7799
7860
  showAttribution = true
7800
7861
  }) {
7801
7862
  const eventBus = useEventBus2();
7802
- const [clickedPosition, setClickedPosition] = useState106(null);
7803
- const handleMapClick = useCallback124((lat, lng) => {
7863
+ const [clickedPosition, setClickedPosition] = useState107(null);
7864
+ const handleMapClick = useCallback126((lat, lng) => {
7804
7865
  if (showClickedPin) {
7805
7866
  setClickedPosition({ lat, lng });
7806
7867
  }
@@ -7809,7 +7870,7 @@ var init_MapView = __esm({
7809
7870
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
7810
7871
  }
7811
7872
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
7812
- const handleMarkerClick = useCallback124((marker) => {
7873
+ const handleMarkerClick = useCallback126((marker) => {
7813
7874
  onMarkerClick?.(marker);
7814
7875
  if (markerClickEvent) {
7815
7876
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -17801,7 +17862,7 @@ var init_CastleTemplate = __esm({
17801
17862
  CastleTemplate.displayName = "CastleTemplate";
17802
17863
  }
17803
17864
  });
17804
- var CHART_COLORS, BarChart, PieChart, LineChart; exports.Chart = void 0;
17865
+ var CHART_COLORS, seriesColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart; exports.Chart = void 0;
17805
17866
  var init_Chart = __esm({
17806
17867
  "components/organisms/Chart.tsx"() {
17807
17868
  "use client";
@@ -17821,38 +17882,159 @@ var init_Chart = __esm({
17821
17882
  "var(--color-info)",
17822
17883
  "var(--color-accent)"
17823
17884
  ];
17824
- BarChart = ({ data, height, showValues }) => {
17825
- const maxValue = Math.max(...data.map((d) => d.value), 1);
17826
- return /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "xs", align: "end", className: "w-full", style: { height }, children: data.map((point, idx) => {
17827
- const barHeight = point.value / maxValue * 100;
17828
- const color = point.color || CHART_COLORS[idx % CHART_COLORS.length];
17829
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", align: "center", flex: true, className: "min-w-0", children: [
17830
- showValues && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: point.value }),
17831
- /* @__PURE__ */ jsxRuntime.jsx(
17832
- exports.Box,
17833
- {
17834
- className: cn(
17835
- "w-full rounded-t-sm transition-all duration-500 ease-out min-h-[4px]"
17836
- ),
17837
- style: {
17838
- height: `${barHeight}%`,
17839
- backgroundColor: color
17840
- }
17885
+ seriesColor = (series, idx) => series.color ?? CHART_COLORS[idx % CHART_COLORS.length];
17886
+ monthFormatter = new Intl.DateTimeFormat(void 0, {
17887
+ month: "short",
17888
+ year: "2-digit"
17889
+ });
17890
+ formatTimeLabel = (raw) => {
17891
+ const parsed = new Date(raw);
17892
+ if (Number.isNaN(parsed.getTime())) return raw;
17893
+ return monthFormatter.format(parsed);
17894
+ };
17895
+ BarChart = ({ series, height, showValues, stack, timeAxis, histogram = false, onPointClick }) => {
17896
+ const categories = React75.useMemo(() => {
17897
+ const set = [];
17898
+ const seen = /* @__PURE__ */ new Set();
17899
+ for (const s of series) {
17900
+ for (const p2 of s.data) {
17901
+ if (!seen.has(p2.label)) {
17902
+ seen.add(p2.label);
17903
+ set.push(p2.label);
17841
17904
  }
17842
- ),
17843
- /* @__PURE__ */ jsxRuntime.jsx(
17844
- exports.Typography,
17845
- {
17846
- variant: "caption",
17847
- color: "secondary",
17848
- className: "truncate w-full text-center",
17849
- children: point.label
17905
+ }
17906
+ }
17907
+ return set;
17908
+ }, [series]);
17909
+ const valueAt = React75.useCallback(
17910
+ (s, label) => {
17911
+ const p2 = s.data.find((d) => d.label === label);
17912
+ return p2 ? p2.value : 0;
17913
+ },
17914
+ []
17915
+ );
17916
+ const columnTotals = React75.useMemo(() => {
17917
+ if (stack === "none") return null;
17918
+ return categories.map(
17919
+ (label) => series.reduce((sum, s) => sum + valueAt(s, label), 0)
17920
+ );
17921
+ }, [categories, series, stack, valueAt]);
17922
+ const maxValue = React75.useMemo(() => {
17923
+ if (stack === "normalize") return 100;
17924
+ if (stack === "stack" && columnTotals) {
17925
+ return Math.max(...columnTotals, 1);
17926
+ }
17927
+ let m = 1;
17928
+ for (const s of series) {
17929
+ for (const p2 of s.data) if (p2.value > m) m = p2.value;
17930
+ }
17931
+ return m;
17932
+ }, [series, stack, columnTotals]);
17933
+ return /* @__PURE__ */ jsxRuntime.jsx(
17934
+ exports.HStack,
17935
+ {
17936
+ gap: histogram ? "none" : "xs",
17937
+ align: "end",
17938
+ className: "w-full",
17939
+ style: { height },
17940
+ children: categories.map((label, catIdx) => {
17941
+ const displayLabel = timeAxis ? formatTimeLabel(label) : label;
17942
+ if (stack === "none") {
17943
+ return /* @__PURE__ */ jsxRuntime.jsxs(
17944
+ exports.VStack,
17945
+ {
17946
+ gap: "xs",
17947
+ align: "center",
17948
+ flex: true,
17949
+ className: "min-w-0",
17950
+ children: [
17951
+ /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: histogram ? "none" : "xs", align: "end", className: "w-full", style: { height: "100%" }, children: series.map((s, sIdx) => {
17952
+ const value = valueAt(s, label);
17953
+ const barHeight = value / maxValue * 100;
17954
+ const color = seriesColor(s, sIdx);
17955
+ return /* @__PURE__ */ jsxRuntime.jsx(
17956
+ exports.Box,
17957
+ {
17958
+ className: cn(
17959
+ "rounded-t-sm transition-all duration-500 ease-out min-h-[4px] cursor-pointer hover:opacity-80",
17960
+ histogram ? "flex-1 mx-0" : "flex-1"
17961
+ ),
17962
+ style: {
17963
+ height: `${barHeight}%`,
17964
+ backgroundColor: color
17965
+ },
17966
+ onClick: () => onPointClick?.(
17967
+ { label, value, color },
17968
+ s.name
17969
+ ),
17970
+ title: `${s.name}: ${value}`
17971
+ },
17972
+ s.name
17973
+ );
17974
+ }) }),
17975
+ showValues && series.length === 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: valueAt(series[0], label) }),
17976
+ /* @__PURE__ */ jsxRuntime.jsx(
17977
+ exports.Typography,
17978
+ {
17979
+ variant: "caption",
17980
+ color: "secondary",
17981
+ className: "truncate w-full text-center",
17982
+ children: displayLabel
17983
+ }
17984
+ )
17985
+ ]
17986
+ },
17987
+ label
17988
+ );
17850
17989
  }
17851
- )
17852
- ] }, point.label);
17853
- }) });
17990
+ const total = columnTotals?.[catIdx] ?? 1;
17991
+ return /* @__PURE__ */ jsxRuntime.jsxs(
17992
+ exports.VStack,
17993
+ {
17994
+ gap: "xs",
17995
+ align: "center",
17996
+ flex: true,
17997
+ className: "min-w-0",
17998
+ children: [
17999
+ /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "none", className: "w-full", style: { height: "100%" }, justify: "end", children: series.map((s, sIdx) => {
18000
+ const value = valueAt(s, label);
18001
+ const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
18002
+ const color = seriesColor(s, sIdx);
18003
+ return /* @__PURE__ */ jsxRuntime.jsx(
18004
+ exports.Box,
18005
+ {
18006
+ className: "w-full transition-all duration-500 ease-out cursor-pointer hover:opacity-80",
18007
+ style: {
18008
+ height: `${ratio}%`,
18009
+ backgroundColor: color
18010
+ },
18011
+ onClick: () => onPointClick?.(
18012
+ { label, value, color },
18013
+ s.name
18014
+ ),
18015
+ title: `${s.name}: ${value}`
18016
+ },
18017
+ s.name
18018
+ );
18019
+ }) }),
18020
+ /* @__PURE__ */ jsxRuntime.jsx(
18021
+ exports.Typography,
18022
+ {
18023
+ variant: "caption",
18024
+ color: "secondary",
18025
+ className: "truncate w-full text-center",
18026
+ children: displayLabel
18027
+ }
18028
+ )
18029
+ ]
18030
+ },
18031
+ label
18032
+ );
18033
+ })
18034
+ }
18035
+ );
17854
18036
  };
17855
- PieChart = ({ data, height, showValues, donut = false }) => {
18037
+ PieChart = ({ data, height, showValues, donut = false, onPointClick }) => {
17856
18038
  const total = data.reduce((sum, d) => sum + d.value, 0);
17857
18039
  const size = Math.min(height, 200);
17858
18040
  const radius = size / 2 - 8;
@@ -17898,7 +18080,11 @@ var init_Chart = __esm({
17898
18080
  fill: seg.color,
17899
18081
  stroke: "var(--color-card)",
17900
18082
  strokeWidth: "2",
17901
- className: "transition-opacity duration-200 hover:opacity-80"
18083
+ className: "transition-opacity duration-200 hover:opacity-80 cursor-pointer",
18084
+ onClick: () => onPointClick?.(
18085
+ { label: seg.label, value: seg.value, color: seg.color },
18086
+ "default"
18087
+ )
17902
18088
  },
17903
18089
  idx
17904
18090
  )),
@@ -17933,56 +18119,243 @@ var init_Chart = __esm({
17933
18119
  ] }, idx)) })
17934
18120
  ] });
17935
18121
  };
17936
- LineChart = ({ data, height, showValues, fill = false }) => {
17937
- const maxValue = Math.max(...data.map((d) => d.value), 1);
18122
+ LineChart = ({ series, height, showValues, fill = false, timeAxis, onPointClick }) => {
17938
18123
  const width = 400;
17939
18124
  const padding = { top: 20, right: 20, bottom: 30, left: 40 };
17940
18125
  const chartWidth = width - padding.left - padding.right;
17941
18126
  const chartHeight = height - padding.top - padding.bottom;
17942
- const points = React75.useMemo(() => {
17943
- return data.map((point, idx) => ({
17944
- x: padding.left + idx / Math.max(data.length - 1, 1) * chartWidth,
17945
- y: padding.top + chartHeight - point.value / maxValue * chartHeight,
17946
- ...point
17947
- }));
17948
- }, [data, maxValue, chartWidth, chartHeight, padding]);
17949
- const linePath = points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
17950
- const areaPath = `${linePath} L ${points[points.length - 1]?.x ?? 0} ${padding.top + chartHeight} L ${padding.left} ${padding.top + chartHeight} Z`;
17951
- return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "100%", height, viewBox: `0 0 ${width} ${height}`, preserveAspectRatio: "xMidYMid meet", children: [
17952
- [0, 0.25, 0.5, 0.75, 1].map((frac) => {
17953
- const y = padding.top + chartHeight * (1 - frac);
17954
- return /* @__PURE__ */ jsxRuntime.jsx(
17955
- "line",
17956
- {
17957
- x1: padding.left,
17958
- y1: y,
17959
- x2: width - padding.right,
17960
- y2: y,
17961
- stroke: "var(--color-border)",
17962
- strokeDasharray: "4 4",
17963
- opacity: 0.5
17964
- },
17965
- frac
17966
- );
17967
- }),
17968
- fill && /* @__PURE__ */ jsxRuntime.jsx("path", { d: areaPath, fill: "var(--color-primary)", opacity: 0.1 }),
17969
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: linePath, fill: "none", stroke: "var(--color-primary)", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
17970
- points.map((p2, idx) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
17971
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: p2.x, cy: p2.y, r: "4", fill: "var(--color-card)", stroke: "var(--color-primary)", strokeWidth: "2" }),
17972
- 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 }),
17973
- /* @__PURE__ */ jsxRuntime.jsx(
17974
- "text",
17975
- {
17976
- x: p2.x,
17977
- y: height - 8,
17978
- textAnchor: "middle",
17979
- fill: "var(--color-muted-foreground)",
17980
- fontSize: "9",
17981
- children: p2.label
18127
+ const labels = React75.useMemo(() => {
18128
+ const seen = /* @__PURE__ */ new Set();
18129
+ const out = [];
18130
+ for (const s of series) {
18131
+ for (const p2 of s.data) {
18132
+ if (!seen.has(p2.label)) {
18133
+ seen.add(p2.label);
18134
+ out.push(p2.label);
17982
18135
  }
17983
- )
17984
- ] }, idx))
17985
- ] });
18136
+ }
18137
+ }
18138
+ return out;
18139
+ }, [series]);
18140
+ const maxValue = React75.useMemo(() => {
18141
+ let m = 1;
18142
+ for (const s of series) {
18143
+ for (const p2 of s.data) if (p2.value > m) m = p2.value;
18144
+ }
18145
+ return m;
18146
+ }, [series]);
18147
+ const xFor = React75.useCallback(
18148
+ (idx) => padding.left + idx / Math.max(labels.length - 1, 1) * chartWidth,
18149
+ [labels.length, chartWidth, padding.left]
18150
+ );
18151
+ const yFor = React75.useCallback(
18152
+ (value) => padding.top + chartHeight - value / maxValue * chartHeight,
18153
+ [maxValue, chartHeight, padding.top]
18154
+ );
18155
+ return /* @__PURE__ */ jsxRuntime.jsxs(
18156
+ "svg",
18157
+ {
18158
+ width: "100%",
18159
+ height,
18160
+ viewBox: `0 0 ${width} ${height}`,
18161
+ preserveAspectRatio: "xMidYMid meet",
18162
+ children: [
18163
+ [0, 0.25, 0.5, 0.75, 1].map((frac) => {
18164
+ const y = padding.top + chartHeight * (1 - frac);
18165
+ return /* @__PURE__ */ jsxRuntime.jsx(
18166
+ "line",
18167
+ {
18168
+ x1: padding.left,
18169
+ y1: y,
18170
+ x2: width - padding.right,
18171
+ y2: y,
18172
+ stroke: "var(--color-border)",
18173
+ strokeDasharray: "4 4",
18174
+ opacity: 0.5
18175
+ },
18176
+ frac
18177
+ );
18178
+ }),
18179
+ series.map((s, sIdx) => {
18180
+ const color = seriesColor(s, sIdx);
18181
+ const points = labels.map((label, idx) => {
18182
+ const point = s.data.find((d) => d.label === label);
18183
+ return {
18184
+ x: xFor(idx),
18185
+ y: yFor(point ? point.value : 0),
18186
+ value: point ? point.value : 0,
18187
+ label
18188
+ };
18189
+ });
18190
+ const linePath = points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
18191
+ const areaPath = `${linePath} L ${points[points.length - 1]?.x ?? 0} ${padding.top + chartHeight} L ${padding.left} ${padding.top + chartHeight} Z`;
18192
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
18193
+ fill && /* @__PURE__ */ jsxRuntime.jsx(
18194
+ "path",
18195
+ {
18196
+ d: areaPath,
18197
+ fill: color,
18198
+ opacity: series.length > 1 ? 0.08 : 0.1
18199
+ }
18200
+ ),
18201
+ /* @__PURE__ */ jsxRuntime.jsx(
18202
+ "path",
18203
+ {
18204
+ d: linePath,
18205
+ fill: "none",
18206
+ stroke: color,
18207
+ strokeWidth: "2",
18208
+ strokeLinecap: "round",
18209
+ strokeLinejoin: "round",
18210
+ strokeDasharray: s.dashed ? "6 4" : void 0
18211
+ }
18212
+ ),
18213
+ points.map((p2, idx) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
18214
+ /* @__PURE__ */ jsxRuntime.jsx(
18215
+ "circle",
18216
+ {
18217
+ cx: p2.x,
18218
+ cy: p2.y,
18219
+ r: "4",
18220
+ fill: "var(--color-card)",
18221
+ stroke: color,
18222
+ strokeWidth: "2",
18223
+ className: "cursor-pointer",
18224
+ onClick: () => onPointClick?.(
18225
+ { label: p2.label, value: p2.value, color },
18226
+ s.name
18227
+ )
18228
+ }
18229
+ ),
18230
+ showValues && series.length === 1 && /* @__PURE__ */ jsxRuntime.jsx(
18231
+ "text",
18232
+ {
18233
+ x: p2.x,
18234
+ y: p2.y - 10,
18235
+ textAnchor: "middle",
18236
+ fill: "var(--color-foreground)",
18237
+ fontSize: "10",
18238
+ fontWeight: "500",
18239
+ children: p2.value
18240
+ }
18241
+ )
18242
+ ] }, idx))
18243
+ ] }, s.name);
18244
+ }),
18245
+ labels.map((label, idx) => /* @__PURE__ */ jsxRuntime.jsx(
18246
+ "text",
18247
+ {
18248
+ x: xFor(idx),
18249
+ y: height - 8,
18250
+ textAnchor: "middle",
18251
+ fill: "var(--color-muted-foreground)",
18252
+ fontSize: "9",
18253
+ children: timeAxis ? formatTimeLabel(label) : label
18254
+ },
18255
+ label
18256
+ ))
18257
+ ]
18258
+ }
18259
+ );
18260
+ };
18261
+ ScatterChart = ({ data, height, onPointClick }) => {
18262
+ const width = 400;
18263
+ const padding = { top: 20, right: 20, bottom: 30, left: 40 };
18264
+ const chartWidth = width - padding.left - padding.right;
18265
+ const chartHeight = height - padding.top - padding.bottom;
18266
+ const { minX, maxX, minY, maxY } = React75.useMemo(() => {
18267
+ if (data.length === 0) {
18268
+ return { minX: 0, maxX: 1, minY: 0, maxY: 1 };
18269
+ }
18270
+ let mnX = data[0].x;
18271
+ let mxX = data[0].x;
18272
+ let mnY = data[0].y;
18273
+ let mxY = data[0].y;
18274
+ for (const p2 of data) {
18275
+ if (p2.x < mnX) mnX = p2.x;
18276
+ if (p2.x > mxX) mxX = p2.x;
18277
+ if (p2.y < mnY) mnY = p2.y;
18278
+ if (p2.y > mxY) mxY = p2.y;
18279
+ }
18280
+ return { minX: mnX, maxX: mxX, minY: mnY, maxY: mxY };
18281
+ }, [data]);
18282
+ const rangeX = maxX - minX || 1;
18283
+ const rangeY = maxY - minY || 1;
18284
+ return /* @__PURE__ */ jsxRuntime.jsxs(
18285
+ "svg",
18286
+ {
18287
+ width: "100%",
18288
+ height,
18289
+ viewBox: `0 0 ${width} ${height}`,
18290
+ preserveAspectRatio: "xMidYMid meet",
18291
+ children: [
18292
+ [0, 0.25, 0.5, 0.75, 1].map((frac) => {
18293
+ const y = padding.top + chartHeight * (1 - frac);
18294
+ return /* @__PURE__ */ jsxRuntime.jsx(
18295
+ "line",
18296
+ {
18297
+ x1: padding.left,
18298
+ y1: y,
18299
+ x2: width - padding.right,
18300
+ y2: y,
18301
+ stroke: "var(--color-border)",
18302
+ strokeDasharray: "4 4",
18303
+ opacity: 0.5
18304
+ },
18305
+ frac
18306
+ );
18307
+ }),
18308
+ data.map((p2, idx) => {
18309
+ const cx = padding.left + (p2.x - minX) / rangeX * chartWidth;
18310
+ const cy = padding.top + chartHeight - (p2.y - minY) / rangeY * chartHeight;
18311
+ const r = p2.size ?? 5;
18312
+ const color = p2.color ?? CHART_COLORS[idx % CHART_COLORS.length];
18313
+ return /* @__PURE__ */ jsxRuntime.jsx(
18314
+ "circle",
18315
+ {
18316
+ cx,
18317
+ cy,
18318
+ r,
18319
+ fill: color,
18320
+ opacity: 0.7,
18321
+ className: "cursor-pointer hover:opacity-100",
18322
+ onClick: () => onPointClick?.(
18323
+ {
18324
+ label: p2.label ?? `(${p2.x}, ${p2.y})`,
18325
+ value: p2.y,
18326
+ color
18327
+ },
18328
+ "default"
18329
+ ),
18330
+ children: /* @__PURE__ */ jsxRuntime.jsx("title", { children: p2.label ?? `(${p2.x}, ${p2.y})` })
18331
+ },
18332
+ idx
18333
+ );
18334
+ }),
18335
+ /* @__PURE__ */ jsxRuntime.jsx(
18336
+ "text",
18337
+ {
18338
+ x: padding.left,
18339
+ y: height - 8,
18340
+ fill: "var(--color-muted-foreground)",
18341
+ fontSize: "9",
18342
+ children: minX.toFixed(1)
18343
+ }
18344
+ ),
18345
+ /* @__PURE__ */ jsxRuntime.jsx(
18346
+ "text",
18347
+ {
18348
+ x: width - padding.right,
18349
+ y: height - 8,
18350
+ textAnchor: "end",
18351
+ fill: "var(--color-muted-foreground)",
18352
+ fontSize: "9",
18353
+ children: maxX.toFixed(1)
18354
+ }
18355
+ )
18356
+ ]
18357
+ }
18358
+ );
17986
18359
  };
17987
18360
  exports.Chart = ({
17988
18361
  title,
@@ -17990,9 +18363,13 @@ var init_Chart = __esm({
17990
18363
  chartType = "bar",
17991
18364
  series,
17992
18365
  data: simpleData,
18366
+ scatterData,
17993
18367
  height = 200,
17994
18368
  showLegend = true,
17995
18369
  showValues = false,
18370
+ stack = "none",
18371
+ timeAxis = false,
18372
+ drillEvent,
17996
18373
  actions,
17997
18374
  entity,
17998
18375
  isLoading = false,
@@ -18009,11 +18386,25 @@ var init_Chart = __esm({
18009
18386
  },
18010
18387
  [eventBus]
18011
18388
  );
18012
- const normalizedData = React75.useMemo(() => {
18013
- if (simpleData) return simpleData;
18014
- if (series && series.length > 0) return series[0].data;
18389
+ const handlePointClick = React75.useCallback(
18390
+ (point, seriesName) => {
18391
+ if (drillEvent) {
18392
+ eventBus.emit(`UI:${drillEvent}`, {
18393
+ label: point.label,
18394
+ value: point.value,
18395
+ seriesLabel: seriesName === "default" ? void 0 : seriesName
18396
+ });
18397
+ }
18398
+ },
18399
+ [drillEvent, eventBus]
18400
+ );
18401
+ const normalizedSeries = React75.useMemo(() => {
18402
+ if (series && series.length > 0) return series;
18403
+ if (simpleData) return [{ name: "default", data: simpleData }];
18015
18404
  return [];
18016
18405
  }, [simpleData, series]);
18406
+ const firstSeriesData = normalizedSeries[0]?.data ?? [];
18407
+ const hasContent = chartType === "scatter" ? (scatterData?.length ?? 0) > 0 : normalizedSeries.some((s) => s.data.length > 0);
18017
18408
  if (isLoading) {
18018
18409
  return /* @__PURE__ */ jsxRuntime.jsx(exports.LoadingState, { message: "Loading chart...", className });
18019
18410
  }
@@ -18027,7 +18418,7 @@ var init_Chart = __esm({
18027
18418
  }
18028
18419
  );
18029
18420
  }
18030
- if (normalizedData.length === 0) {
18421
+ if (!hasContent) {
18031
18422
  return /* @__PURE__ */ jsxRuntime.jsx(exports.EmptyState, { title: t("empty.noData"), description: t("empty.noData"), className });
18032
18423
  }
18033
18424
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn("p-6", className), children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "md", children: [
@@ -18048,18 +18439,84 @@ var init_Chart = __esm({
18048
18439
  )) })
18049
18440
  ] }),
18050
18441
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "w-full", children: [
18051
- chartType === "bar" && /* @__PURE__ */ jsxRuntime.jsx(BarChart, { data: normalizedData, height, showValues }),
18052
- chartType === "line" && /* @__PURE__ */ jsxRuntime.jsx(LineChart, { data: normalizedData, height, showValues }),
18053
- chartType === "area" && /* @__PURE__ */ jsxRuntime.jsx(LineChart, { data: normalizedData, height, showValues, fill: true }),
18054
- chartType === "pie" && /* @__PURE__ */ jsxRuntime.jsx(PieChart, { data: normalizedData, height, showValues: showLegend }),
18055
- chartType === "donut" && /* @__PURE__ */ jsxRuntime.jsx(PieChart, { data: normalizedData, height, showValues: showLegend, donut: true })
18442
+ chartType === "bar" && /* @__PURE__ */ jsxRuntime.jsx(
18443
+ BarChart,
18444
+ {
18445
+ series: normalizedSeries,
18446
+ height,
18447
+ showValues,
18448
+ stack,
18449
+ timeAxis,
18450
+ onPointClick: handlePointClick
18451
+ }
18452
+ ),
18453
+ chartType === "histogram" && /* @__PURE__ */ jsxRuntime.jsx(
18454
+ BarChart,
18455
+ {
18456
+ series: normalizedSeries,
18457
+ height,
18458
+ showValues,
18459
+ stack: "none",
18460
+ timeAxis: false,
18461
+ histogram: true,
18462
+ onPointClick: handlePointClick
18463
+ }
18464
+ ),
18465
+ chartType === "line" && /* @__PURE__ */ jsxRuntime.jsx(
18466
+ LineChart,
18467
+ {
18468
+ series: normalizedSeries,
18469
+ height,
18470
+ showValues,
18471
+ timeAxis,
18472
+ onPointClick: handlePointClick
18473
+ }
18474
+ ),
18475
+ chartType === "area" && /* @__PURE__ */ jsxRuntime.jsx(
18476
+ LineChart,
18477
+ {
18478
+ series: normalizedSeries,
18479
+ height,
18480
+ showValues,
18481
+ timeAxis,
18482
+ fill: true,
18483
+ onPointClick: handlePointClick
18484
+ }
18485
+ ),
18486
+ chartType === "pie" && /* @__PURE__ */ jsxRuntime.jsx(
18487
+ PieChart,
18488
+ {
18489
+ data: firstSeriesData,
18490
+ height,
18491
+ showValues: showLegend,
18492
+ onPointClick: handlePointClick
18493
+ }
18494
+ ),
18495
+ chartType === "donut" && /* @__PURE__ */ jsxRuntime.jsx(
18496
+ PieChart,
18497
+ {
18498
+ data: firstSeriesData,
18499
+ height,
18500
+ showValues: showLegend,
18501
+ donut: true,
18502
+ onPointClick: handlePointClick
18503
+ }
18504
+ ),
18505
+ chartType === "scatter" && /* @__PURE__ */ jsxRuntime.jsx(
18506
+ ScatterChart,
18507
+ {
18508
+ data: scatterData ?? [],
18509
+ height,
18510
+ onPointClick: handlePointClick
18511
+ }
18512
+ )
18056
18513
  ] }),
18057
- showLegend && series && series.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "md", justify: "center", wrap: true, children: series.map((s, idx) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
18514
+ showLegend && normalizedSeries.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "md", justify: "center", wrap: true, children: normalizedSeries.map((s, idx) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
18058
18515
  /* @__PURE__ */ jsxRuntime.jsx(
18059
18516
  exports.Box,
18060
18517
  {
18061
18518
  className: "w-3 h-3 rounded-full flex-shrink-0",
18062
- style: { backgroundColor: s.color || CHART_COLORS[idx % CHART_COLORS.length] }
18519
+ style: { backgroundColor: seriesColor(s, idx) }
18063
18520
  }
18064
18521
  ),
18065
18522
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: s.name })
@@ -20151,7 +20608,6 @@ function useDataDnd(args) {
20151
20608
  const raw = it[dndItemIdField];
20152
20609
  return raw ?? `__idx_${idx}`;
20153
20610
  }),
20154
- // eslint-disable-next-line react-hooks/exhaustive-deps
20155
20611
  [itemIdsSignature]
20156
20612
  );
20157
20613
  const itemsContentSig = items.map((it, idx) => String(it[dndItemIdField] ?? `__${idx}`)).join("|");
@@ -21753,7 +22209,16 @@ var init_FilterGroup = __esm({
21753
22209
  onClear: () => handleFilterSelect(`${filter.field}_to`, null)
21754
22210
  }
21755
22211
  )
21756
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
22212
+ ] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
22213
+ exports.Input,
22214
+ {
22215
+ value: selectedValues[filter.field] || "",
22216
+ onChange: (e) => handleFilterSelect(filter.field, e.target.value || null),
22217
+ placeholder: filter.label,
22218
+ clearable: true,
22219
+ onClear: () => handleFilterSelect(filter.field, null)
22220
+ }
22221
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
21757
22222
  exports.Select,
21758
22223
  {
21759
22224
  value: selectedValues[filter.field] || "all",
@@ -21820,7 +22285,17 @@ var init_FilterGroup = __esm({
21820
22285
  className: "text-sm min-w-[100px]"
21821
22286
  }
21822
22287
  )
21823
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
22288
+ ] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
22289
+ exports.Input,
22290
+ {
22291
+ value: selectedValues[filter.field] || "",
22292
+ onChange: (e) => handleFilterSelect(filter.field, e.target.value || null),
22293
+ placeholder: filter.label,
22294
+ clearable: true,
22295
+ onClear: () => handleFilterSelect(filter.field, null),
22296
+ className: "text-sm"
22297
+ }
22298
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
21824
22299
  exports.Select,
21825
22300
  {
21826
22301
  value: selectedValues[filter.field] || "all",
@@ -21925,7 +22400,17 @@ var init_FilterGroup = __esm({
21925
22400
  className: "min-w-[130px]"
21926
22401
  }
21927
22402
  )
21928
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
22403
+ ] }) : resolveFilterType(filter) === "text" ? /* @__PURE__ */ jsxRuntime.jsx(
22404
+ exports.Input,
22405
+ {
22406
+ value: selectedValues[filter.field] || "",
22407
+ onChange: (e) => handleFilterSelect(filter.field, e.target.value || null),
22408
+ placeholder: filter.label,
22409
+ clearable: true,
22410
+ onClear: () => handleFilterSelect(filter.field, null),
22411
+ className: "min-w-[160px]"
22412
+ }
22413
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
21929
22414
  exports.Select,
21930
22415
  {
21931
22416
  value: selectedValues[filter.field] || "all",
@@ -23214,6 +23699,151 @@ var init_FlipCard = __esm({
23214
23699
  exports.FlipCard.displayName = "FlipCard";
23215
23700
  }
23216
23701
  });
23702
+ function toISODate(d) {
23703
+ return d.toISOString().slice(0, 10);
23704
+ }
23705
+ function startOfMonth(d) {
23706
+ return new Date(d.getFullYear(), d.getMonth(), 1);
23707
+ }
23708
+ function startOfQuarter(d) {
23709
+ return new Date(d.getFullYear(), Math.floor(d.getMonth() / 3) * 3, 1);
23710
+ }
23711
+ function startOfYear(d) {
23712
+ return new Date(d.getFullYear(), 0, 1);
23713
+ }
23714
+ function daysAgo(n) {
23715
+ const d = /* @__PURE__ */ new Date();
23716
+ d.setDate(d.getDate() - n);
23717
+ return d;
23718
+ }
23719
+ var DEFAULT_PRESETS; exports.DateRangePicker = void 0;
23720
+ var init_DateRangePicker = __esm({
23721
+ "components/molecules/DateRangePicker.tsx"() {
23722
+ "use client";
23723
+ init_cn();
23724
+ init_Button();
23725
+ init_Input();
23726
+ init_Stack();
23727
+ init_Typography();
23728
+ init_useEventBus();
23729
+ DEFAULT_PRESETS = [
23730
+ {
23731
+ label: "Last 7 days",
23732
+ value: "7d",
23733
+ range: () => ({ from: toISODate(daysAgo(7)), to: toISODate(/* @__PURE__ */ new Date()) })
23734
+ },
23735
+ {
23736
+ label: "Last 30 days",
23737
+ value: "30d",
23738
+ range: () => ({ from: toISODate(daysAgo(30)), to: toISODate(/* @__PURE__ */ new Date()) })
23739
+ },
23740
+ {
23741
+ label: "This Month",
23742
+ value: "month",
23743
+ range: () => ({ from: toISODate(startOfMonth(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
23744
+ },
23745
+ {
23746
+ label: "This Quarter",
23747
+ value: "quarter",
23748
+ range: () => ({ from: toISODate(startOfQuarter(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
23749
+ },
23750
+ {
23751
+ label: "YTD",
23752
+ value: "ytd",
23753
+ range: () => ({ from: toISODate(startOfYear(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
23754
+ }
23755
+ ];
23756
+ exports.DateRangePicker = ({
23757
+ from: fromProp,
23758
+ to: toProp,
23759
+ event,
23760
+ onChange,
23761
+ presets = DEFAULT_PRESETS,
23762
+ fromLabel = "From",
23763
+ toLabel = "To",
23764
+ className
23765
+ }) => {
23766
+ const eventBus = useEventBus();
23767
+ const [from, setFrom] = React75.useState(fromProp ?? "");
23768
+ const [to, setTo] = React75.useState(toProp ?? "");
23769
+ const [activePreset, setActivePreset] = React75.useState(null);
23770
+ const emit = React75.useCallback(
23771
+ (range) => {
23772
+ onChange?.(range);
23773
+ if (event) eventBus.emit(`UI:${event}`, range);
23774
+ },
23775
+ [onChange, event, eventBus]
23776
+ );
23777
+ const handleFromChange = React75.useCallback(
23778
+ (next) => {
23779
+ setFrom(next);
23780
+ setActivePreset(null);
23781
+ emit({ from: next, to });
23782
+ },
23783
+ [to, emit]
23784
+ );
23785
+ const handleToChange = React75.useCallback(
23786
+ (next) => {
23787
+ setTo(next);
23788
+ setActivePreset(null);
23789
+ emit({ from, to: next });
23790
+ },
23791
+ [from, emit]
23792
+ );
23793
+ const handlePreset = React75.useCallback(
23794
+ (preset) => {
23795
+ const range = preset.range();
23796
+ setFrom(range.from);
23797
+ setTo(range.to);
23798
+ setActivePreset(preset.value);
23799
+ emit(range);
23800
+ },
23801
+ [emit]
23802
+ );
23803
+ const presetButtons = React75.useMemo(
23804
+ () => presets.map((preset) => /* @__PURE__ */ jsxRuntime.jsx(
23805
+ exports.Button,
23806
+ {
23807
+ variant: activePreset === preset.value ? "primary" : "ghost",
23808
+ size: "sm",
23809
+ onClick: () => handlePreset(preset),
23810
+ children: preset.label
23811
+ },
23812
+ preset.value
23813
+ )),
23814
+ [presets, activePreset, handlePreset]
23815
+ );
23816
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", className: cn(className), children: [
23817
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "md", align: "end", children: [
23818
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
23819
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: fromLabel }),
23820
+ /* @__PURE__ */ jsxRuntime.jsx(
23821
+ exports.Input,
23822
+ {
23823
+ type: "date",
23824
+ value: from,
23825
+ onChange: (e) => handleFromChange(e.target.value)
23826
+ }
23827
+ )
23828
+ ] }),
23829
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
23830
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: toLabel }),
23831
+ /* @__PURE__ */ jsxRuntime.jsx(
23832
+ exports.Input,
23833
+ {
23834
+ type: "date",
23835
+ value: to,
23836
+ onChange: (e) => handleToChange(e.target.value)
23837
+ }
23838
+ )
23839
+ ] })
23840
+ ] }),
23841
+ presets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "xs", wrap: true, children: presetButtons })
23842
+ ] });
23843
+ };
23844
+ exports.DateRangePicker.displayName = "DateRangePicker";
23845
+ }
23846
+ });
23217
23847
  var DEFAULT_OPTIONS; exports.DateRangeSelector = void 0;
23218
23848
  var init_DateRangeSelector = __esm({
23219
23849
  "components/molecules/DateRangeSelector.tsx"() {
@@ -26469,7 +27099,9 @@ var init_StatDisplay = __esm({
26469
27099
  init_Typography();
26470
27100
  init_Box();
26471
27101
  init_Stack();
27102
+ init_Sparkline();
26472
27103
  init_Icon();
27104
+ init_useEventBus();
26473
27105
  variantColor = {
26474
27106
  default: "text-foreground",
26475
27107
  primary: "text-primary",
@@ -26484,6 +27116,10 @@ var init_StatDisplay = __esm({
26484
27116
  max,
26485
27117
  target,
26486
27118
  trend,
27119
+ trendPolarity = "higher-is-better",
27120
+ trendFormat = "absolute",
27121
+ sparklineData,
27122
+ clickEvent,
26487
27123
  prefix,
26488
27124
  suffix,
26489
27125
  icon: iconProp,
@@ -26497,6 +27133,10 @@ var init_StatDisplay = __esm({
26497
27133
  isLoading = false,
26498
27134
  error = null
26499
27135
  }) => {
27136
+ const eventBus = useEventBus();
27137
+ const handleClick = React75.useCallback(() => {
27138
+ if (clickEvent) eventBus.emit(`UI:${clickEvent}`, { metricLabel: label });
27139
+ }, [clickEvent, eventBus, label]);
26500
27140
  const ResolvedIcon = typeof iconProp === "string" ? resolveIcon(iconProp) : null;
26501
27141
  const iconSizes3 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
26502
27142
  const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
@@ -26507,7 +27147,10 @@ var init_StatDisplay = __esm({
26507
27147
  const targetPct = showTarget ? Math.max(0, Math.min(100, numericValue / target * 100)) : 0;
26508
27148
  const showTrend = typeof trend === "number" && trend !== 0 && Number.isFinite(trend);
26509
27149
  const trendUp = showTrend && trend > 0;
26510
- const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${Math.abs(trend)}` : "";
27150
+ const trendIsGood = trendPolarity === "lower-is-better" ? !trendUp : trendUp;
27151
+ const trendMagnitude = Math.abs(trend);
27152
+ const trendSuffix = trendFormat === "percent" ? "%" : "";
27153
+ const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${trendMagnitude}${trendSuffix}` : "";
26511
27154
  if (error) {
26512
27155
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "error", children: error.message }) });
26513
27156
  }
@@ -26518,38 +27161,57 @@ var init_StatDisplay = __esm({
26518
27161
  ] }) });
26519
27162
  }
26520
27163
  if (compact) {
26521
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: cn("items-center", className), children: [
26522
- ResolvedIcon && /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }),
26523
- typeof iconProp !== "string" && iconProp,
26524
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: label }),
26525
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
26526
- showTrend && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: cn("font-semibold", trendUp ? "text-success" : "text-error"), children: trendLabel })
26527
- ] });
27164
+ return /* @__PURE__ */ jsxRuntime.jsxs(
27165
+ exports.HStack,
27166
+ {
27167
+ gap: "sm",
27168
+ className: cn("items-center", clickEvent && "cursor-pointer hover:opacity-80", className),
27169
+ onClick: clickEvent ? handleClick : void 0,
27170
+ children: [
27171
+ ResolvedIcon && /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }),
27172
+ typeof iconProp !== "string" && iconProp,
27173
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: label }),
27174
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27175
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: cn("font-semibold", trendIsGood ? "text-success" : "text-error"), children: trendLabel }),
27176
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.Sparkline, { data: sparklineData, color: "auto", width: 60, height: 20 })
27177
+ ]
27178
+ }
27179
+ );
26528
27180
  }
26529
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "start", justify: "between", children: [
26530
- /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "space-y-1 flex-1", children: [
26531
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "overline", color: "secondary", children: label }),
26532
- /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", align: "end", children: [
26533
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
26534
- showTrend && /* @__PURE__ */ jsxRuntime.jsx(
26535
- exports.Typography,
26536
- {
26537
- variant: "caption",
26538
- className: cn("font-semibold pb-1", trendUp ? "text-success" : "text-error"),
26539
- children: trendLabel
26540
- }
26541
- )
26542
- ] }),
26543
- showTarget && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
26544
- exports.Box,
26545
- {
26546
- className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
26547
- style: { width: `${targetPct}%` }
26548
- }
26549
- ) })
26550
- ] }),
26551
- (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }) : iconProp })
26552
- ] }) });
27181
+ return /* @__PURE__ */ jsxRuntime.jsx(
27182
+ exports.Card,
27183
+ {
27184
+ className: cn(padSizes[size], clickEvent && "cursor-pointer hover:shadow-md transition-shadow", className),
27185
+ onClick: clickEvent ? handleClick : void 0,
27186
+ children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "start", justify: "between", children: [
27187
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "space-y-1 flex-1", children: [
27188
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "overline", color: "secondary", children: label }),
27189
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", align: "end", children: [
27190
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27191
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(
27192
+ exports.Typography,
27193
+ {
27194
+ variant: "caption",
27195
+ className: cn("font-semibold pb-1", trendIsGood ? "text-success" : "text-error"),
27196
+ children: trendLabel
27197
+ }
27198
+ )
27199
+ ] }),
27200
+ showTarget && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
27201
+ exports.Box,
27202
+ {
27203
+ className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
27204
+ style: { width: `${targetPct}%` }
27205
+ }
27206
+ ) })
27207
+ ] }),
27208
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", align: "end", children: [
27209
+ (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }) : iconProp }),
27210
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.Sparkline, { data: sparklineData, color: "auto" })
27211
+ ] })
27212
+ ] })
27213
+ }
27214
+ );
26553
27215
  };
26554
27216
  exports.StatDisplay.displayName = "StatDisplay";
26555
27217
  }
@@ -31898,6 +32560,7 @@ var init_molecules = __esm({
31898
32560
  init_ViolationAlert();
31899
32561
  init_FormSectionHeader();
31900
32562
  init_FlipCard();
32563
+ init_DateRangePicker();
31901
32564
  init_DateRangeSelector();
31902
32565
  init_ChartLegend();
31903
32566
  init_LineChart();
@@ -41454,6 +42117,7 @@ var init_StatCard = __esm({
41454
42117
  init_Box();
41455
42118
  init_Stack();
41456
42119
  init_Button();
42120
+ init_Sparkline();
41457
42121
  init_useEventBus();
41458
42122
  init_useTranslate();
41459
42123
  init_Icon();
@@ -41623,32 +42287,7 @@ var init_StatCard = __esm({
41623
42287
  ] }),
41624
42288
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", align: "end", children: [
41625
42289
  Icon3 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsxRuntime.jsx(Icon3, { className: cn("h-6 w-6", iconColor) }) }),
41626
- sparklineData && sparklineData.length > 1 && (() => {
41627
- const w = 80;
41628
- const h = 32;
41629
- const pad = 2;
41630
- const min = Math.min(...sparklineData);
41631
- const max = Math.max(...sparklineData);
41632
- const range = max - min || 1;
41633
- const points = sparklineData.map((v, i) => {
41634
- const x = pad + i / (sparklineData.length - 1) * (w - pad * 2);
41635
- const y = pad + (1 - (v - min) / range) * (h - pad * 2);
41636
- return `${x},${y}`;
41637
- }).join(" ");
41638
- const trending = sparklineData[sparklineData.length - 1] >= sparklineData[0];
41639
- const strokeColor = trending ? "var(--color-success)" : "var(--color-error)";
41640
- return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: w, height: h, viewBox: `0 0 ${w} ${h}`, className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
41641
- "polyline",
41642
- {
41643
- fill: "none",
41644
- stroke: strokeColor,
41645
- strokeWidth: "2",
41646
- strokeLinecap: "round",
41647
- strokeLinejoin: "round",
41648
- points
41649
- }
41650
- ) });
41651
- })()
42290
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.Sparkline, { data: sparklineData, color: "auto" })
41652
42291
  ] })
41653
42292
  ] }),
41654
42293
  action && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -43735,6 +44374,7 @@ var init_component_registry_generated = __esm({
43735
44374
  init_DataGrid();
43736
44375
  init_DataList();
43737
44376
  init_DataTable();
44377
+ init_DateRangePicker();
43738
44378
  init_DateRangeSelector();
43739
44379
  init_DayCell();
43740
44380
  init_DebuggerBoard();
@@ -43867,6 +44507,7 @@ var init_component_registry_generated = __esm({
43867
44507
  init_Skeleton();
43868
44508
  init_SocialProof();
43869
44509
  init_SortableList();
44510
+ init_Sparkline();
43870
44511
  init_Split();
43871
44512
  init_SplitPane();
43872
44513
  init_SplitSection();
@@ -44015,6 +44656,7 @@ var init_component_registry_generated = __esm({
44015
44656
  "DataGrid": DataGrid,
44016
44657
  "DataList": DataList,
44017
44658
  "DataTable": DataTable,
44659
+ "DateRangePicker": exports.DateRangePicker,
44018
44660
  "DateRangeSelector": exports.DateRangeSelector,
44019
44661
  "DayCell": DayCell,
44020
44662
  "DebuggerBoard": DebuggerBoard,
@@ -44176,6 +44818,7 @@ var init_component_registry_generated = __esm({
44176
44818
  "SortableList": exports.SortableList,
44177
44819
  "Spacer": SpacerPattern,
44178
44820
  "SpacerPattern": SpacerPattern,
44821
+ "Sparkline": exports.Sparkline,
44179
44822
  "Spinner": SpinnerPattern,
44180
44823
  "SpinnerPattern": SpinnerPattern,
44181
44824
  "Split": exports.Split,
@@ -45180,6 +45823,7 @@ var init_atoms = __esm({
45180
45823
  init_Icon();
45181
45824
  init_ProgressBar();
45182
45825
  init_Radio();
45826
+ init_Sparkline();
45183
45827
  init_Switch();
45184
45828
  init_Spacer();
45185
45829
  init_Stack();