@algorithm-shift/design-system 1.3.120 → 1.3.121

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.
package/dist/index.mjs CHANGED
@@ -2918,13 +2918,61 @@ function resolveDate(option, customOption) {
2918
2918
  switch (option) {
2919
2919
  case "today":
2920
2920
  return /* @__PURE__ */ new Date();
2921
- case "custom":
2922
- return customOption ? new Date(customOption) : void 0;
2921
+ case "custom": {
2922
+ if (!customOption) return void 0;
2923
+ const d = new Date(customOption);
2924
+ return !isNaN(d.getTime()) ? d : void 0;
2925
+ }
2923
2926
  case "none":
2924
2927
  default:
2925
2928
  return void 0;
2926
2929
  }
2927
2930
  }
2931
+ function resolveDefaultDate(option, customOption) {
2932
+ if (!option || option === "none") return void 0;
2933
+ const now = /* @__PURE__ */ new Date();
2934
+ switch (option) {
2935
+ case "today":
2936
+ return new Date(now);
2937
+ case "currentPlus1Week": {
2938
+ const d = new Date(now);
2939
+ d.setDate(d.getDate() + 7);
2940
+ return d;
2941
+ }
2942
+ case "currentPlus1Year": {
2943
+ const d = new Date(now);
2944
+ d.setFullYear(d.getFullYear() + 1);
2945
+ return d;
2946
+ }
2947
+ case "previousDay": {
2948
+ const d = new Date(now);
2949
+ d.setDate(d.getDate() - 1);
2950
+ return d;
2951
+ }
2952
+ case "previous1Week": {
2953
+ const d = new Date(now);
2954
+ d.setDate(d.getDate() - 7);
2955
+ return d;
2956
+ }
2957
+ case "previous1Month": {
2958
+ const d = new Date(now);
2959
+ d.setMonth(d.getMonth() - 1);
2960
+ return d;
2961
+ }
2962
+ case "previous1Year": {
2963
+ const d = new Date(now);
2964
+ d.setFullYear(d.getFullYear() - 1);
2965
+ return d;
2966
+ }
2967
+ case "custom": {
2968
+ if (!customOption) return void 0;
2969
+ const d = new Date(customOption);
2970
+ return !isNaN(d.getTime()) ? d : void 0;
2971
+ }
2972
+ default:
2973
+ return void 0;
2974
+ }
2975
+ }
2928
2976
  function DateTimePicker({
2929
2977
  className,
2930
2978
  style,
@@ -2942,6 +2990,8 @@ function DateTimePicker({
2942
2990
  const customMinimumDate = props.customMinimumDate ?? "";
2943
2991
  const maximumDate = props.maximumDate ?? "none";
2944
2992
  const customMaximumDate = props.customMaximumDate ?? "";
2993
+ const defaultDateValue = props.defaultDateValue ?? "none";
2994
+ const customDefaultDate = props.customDefaultDate ?? "";
2945
2995
  const isEditable = props.isEditable ?? true;
2946
2996
  const isDisabled = props.isDisabled ?? false;
2947
2997
  const isReadonly = props.isReadonly ?? false;
@@ -2949,9 +2999,11 @@ function DateTimePicker({
2949
2999
  const minDate = resolveDate(minimumDate, customMinimumDate);
2950
3000
  const maxDate = resolveDate(maximumDate, customMaximumDate);
2951
3001
  const [date, setDate] = React8.useState(() => {
2952
- if (!props.value) return void 0;
2953
- const d = new Date(props.value);
2954
- return isNaN(d.getTime()) ? void 0 : d;
3002
+ if (props.value) {
3003
+ const d = new Date(props.value);
3004
+ if (!isNaN(d.getTime())) return d;
3005
+ }
3006
+ return resolveDefaultDate(defaultDateValue, customDefaultDate);
2955
3007
  });
2956
3008
  const initialHours = date ? date.getHours() : 0;
2957
3009
  const initialMinutes = date ? date.getMinutes() : 0;
@@ -2966,26 +3018,6 @@ function DateTimePicker({
2966
3018
  React8.useEffect(() => {
2967
3019
  setAmPm(hours >= 12 ? "PM" : "AM");
2968
3020
  }, [hours]);
2969
- React8.useEffect(() => {
2970
- if (!props.value) {
2971
- setDate(void 0);
2972
- return;
2973
- }
2974
- const d = new Date(props.value);
2975
- if (!isNaN(d.getTime())) {
2976
- setDate(d);
2977
- setHours(d.getHours());
2978
- setMinutes(d.getMinutes());
2979
- }
2980
- }, [props.value]);
2981
- const [year, setYear] = React8.useState(date ? date.getFullYear() : (/* @__PURE__ */ new Date()).getFullYear());
2982
- React8.useEffect(() => {
2983
- if (!date) return;
2984
- const newDate = new Date(date);
2985
- newDate.setFullYear(year);
2986
- setDate(newDate);
2987
- emitChange(newDate);
2988
- }, [year]);
2989
3021
  const emitChange = (nextDate) => {
2990
3022
  if (!props.onChange) return;
2991
3023
  let valueString = "";
@@ -3007,6 +3039,36 @@ function DateTimePicker({
3007
3039
  };
3008
3040
  props.onChange(event, props.name || "");
3009
3041
  };
3042
+ React8.useEffect(() => {
3043
+ if (!props.value) {
3044
+ const defaultDate = resolveDefaultDate(defaultDateValue, customDefaultDate);
3045
+ setDate(defaultDate);
3046
+ if (defaultDate) {
3047
+ setYear(defaultDate.getFullYear());
3048
+ setHours(defaultDate.getHours());
3049
+ setMinutes(defaultDate.getMinutes());
3050
+ emitChange(defaultDate);
3051
+ } else {
3052
+ setHours(0);
3053
+ setMinutes(0);
3054
+ }
3055
+ return;
3056
+ }
3057
+ const d = new Date(props.value);
3058
+ if (!isNaN(d.getTime())) {
3059
+ setDate(d);
3060
+ setHours(d.getHours());
3061
+ setMinutes(d.getMinutes());
3062
+ }
3063
+ }, [props.value, defaultDateValue, customDefaultDate]);
3064
+ const [year, setYear] = React8.useState(date ? date.getFullYear() : (/* @__PURE__ */ new Date()).getFullYear());
3065
+ React8.useEffect(() => {
3066
+ if (!date) return;
3067
+ const newDate = new Date(date);
3068
+ newDate.setFullYear(year);
3069
+ setDate(newDate);
3070
+ emitChange(newDate);
3071
+ }, [year]);
3010
3072
  const updateDateTime = (nextBaseDate, h = hours, m = minutes) => {
3011
3073
  if (!nextBaseDate) {
3012
3074
  setDate(void 0);
@@ -3237,7 +3299,7 @@ function DateTimePicker({
3237
3299
  name: props.name,
3238
3300
  autoComplete: isAutocomplete ? "on" : "off",
3239
3301
  readOnly: isReadonly,
3240
- value: !date ? "" : mode === "date" ? format(date, "yyyy-MM-dd") : mode === "time" ? format(date, "HH:mm:ss") : date.toISOString(),
3302
+ value: !date || isNaN(date.getTime()) ? "" : mode === "date" ? format(date, "yyyy-MM-dd") : mode === "time" ? format(date, "HH:mm:ss") : date.toISOString(),
3241
3303
  onChange: () => {
3242
3304
  }
3243
3305
  }
@@ -5967,55 +6029,51 @@ import {
5967
6029
  YAxis,
5968
6030
  CartesianGrid,
5969
6031
  Tooltip as Tooltip2,
5970
- ResponsiveContainer,
5971
- Legend
6032
+ ResponsiveContainer
5972
6033
  } from "recharts";
5973
6034
  import { jsx as jsx68, jsxs as jsxs42 } from "react/jsx-runtime";
5974
- var getRandomColor = () => {
5975
- const palette = [
5976
- "#2563eb",
5977
- "#1d4ed8",
5978
- "#1e40af",
5979
- "#1e3a8a",
5980
- "#1e293b",
5981
- "#10b981",
5982
- "#059669",
5983
- "#047857",
5984
- "#065f46",
5985
- "#022c22",
5986
- "#f59e0b",
5987
- "#d97706",
5988
- "#b45309",
5989
- "#92400e",
5990
- "#422006",
5991
- "#ef4444",
5992
- "#dc2626",
5993
- "#b91c1c",
5994
- "#991b1b",
5995
- "#7f1d1d",
5996
- "#8b5cf6",
5997
- "#7c3aed",
5998
- "#6d28d9",
5999
- "#5b21b6",
6000
- "#4c1d95",
6001
- "#14b8a6",
6002
- "#0d9488",
6003
- "#0f766e",
6004
- "#115e59",
6005
- "#134e4a",
6006
- "#ec4899",
6007
- "#db2777",
6008
- "#be185d",
6009
- "#9d174d",
6010
- "#831843",
6011
- "#22c55e",
6012
- "#16a34a",
6013
- "#15803d",
6014
- "#166534",
6015
- "#14532d"
6016
- ];
6017
- return palette[Math.floor(Math.random() * palette.length)];
6018
- };
6035
+ var palette = [
6036
+ "#2563eb",
6037
+ "#1d4ed8",
6038
+ "#1e40af",
6039
+ "#1e3a8a",
6040
+ "#1e293b",
6041
+ "#10b981",
6042
+ "#059669",
6043
+ "#047857",
6044
+ "#065f46",
6045
+ "#022c22",
6046
+ "#f59e0b",
6047
+ "#d97706",
6048
+ "#b45309",
6049
+ "#92400e",
6050
+ "#422006",
6051
+ "#ef4444",
6052
+ "#dc2626",
6053
+ "#b91c1c",
6054
+ "#991b1b",
6055
+ "#7f1d1d",
6056
+ "#8b5cf6",
6057
+ "#7c3aed",
6058
+ "#6d28d9",
6059
+ "#5b21b6",
6060
+ "#4c1d95",
6061
+ "#14b8a6",
6062
+ "#0d9488",
6063
+ "#0f766e",
6064
+ "#115e59",
6065
+ "#134e4a",
6066
+ "#ec4899",
6067
+ "#db2777",
6068
+ "#be185d",
6069
+ "#9d174d",
6070
+ "#831843",
6071
+ "#22c55e",
6072
+ "#16a34a",
6073
+ "#15803d",
6074
+ "#166534",
6075
+ "#14532d"
6076
+ ];
6019
6077
  var ChartComponent = ({
6020
6078
  className,
6021
6079
  style,
@@ -6023,25 +6081,30 @@ var ChartComponent = ({
6023
6081
  dataKey,
6024
6082
  dataLabel,
6025
6083
  apiUrl,
6084
+ source,
6026
6085
  isPaginationEnabled,
6027
6086
  limit = 10,
6028
6087
  canvasMode,
6088
+ showLegends = true,
6089
+ legendPosition = "bottom",
6090
+ onLegendClick,
6029
6091
  ...props
6030
6092
  }) => {
6093
+ const useApi = source === "api" && !!apiUrl;
6031
6094
  const [rawData, setRawData] = useState15([]);
6032
6095
  const [rawMeta, setRawMeta] = useState15(null);
6033
6096
  const [localLoading, setLocalLoading] = useState15(false);
6034
6097
  const [currentPage, setCurrentPage] = useState15(1);
6035
- const effectiveData = apiUrl ? rawData : props.data || [];
6036
- const effectiveLoading = apiUrl ? localLoading : externalLoading;
6098
+ const effectiveData = useApi ? rawData : props.data || [];
6099
+ const effectiveLoading = useApi ? localLoading : externalLoading;
6037
6100
  useEffect29(() => {
6038
- if (apiUrl) {
6101
+ if (useApi) {
6039
6102
  setCurrentPage(1);
6040
6103
  }
6041
- }, [apiUrl]);
6104
+ }, [useApi]);
6042
6105
  const fetchData = useCallback7(async (page) => {
6043
6106
  if (!apiUrl) return;
6044
- const cancelled = false;
6107
+ let cancelled = false;
6045
6108
  try {
6046
6109
  setLocalLoading(true);
6047
6110
  const params = new URLSearchParams({
@@ -6077,9 +6140,9 @@ var ChartComponent = ({
6077
6140
  }
6078
6141
  }, [apiUrl, limit]);
6079
6142
  useEffect29(() => {
6080
- if (!apiUrl) return;
6143
+ if (!useApi) return;
6081
6144
  fetchData(currentPage);
6082
- }, [apiUrl, currentPage, fetchData]);
6145
+ }, [useApi, currentPage, fetchData]);
6083
6146
  const handlePageChange = (newPage) => {
6084
6147
  if (rawMeta && (newPage < 1 || newPage > rawMeta.pages)) return;
6085
6148
  setCurrentPage(newPage);
@@ -6088,15 +6151,59 @@ var ChartComponent = ({
6088
6151
  if (!Array.isArray(effectiveData) || effectiveData.length === 0 || !dataKey || !dataLabel) {
6089
6152
  return [];
6090
6153
  }
6091
- return effectiveData.map((item) => ({
6154
+ return effectiveData.map((item, index) => ({
6092
6155
  ...item,
6093
6156
  [dataKey]: Number(item[dataKey] || 0),
6094
- fill: getRandomColor()
6157
+ fill: item.fill || palette[index % palette.length]
6095
6158
  }));
6096
6159
  }, [effectiveData, dataKey, dataLabel]);
6097
6160
  const chartType = props.chartType || "bar";
6098
- const legendsPosition = ["middle", "bottom"].includes(props.legendsPosition) ? props.legendsPosition : "top";
6099
- if (effectiveLoading || data.length === 0) {
6161
+ const forceMobile = canvasMode === "mobile" || canvasMode === "tablet";
6162
+ const renderLegends = useMemo11(() => {
6163
+ if (!showLegends || !dataKey || !dataLabel) return null;
6164
+ const isLegendRight2 = !forceMobile && legendPosition === "right";
6165
+ return /* @__PURE__ */ jsx68(
6166
+ "div",
6167
+ {
6168
+ className: isLegendRight2 ? "flex flex-col gap-2 w-full min-w-0 justify-start" : "flex flex-wrap justify-center gap-2 mt-4 w-full max-w-4xl",
6169
+ children: data.map((d, index) => {
6170
+ const value = d[dataKey] ?? 0;
6171
+ const displayValue = value >= 1e3 ? `${(value / 1e3).toFixed(0)}k` : value.toLocaleString();
6172
+ const payload = {
6173
+ name: d[dataLabel],
6174
+ value,
6175
+ [dataLabel]: d[dataLabel],
6176
+ [dataKey]: value
6177
+ };
6178
+ return /* @__PURE__ */ jsxs42(
6179
+ "div",
6180
+ {
6181
+ role: onLegendClick ? "button" : void 0,
6182
+ tabIndex: onLegendClick ? 0 : void 0,
6183
+ onClick: onLegendClick ? () => onLegendClick(payload) : void 0,
6184
+ onKeyDown: onLegendClick ? (e) => e.key === "Enter" && onLegendClick(payload) : void 0,
6185
+ className: `flex items-center space-x-2 rounded-lg border border-gray-200/50 px-3 py-1.5 min-w-[180px] w-[180px] bg-white/80 backdrop-blur-sm shadow-sm hover:shadow-md transition-all ${onLegendClick ? "cursor-pointer" : ""}`,
6186
+ children: [
6187
+ /* @__PURE__ */ jsx68(
6188
+ "span",
6189
+ {
6190
+ className: "inline-block w-[12px] h-[12px] rounded-full shrink-0 border-2 border-white/50",
6191
+ style: { backgroundColor: d.fill }
6192
+ }
6193
+ ),
6194
+ /* @__PURE__ */ jsxs42("div", { className: "min-w-0 flex-1", children: [
6195
+ /* @__PURE__ */ jsx68("span", { className: "text-gray-900 text-[11px] md:text-[13px] font-semibold block truncate leading-tight capitalize", children: d[dataLabel] }),
6196
+ /* @__PURE__ */ jsx68("span", { className: "text-xs text-gray-600 font-medium", children: displayValue })
6197
+ ] })
6198
+ ]
6199
+ },
6200
+ `legend-${index}`
6201
+ );
6202
+ })
6203
+ }
6204
+ );
6205
+ }, [data, dataLabel, dataKey, showLegends, onLegendClick, legendPosition, forceMobile]);
6206
+ if (effectiveLoading) {
6100
6207
  return /* @__PURE__ */ jsxs42(
6101
6208
  "div",
6102
6209
  {
@@ -6127,141 +6234,167 @@ var ChartComponent = ({
6127
6234
  }
6128
6235
  );
6129
6236
  }
6130
- return /* @__PURE__ */ jsxs42("div", { className: `${className} h-[450px]`, style, children: [
6131
- isPaginationEnabled && rawMeta && /* @__PURE__ */ jsxs42("div", { className: "flex items-center justify-between mb-4 px-2", children: [
6132
- /* @__PURE__ */ jsxs42("div", { className: "flex items-center space-x-2 sm:hidden w-full justify-center", children: [
6133
- /* @__PURE__ */ jsx68(
6134
- "button",
6135
- {
6136
- onClick: () => handlePageChange(currentPage - 1),
6137
- disabled: currentPage === 1 || localLoading,
6138
- className: "flex-1 px-3 py-2 text-xs font-medium rounded-lg border bg-white shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center justify-center space-x-1 min-w-0",
6139
- children: /* @__PURE__ */ jsx68("span", { children: "\u2190 Prev" })
6140
- }
6141
- ),
6142
- /* @__PURE__ */ jsx68("span", { className: "px-2 py-2 text-xs font-semibold text-gray-700 min-w-[36px] text-center flex-shrink-0", children: currentPage }),
6143
- /* @__PURE__ */ jsx68(
6144
- "button",
6145
- {
6146
- onClick: () => handlePageChange(currentPage + 1),
6147
- disabled: currentPage >= rawMeta.pages || localLoading,
6148
- className: "flex-1 px-3 py-2 text-xs font-medium rounded-lg border bg-white shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center justify-center space-x-1 min-w-0",
6149
- children: /* @__PURE__ */ jsx68("span", { children: "Next \u2192" })
6150
- }
6151
- )
6152
- ] }),
6153
- /* @__PURE__ */ jsxs42("div", { className: "flex items-center space-x-2 hidden sm:flex", children: [
6154
- /* @__PURE__ */ jsx68(
6155
- "button",
6237
+ if (data.length === 0) {
6238
+ return /* @__PURE__ */ jsx68(
6239
+ "div",
6240
+ {
6241
+ className: `relative flex flex-col items-center justify-center w-full h-[300px] md:h-[400px] bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl p-6 ${className}`,
6242
+ style,
6243
+ children: /* @__PURE__ */ jsx68("div", { className: "text-center", children: /* @__PURE__ */ jsx68("div", { className: "inline-flex items-center space-x-2 bg-white/80 px-6 py-3 rounded-xl backdrop-blur-sm border border-gray-200 shadow-lg", children: /* @__PURE__ */ jsx68("span", { className: "text-sm font-medium text-gray-600", children: "No data" }) }) })
6244
+ }
6245
+ );
6246
+ }
6247
+ const isLegendRight = !forceMobile && legendPosition === "right";
6248
+ return /* @__PURE__ */ jsxs42(
6249
+ "div",
6250
+ {
6251
+ className: `relative flex ${isLegendRight ? "flex-row items-stretch gap-4" : "flex-col items-center"} ${className}`,
6252
+ style,
6253
+ children: [
6254
+ /* @__PURE__ */ jsxs42(
6255
+ "div",
6156
6256
  {
6157
- onClick: () => handlePageChange(currentPage - 1),
6158
- disabled: currentPage === 1 || localLoading,
6159
- className: "px-3 py-1.5 text-sm font-medium rounded-lg border bg-white shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center space-x-1",
6160
- children: /* @__PURE__ */ jsx68("span", { children: "\u2190 Prev" })
6257
+ className: `relative flex items-center justify-center ${isLegendRight ? "flex-[2] min-w-0 max-w-[70%] h-[450px]" : "w-full md:w-[75%] h-[450px] mb-2"}`,
6258
+ children: [
6259
+ isPaginationEnabled && rawMeta && /* @__PURE__ */ jsxs42("div", { className: "flex items-center justify-between mb-4 px-2", children: [
6260
+ /* @__PURE__ */ jsxs42("div", { className: "flex items-center space-x-2 sm:hidden w-full justify-center", children: [
6261
+ /* @__PURE__ */ jsx68(
6262
+ "button",
6263
+ {
6264
+ onClick: () => handlePageChange(currentPage - 1),
6265
+ disabled: currentPage === 1 || localLoading,
6266
+ className: "flex-1 px-3 py-2 text-xs font-medium rounded-lg border bg-white shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center justify-center space-x-1 min-w-0",
6267
+ children: /* @__PURE__ */ jsx68("span", { children: "\u2190 Prev" })
6268
+ }
6269
+ ),
6270
+ /* @__PURE__ */ jsx68("span", { className: "px-2 py-2 text-xs font-semibold text-gray-700 min-w-[36px] text-center flex-shrink-0", children: currentPage }),
6271
+ /* @__PURE__ */ jsx68(
6272
+ "button",
6273
+ {
6274
+ onClick: () => handlePageChange(currentPage + 1),
6275
+ disabled: currentPage >= rawMeta.pages || localLoading,
6276
+ className: "flex-1 px-3 py-2 text-xs font-medium rounded-lg border bg-white shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center justify-center space-x-1 min-w-0",
6277
+ children: /* @__PURE__ */ jsx68("span", { children: "Next \u2192" })
6278
+ }
6279
+ )
6280
+ ] }),
6281
+ /* @__PURE__ */ jsxs42("div", { className: "hidden sm:flex items-center space-x-2", children: [
6282
+ /* @__PURE__ */ jsx68(
6283
+ "button",
6284
+ {
6285
+ onClick: () => handlePageChange(currentPage - 1),
6286
+ disabled: currentPage === 1 || localLoading,
6287
+ className: "px-3 py-1.5 text-sm font-medium rounded-lg border bg-white shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center space-x-1",
6288
+ children: /* @__PURE__ */ jsx68("span", { children: "\u2190 Prev" })
6289
+ }
6290
+ ),
6291
+ /* @__PURE__ */ jsx68("span", { className: "px-3 py-1 text-sm font-medium text-gray-700", children: currentPage }),
6292
+ /* @__PURE__ */ jsx68(
6293
+ "button",
6294
+ {
6295
+ onClick: () => handlePageChange(currentPage + 1),
6296
+ disabled: currentPage >= rawMeta.pages || localLoading,
6297
+ className: "px-3 py-1.5 text-sm font-medium rounded-lg border bg-white shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center space-x-1",
6298
+ children: /* @__PURE__ */ jsx68("span", { children: "Next \u2192" })
6299
+ }
6300
+ )
6301
+ ] })
6302
+ ] }),
6303
+ /* @__PURE__ */ jsx68(ResponsiveContainer, { width: "100%", height: "100%", children: chartType === "bar" ? /* @__PURE__ */ jsxs42(BarChart, { data, children: [
6304
+ /* @__PURE__ */ jsx68(CartesianGrid, { strokeDasharray: "3 3" }),
6305
+ /* @__PURE__ */ jsx68(
6306
+ XAxis,
6307
+ {
6308
+ dataKey: dataLabel,
6309
+ angle: -45,
6310
+ textAnchor: "end",
6311
+ interval: 0,
6312
+ tickFormatter: (value) => value?.toString().length > 10 ? `${value.toString().substring(0, 10)}...` : value,
6313
+ tick: {
6314
+ fontSize: 13,
6315
+ fontWeight: 500,
6316
+ fill: "#666",
6317
+ fontFamily: "inherit"
6318
+ },
6319
+ height: 80,
6320
+ className: "hidden sm:block"
6321
+ }
6322
+ ),
6323
+ /* @__PURE__ */ jsx68(
6324
+ YAxis,
6325
+ {
6326
+ tickFormatter: (value) => `${(value / 1e3).toFixed(0)}k`,
6327
+ tick: {
6328
+ fontSize: 12,
6329
+ fill: "#94a3b8",
6330
+ fontWeight: 500
6331
+ },
6332
+ domain: ["dataMin", "dataMax"],
6333
+ width: 60
6334
+ }
6335
+ ),
6336
+ /* @__PURE__ */ jsx68(Tooltip2, { formatter: (value) => [`${value}`, "Count"] }),
6337
+ /* @__PURE__ */ jsx68(
6338
+ Bar,
6339
+ {
6340
+ dataKey,
6341
+ radius: [6, 6, 0, 0],
6342
+ isAnimationActive: false
6343
+ }
6344
+ )
6345
+ ] }) : /* @__PURE__ */ jsxs42(AreaChart, { data, children: [
6346
+ /* @__PURE__ */ jsx68("defs", { children: /* @__PURE__ */ jsxs42("linearGradient", { id: "colorCount", x1: "0", y1: "0", x2: "0", y2: "1", children: [
6347
+ /* @__PURE__ */ jsx68("stop", { offset: "5%", stopColor: "#00695C", stopOpacity: 0.8 }),
6348
+ /* @__PURE__ */ jsx68("stop", { offset: "95%", stopColor: "#00695C", stopOpacity: 0 })
6349
+ ] }) }),
6350
+ /* @__PURE__ */ jsx68(CartesianGrid, { strokeDasharray: "3 3" }),
6351
+ /* @__PURE__ */ jsx68(
6352
+ XAxis,
6353
+ {
6354
+ dataKey: dataLabel,
6355
+ angle: 0,
6356
+ interval: 0,
6357
+ tick: {
6358
+ fontSize: 13,
6359
+ fontWeight: 500,
6360
+ fill: "#666",
6361
+ fontFamily: "inherit"
6362
+ }
6363
+ }
6364
+ ),
6365
+ /* @__PURE__ */ jsx68(
6366
+ YAxis,
6367
+ {
6368
+ tickFormatter: (value) => `${(value / 1e3).toFixed(0)}k`,
6369
+ tick: {
6370
+ fontSize: 12,
6371
+ fill: "#94a3b8",
6372
+ fontWeight: 500
6373
+ },
6374
+ domain: ["dataMin", "dataMax"],
6375
+ width: 60
6376
+ }
6377
+ ),
6378
+ /* @__PURE__ */ jsx68(Tooltip2, { formatter: (value) => `${value}k` }),
6379
+ /* @__PURE__ */ jsx68(
6380
+ Area,
6381
+ {
6382
+ type: "monotone",
6383
+ dataKey,
6384
+ stroke: "#00695C",
6385
+ fillOpacity: 1,
6386
+ fill: "url(#colorCount)",
6387
+ isAnimationActive: false
6388
+ }
6389
+ )
6390
+ ] }) })
6391
+ ]
6161
6392
  }
6162
6393
  ),
6163
- /* @__PURE__ */ jsx68("span", { className: "px-3 py-1 text-sm font-medium text-gray-700", children: currentPage }),
6164
- /* @__PURE__ */ jsx68(
6165
- "button",
6166
- {
6167
- onClick: () => handlePageChange(currentPage + 1),
6168
- disabled: currentPage >= rawMeta.pages || localLoading,
6169
- className: "px-3 py-1.5 text-sm font-medium rounded-lg border bg-white shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center space-x-1",
6170
- children: /* @__PURE__ */ jsx68("span", { children: "Next \u2192" })
6171
- }
6172
- )
6173
- ] })
6174
- ] }),
6175
- /* @__PURE__ */ jsx68(ResponsiveContainer, { width: "100%", height: "100%", children: chartType === "bar" ? /* @__PURE__ */ jsxs42(BarChart, { data, children: [
6176
- /* @__PURE__ */ jsx68(CartesianGrid, { strokeDasharray: "3 3" }),
6177
- /* @__PURE__ */ jsx68(
6178
- XAxis,
6179
- {
6180
- dataKey: dataLabel,
6181
- angle: -45,
6182
- textAnchor: "end",
6183
- interval: 0,
6184
- tickFormatter: (value) => value?.toString().length > 10 ? `${value.toString().substring(0, 10)}...` : value,
6185
- tick: {
6186
- fontSize: 13,
6187
- fontWeight: 500,
6188
- fill: "#666",
6189
- fontFamily: "inherit"
6190
- },
6191
- height: 80,
6192
- className: "hidden sm:block"
6193
- }
6194
- ),
6195
- /* @__PURE__ */ jsx68(
6196
- YAxis,
6197
- {
6198
- tickFormatter: (value) => `${(value / 1e3).toFixed(0)}k`,
6199
- tick: {
6200
- fontSize: 12,
6201
- fill: "#94a3b8",
6202
- fontWeight: 500
6203
- },
6204
- domain: ["dataMin", "dataMax"],
6205
- width: 60
6206
- }
6207
- ),
6208
- /* @__PURE__ */ jsx68(Tooltip2, { formatter: (value) => [`${value}`, "Count"] }),
6209
- /* @__PURE__ */ jsx68(Legend, { verticalAlign: legendsPosition, align: "center" }),
6210
- /* @__PURE__ */ jsx68(
6211
- Bar,
6212
- {
6213
- dataKey,
6214
- radius: [6, 6, 0, 0],
6215
- isAnimationActive: false
6216
- }
6217
- )
6218
- ] }) : /* @__PURE__ */ jsxs42(AreaChart, { data, children: [
6219
- /* @__PURE__ */ jsx68("defs", { children: /* @__PURE__ */ jsxs42("linearGradient", { id: "colorCount", x1: "0", y1: "0", x2: "0", y2: "1", children: [
6220
- /* @__PURE__ */ jsx68("stop", { offset: "5%", stopColor: "#00695C", stopOpacity: 0.8 }),
6221
- /* @__PURE__ */ jsx68("stop", { offset: "95%", stopColor: "#00695C", stopOpacity: 0 })
6222
- ] }) }),
6223
- /* @__PURE__ */ jsx68(CartesianGrid, { strokeDasharray: "3 3" }),
6224
- /* @__PURE__ */ jsx68(
6225
- XAxis,
6226
- {
6227
- dataKey: dataLabel,
6228
- angle: 0,
6229
- interval: 0,
6230
- tick: {
6231
- fontSize: 13,
6232
- fontWeight: 500,
6233
- fill: "#666",
6234
- fontFamily: "inherit"
6235
- }
6236
- }
6237
- ),
6238
- /* @__PURE__ */ jsx68(
6239
- YAxis,
6240
- {
6241
- tickFormatter: (value) => `${(value / 1e3).toFixed(0)}k`,
6242
- tick: {
6243
- fontSize: 12,
6244
- fill: "#94a3b8",
6245
- fontWeight: 500
6246
- },
6247
- domain: ["dataMin", "dataMax"],
6248
- width: 60
6249
- }
6250
- ),
6251
- /* @__PURE__ */ jsx68(Tooltip2, { formatter: (value) => `${value}k` }),
6252
- /* @__PURE__ */ jsx68(
6253
- Area,
6254
- {
6255
- type: "monotone",
6256
- dataKey,
6257
- stroke: "#00695C",
6258
- fillOpacity: 1,
6259
- fill: "url(#colorCount)",
6260
- isAnimationActive: false
6261
- }
6262
- )
6263
- ] }) })
6264
- ] });
6394
+ showLegends && /* @__PURE__ */ jsx68("div", { className: isLegendRight ? "flex flex-col w-[30%] min-w-[180px] justify-center" : "w-full", children: renderLegends })
6395
+ ]
6396
+ }
6397
+ );
6265
6398
  };
6266
6399
  var BarChart_default = React14.memo(ChartComponent);
6267
6400
 
@@ -6276,8 +6409,8 @@ import {
6276
6409
  Tooltip as Tooltip3
6277
6410
  } from "recharts";
6278
6411
  import { jsx as jsx69, jsxs as jsxs43 } from "react/jsx-runtime";
6279
- var getRandomColor2 = () => {
6280
- const palette = [
6412
+ var getRandomColor = () => {
6413
+ const palette2 = [
6281
6414
  "#2563eb",
6282
6415
  "#1d4ed8",
6283
6416
  "#1e40af",
@@ -6339,7 +6472,7 @@ var getRandomColor2 = () => {
6339
6472
  "#155e75",
6340
6473
  "#164e63"
6341
6474
  ];
6342
- return palette[Math.floor(Math.random() * palette.length)];
6475
+ return palette2[Math.floor(Math.random() * palette2.length)];
6343
6476
  };
6344
6477
  var DonutChart = ({
6345
6478
  className,
@@ -6348,16 +6481,20 @@ var DonutChart = ({
6348
6481
  dataKey = "value",
6349
6482
  dataLabel = "name",
6350
6483
  apiUrl,
6484
+ source,
6485
+ legendPosition = "bottom",
6486
+ onLegendClick,
6351
6487
  ...props
6352
6488
  }) => {
6353
6489
  const showLegends = props.showLegends ?? true;
6354
6490
  const canvasMode = props.canvasMode;
6491
+ const useApi = source === "api" && !!apiUrl;
6355
6492
  const [rawData, setRawData] = useState16([]);
6356
6493
  const [localLoading, setLocalLoading] = useState16(false);
6357
- const effectiveData = apiUrl ? rawData : props.data || [];
6358
- const effectiveLoading = apiUrl ? localLoading : externalLoading;
6494
+ const effectiveData = useApi ? rawData : props.data || [];
6495
+ const effectiveLoading = useApi ? localLoading : externalLoading;
6359
6496
  useEffect30(() => {
6360
- if (!apiUrl) return;
6497
+ if (!useApi) return;
6361
6498
  let cancelled = false;
6362
6499
  const fetchData = async () => {
6363
6500
  try {
@@ -6391,12 +6528,12 @@ var DonutChart = ({
6391
6528
  return () => {
6392
6529
  cancelled = true;
6393
6530
  };
6394
- }, [apiUrl]);
6531
+ }, [useApi, apiUrl]);
6395
6532
  const data = useMemo12(() => {
6396
6533
  if (!Array.isArray(effectiveData) || effectiveData.length === 0) return [];
6397
6534
  return effectiveData.map((item) => ({
6398
6535
  ...item,
6399
- color: item.color || getRandomColor2(),
6536
+ color: item.color || getRandomColor(),
6400
6537
  [dataKey]: Number(item[dataKey] ?? 0),
6401
6538
  [dataLabel]: item[dataLabel] ?? "Unknown"
6402
6539
  }));
@@ -6444,42 +6581,59 @@ var DonutChart = ({
6444
6581
  }, []);
6445
6582
  const renderLegends = useMemo12(() => {
6446
6583
  if (!showLegends) return null;
6447
- return /* @__PURE__ */ jsx69("div", { className: "flex flex-wrap justify-center gap-2 mt-4 w-full max-w-4xl", children: chartData.map((d, index) => {
6448
- const actualValue = data.find(
6449
- (item) => item[dataLabel] === d[dataLabel]
6450
- )?.[dataKey] ?? d[dataKey];
6451
- const displayValue = actualValue >= 1e3 ? `${(actualValue / 1e3).toFixed(0)}k` : actualValue.toLocaleString();
6452
- return /* @__PURE__ */ jsxs43(
6453
- "div",
6454
- {
6455
- className: "flex items-center space-x-2 rounded-lg border border-gray-200/50 px-3 py-1.5 w-[48%] sm:w-[32%] md:w-auto bg-white/80 backdrop-blur-sm shadow-sm hover:shadow-md transition-all",
6456
- children: [
6457
- /* @__PURE__ */ jsx69(
6458
- "span",
6459
- {
6460
- className: "inline-block w-[12px] h-[12px] rounded-full shrink-0 border-2 border-white/50",
6461
- style: { backgroundColor: d.color }
6462
- }
6463
- ),
6464
- /* @__PURE__ */ jsxs43("div", { className: "min-w-0 flex-1", children: [
6465
- /* @__PURE__ */ jsx69("span", { className: "text-gray-900 text-[11px] md:text-[13px] font-semibold block truncate leading-tight", children: d[dataLabel] }),
6466
- /* @__PURE__ */ jsxs43("div", { className: "flex items-center gap-1 text-xs text-gray-600 font-medium", children: [
6467
- /* @__PURE__ */ jsx69("span", { children: displayValue }),
6468
- /* @__PURE__ */ jsxs43("span", { children: [
6469
- (actualValue / total * 100).toFixed(1),
6470
- "%"
6471
- ] }),
6472
- d.isBoosted && /* @__PURE__ */ jsx69("span", { className: "text-[9px] px-1 py-0.5 bg-blue-100 text-blue-700 rounded-full", children: "min" })
6473
- ] })
6474
- ] })
6475
- ]
6476
- },
6477
- `legend-${index}`
6478
- );
6479
- }) });
6480
- }, [chartData, data, dataLabel, dataKey, total, showLegends]);
6584
+ const isLegendRight2 = !forceMobile && legendPosition === "right";
6585
+ return /* @__PURE__ */ jsx69(
6586
+ "div",
6587
+ {
6588
+ className: isLegendRight2 ? "flex flex-col gap-2 w-full min-w-0 justify-start" : "flex flex-wrap justify-center gap-2 mt-4 w-full max-w-4xl",
6589
+ children: chartData.map((d, index) => {
6590
+ const actualValue = data.find(
6591
+ (item) => item[dataLabel] === d[dataLabel]
6592
+ )?.[dataKey] ?? d[dataKey];
6593
+ const displayValue = actualValue >= 1e3 ? `${(actualValue / 1e3).toFixed(0)}k` : actualValue.toLocaleString();
6594
+ const payload = {
6595
+ name: d[dataLabel],
6596
+ value: actualValue,
6597
+ [dataLabel]: d[dataLabel],
6598
+ [dataKey]: actualValue
6599
+ };
6600
+ return /* @__PURE__ */ jsxs43(
6601
+ "div",
6602
+ {
6603
+ role: onLegendClick ? "button" : void 0,
6604
+ tabIndex: onLegendClick ? 0 : void 0,
6605
+ onClick: onLegendClick ? () => onLegendClick(payload) : void 0,
6606
+ onKeyDown: onLegendClick ? (e) => e.key === "Enter" && onLegendClick(payload) : void 0,
6607
+ className: `flex items-center space-x-2 rounded-lg border border-gray-200/50 px-3 py-1.5 w-[48%] sm:w-[32%] md:w-auto bg-white/80 backdrop-blur-sm shadow-sm hover:shadow-md transition-all ${onLegendClick ? "cursor-pointer" : ""}`,
6608
+ children: [
6609
+ /* @__PURE__ */ jsx69(
6610
+ "span",
6611
+ {
6612
+ className: "inline-block w-[12px] h-[12px] rounded-full shrink-0 border-2 border-white/50",
6613
+ style: { backgroundColor: d.color }
6614
+ }
6615
+ ),
6616
+ /* @__PURE__ */ jsxs43("div", { className: "min-w-0 flex-1", children: [
6617
+ /* @__PURE__ */ jsx69("span", { className: "text-gray-900 text-[11px] md:text-[13px] font-semibold block truncate leading-tight", children: d[dataLabel] }),
6618
+ /* @__PURE__ */ jsxs43("div", { className: "flex items-center gap-1 text-xs text-gray-600 font-medium", children: [
6619
+ /* @__PURE__ */ jsx69("span", { children: displayValue }),
6620
+ /* @__PURE__ */ jsxs43("span", { children: [
6621
+ (actualValue / total * 100).toFixed(1),
6622
+ "%"
6623
+ ] }),
6624
+ d.isBoosted && /* @__PURE__ */ jsx69("span", { className: "text-[9px] px-1 py-0.5 bg-blue-100 text-blue-700 rounded-full", children: "min" })
6625
+ ] })
6626
+ ] })
6627
+ ]
6628
+ },
6629
+ `legend-${index}`
6630
+ );
6631
+ })
6632
+ }
6633
+ );
6634
+ }, [chartData, data, dataLabel, dataKey, total, showLegends, onLegendClick, legendPosition, forceMobile]);
6481
6635
  if (!mounted) return null;
6482
- if (effectiveLoading || data.length === 0) {
6636
+ if (effectiveLoading) {
6483
6637
  return /* @__PURE__ */ jsxs43(
6484
6638
  "div",
6485
6639
  {
@@ -6509,66 +6663,90 @@ var DonutChart = ({
6509
6663
  }
6510
6664
  );
6511
6665
  }
6666
+ if (data.length === 0) {
6667
+ return /* @__PURE__ */ jsx69(
6668
+ "div",
6669
+ {
6670
+ className: `relative flex flex-col items-center justify-center w-full h-[300px] md:h-[400px] bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl p-6 ${className}`,
6671
+ style,
6672
+ children: /* @__PURE__ */ jsx69("div", { className: "text-center", children: /* @__PURE__ */ jsx69("div", { className: "inline-flex items-center space-x-2 bg-white/80 px-6 py-3 rounded-xl backdrop-blur-sm border border-gray-200 shadow-lg", children: /* @__PURE__ */ jsx69("span", { className: "text-sm font-medium text-gray-600", children: "No data" }) }) })
6673
+ }
6674
+ );
6675
+ }
6512
6676
  const { inner, outer } = getDynamicRadius();
6513
6677
  const innerRadius = inner;
6514
6678
  const outerRadius = outer;
6515
- return /* @__PURE__ */ jsxs43("div", { className: `relative flex flex-col items-center ${className}`, style, children: [
6516
- /* @__PURE__ */ jsxs43("div", { className: "relative w-full md:w-[75%] h-[280px] md:h-[380px] flex items-center justify-center mb-2", children: [
6517
- /* @__PURE__ */ jsx69(ResponsiveContainer2, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs43(PieChart, { children: [
6518
- /* @__PURE__ */ jsx69(
6519
- Pie,
6679
+ const isLegendRight = !forceMobile && legendPosition === "right";
6680
+ return /* @__PURE__ */ jsxs43(
6681
+ "div",
6682
+ {
6683
+ className: `relative flex ${isLegendRight ? "flex-row items-stretch gap-4" : "flex-col items-center"} ${className}`,
6684
+ style,
6685
+ children: [
6686
+ /* @__PURE__ */ jsxs43(
6687
+ "div",
6520
6688
  {
6521
- data: chartData,
6522
- cx: "50%",
6523
- cy: "50%",
6524
- innerRadius,
6525
- outerRadius,
6526
- dataKey,
6527
- labelLine: false,
6528
- isAnimationActive: true,
6529
- animationDuration: 800,
6530
- minAngle: 3,
6531
- children: chartData.map((entry, index) => /* @__PURE__ */ jsx69(
6532
- Cell,
6533
- {
6534
- fill: entry.color,
6535
- stroke: entry.isBoosted ? "#fff" : "transparent",
6536
- strokeWidth: entry.isBoosted ? 1.5 : 0
6537
- },
6538
- `cell-${index}`
6539
- ))
6689
+ className: `relative flex items-center justify-center ${isLegendRight ? "flex-[2] min-w-0 max-w-[70%]" : "w-full md:w-[75%]"} h-[280px] md:h-[380px] ${isLegendRight ? "" : "mb-2"}`,
6690
+ children: [
6691
+ /* @__PURE__ */ jsx69(ResponsiveContainer2, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs43(PieChart, { children: [
6692
+ /* @__PURE__ */ jsx69(
6693
+ Pie,
6694
+ {
6695
+ data: chartData,
6696
+ cx: "50%",
6697
+ cy: "50%",
6698
+ innerRadius,
6699
+ outerRadius,
6700
+ dataKey,
6701
+ labelLine: false,
6702
+ isAnimationActive: true,
6703
+ animationDuration: 800,
6704
+ minAngle: 3,
6705
+ children: chartData.map((entry, index) => /* @__PURE__ */ jsx69(
6706
+ Cell,
6707
+ {
6708
+ fill: entry.color,
6709
+ stroke: entry.isBoosted ? "#fff" : "transparent",
6710
+ strokeWidth: entry.isBoosted ? 1.5 : 0
6711
+ },
6712
+ `cell-${index}`
6713
+ ))
6714
+ }
6715
+ ),
6716
+ /* @__PURE__ */ jsx69(
6717
+ Tooltip3,
6718
+ {
6719
+ formatter: (value, name, payload) => {
6720
+ const label = payload && payload.payload ? payload.payload[dataLabel] : name;
6721
+ const actualItem = data.find((item) => item[dataLabel] === label);
6722
+ const actualValue = actualItem ? actualItem[dataKey] : value;
6723
+ const valueFormatted = actualValue >= 1e3 ? `${(actualValue / 1e3).toFixed(0)}k` : actualValue.toLocaleString();
6724
+ const percentage = (actualValue / total * 100).toFixed(1);
6725
+ return [
6726
+ `${label}: ${valueFormatted} (${percentage}%)`
6727
+ ];
6728
+ },
6729
+ contentStyle: {
6730
+ backgroundColor: "white",
6731
+ border: "1px solid #e5e7eb",
6732
+ borderRadius: "8px",
6733
+ fontSize: "13px",
6734
+ padding: "8px 12px"
6735
+ }
6736
+ }
6737
+ )
6738
+ ] }) }),
6739
+ total > 0 && /* @__PURE__ */ jsx69("div", { className: `absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-center pointer-events-none ${forceMobile ? "text-xl px-2" : "text-3xl px-4"} font-bold bg-white/90 backdrop-blur-sm rounded-full py-1 shadow-lg`, children: /* @__PURE__ */ jsxs43("div", { className: "text-[#1f2937] leading-tight", children: [
6740
+ formattedTotal,
6741
+ /* @__PURE__ */ jsx69("span", { className: "text-sm md:text-base font-normal text-gray-600 block md:inline-block md:ml-1", children: "total" })
6742
+ ] }) })
6743
+ ]
6540
6744
  }
6541
6745
  ),
6542
- /* @__PURE__ */ jsx69(
6543
- Tooltip3,
6544
- {
6545
- formatter: (value, name, payload) => {
6546
- const label = payload && payload.payload ? payload.payload[dataLabel] : name;
6547
- const actualItem = data.find((item) => item[dataLabel] === label);
6548
- const actualValue = actualItem ? actualItem[dataKey] : value;
6549
- const valueFormatted = actualValue >= 1e3 ? `${(actualValue / 1e3).toFixed(0)}k` : actualValue.toLocaleString();
6550
- const percentage = (actualValue / total * 100).toFixed(1);
6551
- return [
6552
- `${label}: ${valueFormatted} (${percentage}%)`
6553
- ];
6554
- },
6555
- contentStyle: {
6556
- backgroundColor: "white",
6557
- border: "1px solid #e5e7eb",
6558
- borderRadius: "8px",
6559
- fontSize: "13px",
6560
- padding: "8px 12px"
6561
- }
6562
- }
6563
- )
6564
- ] }) }),
6565
- total > 0 && /* @__PURE__ */ jsx69("div", { className: `absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-center pointer-events-none ${forceMobile ? "text-xl px-2" : "text-3xl px-4"} font-bold bg-white/90 backdrop-blur-sm rounded-full py-1 shadow-lg`, children: /* @__PURE__ */ jsxs43("div", { className: "text-[#1f2937] leading-tight", children: [
6566
- formattedTotal,
6567
- /* @__PURE__ */ jsx69("span", { className: "text-sm md:text-base font-normal text-gray-600 block md:inline-block md:ml-1", children: "total" })
6568
- ] }) })
6569
- ] }),
6570
- renderLegends
6571
- ] });
6746
+ showLegends && /* @__PURE__ */ jsx69("div", { className: isLegendRight ? "flex flex-col w-[30%] min-w-[180px] justify-center" : "w-full", children: renderLegends })
6747
+ ]
6748
+ }
6749
+ );
6572
6750
  };
6573
6751
  var PieChart_default = React15.memo(DonutChart);
6574
6752