From 699548c674c3872f6ec17c19731f63274174d429 Mon Sep 17 00:00:00 2001 From: Saeed <42695081+saeed0920@users.noreply.github.com> Date: Wed, 13 Aug 2025 18:15:02 +0330 Subject: [PATCH] Refactor process impacts chart to use new CustomBarChart component (#3) Co-authored-by: Cursor Agent --- .../process-innovation-page.tsx | 136 +++++------------ app/components/ui/custom-bar-chart.tsx | 144 ++++++++++++++++++ 2 files changed, 178 insertions(+), 102 deletions(-) create mode 100644 app/components/ui/custom-bar-chart.tsx diff --git a/app/components/dashboard/project-management/process-innovation-page.tsx b/app/components/dashboard/project-management/process-innovation-page.tsx index b4e7eaa..7016940 100644 --- a/app/components/dashboard/project-management/process-innovation-page.tsx +++ b/app/components/dashboard/project-management/process-innovation-page.tsx @@ -4,6 +4,8 @@ import { Card, CardContent } from "~/components/ui/card"; import { Button } from "~/components/ui/button"; import { Badge } from "~/components/ui/badge"; import { Checkbox } from "~/components/ui/checkbox"; +import { CustomBarChart } from "~/components/ui/custom-bar-chart"; +import type { BarChartData } from "~/components/ui/custom-bar-chart"; import { Table, TableBody, @@ -583,108 +585,38 @@ export function ProcessInnovationPage() { {/* Process Impacts Chart */} -
-

- تاثیرات فرآیندی به صورت درصد مقایسه ای -

-
- - {/* Chart Container */} -
- {/* Chart Item 1 */} -
- کاهش توقفات تولید -
-
-
-
- - {formatNumber(((stats.percentProductionStops ?? 0) as number).toFixed?.(1) ?? (stats.percentProductionStops ?? 0))}% - -
- - {/* Chart Item 2 */} -
- رفع گلوگاه تولید -
-
-
-
- - {formatNumber(((stats.percentBottleneckRemoval ?? 0) as number).toFixed?.(1) ?? (stats.percentBottleneckRemoval ?? 0))}% - -
- - {/* Chart Item 3 */} -
- کاهش ارز بری -
-
-
-
- - {formatNumber(((stats.percentCurrencyReduction ?? 0) as number).toFixed?.(0) ?? (stats.percentCurrencyReduction ?? 0))}% - -
- - {/* Chart Item 4 */} -
- کاهش خرابی پر تکرار -
-
-
-
- - {formatNumber(((stats.percentFailuresReduction ?? 0) as number).toFixed?.(1) ?? (stats.percentFailuresReduction ?? 0))}% - -
-
- - -
- ۰٪ - { - (() => { - const p1 = (stats.percentProductionStops || 0); - const p2 = (stats.percentBottleneckRemoval || 0); - const p3 = (stats.percentCurrencyReduction || 0); - const p4 = (stats.percentFailuresReduction || 0); - const maxVal = Math.max(p1, p2, p3, p4); - return ( - <> - {formatNumber(Math.round(maxVal / 4))}٪ - {formatNumber(Math.round(maxVal / 2))}٪ - {formatNumber(Math.round((maxVal * 3) / 4))}٪ - {formatNumber(Math.round(maxVal))}٪ - - ); - })() - } -
-
-
- - {/* Percentage Scale */} +
diff --git a/app/components/ui/custom-bar-chart.tsx b/app/components/ui/custom-bar-chart.tsx new file mode 100644 index 0000000..86b8ad2 --- /dev/null +++ b/app/components/ui/custom-bar-chart.tsx @@ -0,0 +1,144 @@ +import * as React from "react"; + +export interface BarChartData { + label: string; + value: number; + maxValue?: number; + color?: string; + labelColor?: string; + valuePrefix?: string; + valueSuffix?: string; +} + +interface CustomBarChartProps { + data: BarChartData[]; + title?: string; + height?: string; + barHeight?: string; + showAxisLabels?: boolean; + className?: string; + loading?: boolean; +} + +export function CustomBarChart({ + data, + title, + height = "auto", + barHeight = "h-6", + showAxisLabels = true, + className = "", + loading = false +}: CustomBarChartProps) { + // Calculate the maximum value across all data points for consistent scaling + const globalMaxValue = Math.max(...data.map(item => item.maxValue || item.value)); + + // Function to format numbers with Persian digits + const formatNumber = (value: number): string => { + const str = String(value); + const map: Record = { + "0": "۰", "1": "۱", "2": "۲", "3": "۳", "4": "۴", + "5": "۵", "6": "۶", "7": "۷", "8": "۸", "9": "۹" + }; + return str.replace(/[0-9]/g, (d) => map[d] ?? d); + }; + + // Loading skeleton + if (loading) { + return ( +
+ {title && ( +
+ )} + +
+ {Array.from({ length: 4 }).map((_, index) => ( +
+ {/* Label skeleton */} +
+ + {/* Bar skeleton */} +
+
+
+ + {/* Value skeleton */} +
+
+ ))} +
+
+ ); + } + + return ( +
+ {title && ( +

+ {title} +

+ )} + +
+ {data.map((item, index) => { + const percentage = globalMaxValue > 0 ? (item.value / globalMaxValue) * 100 : 0; + const displayValue = item.value.toFixed(1); + + return ( +
+ {/* Label */} + + {item.label} + + + {/* Bar Container */} +
+
+ {/* Add a subtle gradient effect for better visual appeal */} +
+
+
+ + {/* Value Label */} + + {item.valuePrefix || ''}{formatNumber(parseFloat(displayValue))}{item.valueSuffix || '%'} + +
+ ); + })} + + {/* Axis Labels */} + {showAxisLabels && globalMaxValue > 0 && ( +
+ +
+ {formatNumber(0)}% + {formatNumber(Math.round(globalMaxValue / 4))}% + {formatNumber(Math.round(globalMaxValue / 2))}% + {formatNumber(Math.round((globalMaxValue * 3) / 4))}% + {formatNumber(Math.round(globalMaxValue))}% +
+ +
+ )} +
+
+ ); +} \ No newline at end of file