diff --git a/app/components/dashboard/project-management/mange-ideas-tech-page.tsx b/app/components/dashboard/project-management/mange-ideas-tech-page.tsx index e451903..f3a94e7 100644 --- a/app/components/dashboard/project-management/mange-ideas-tech-page.tsx +++ b/app/components/dashboard/project-management/mange-ideas-tech-page.tsx @@ -1,5 +1,5 @@ import { ChevronDown, ChevronUp, RefreshCw, Eye, Star, TrendingUp, Hexagon, Download } from "lucide-react"; -import { useCallback, useEffect, useRef, useState, useMemo } from "react"; +import { useCallback, useEffect, useRef, useState, useMemo, memo } from "react"; import toast from "react-hot-toast"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; @@ -92,6 +92,135 @@ const columns: ColumnDef[] = [ { key: "details", label: "جزئیات بیشتر", sortable: false, width: "120px" }, ]; +// Memoized Vertical Bar Chart Component +const VerticalBarChart = memo<{ + chartData: IdeaStatusData[]; + loadingChart: boolean; + chartConfig: ChartConfig; + getChartStatusColor: (status: string) => string; + toPersianDigits: (input: string | number) => string; + formatNumber: (value: number) => string; +}>(({ chartData, loadingChart, chartConfig, getChartStatusColor, toPersianDigits, formatNumber }) => { + if (loadingChart) { + return ( +
+ {/* Chart title skeleton */} +
+ + {/* Chart area skeleton */} +
+ {/* Y-axis labels */} +
+ {Array.from({ length: 4 }).map((_, i) => ( +
+ ))} +
+ + {/* Bars skeleton */} +
+ {Array.from({ length: 4 }).map((_, i) => ( +
+ {/* Bar */} +
+ {/* X-axis label */} +
+
+ ))} +
+
+
+ ); + } + + if (!chartData.length) { + return ( +
+

وضعیت ایده ها

+

هیچ داده‌ای یافت نشد

+
+ ); + } + + // Prepare data for recharts + const rechartData = useMemo(() => chartData.map((item) => ({ + status: item.idea_status, + count: item.idea_status_count, + fill: getChartStatusColor(item.idea_status), + })), [chartData, getChartStatusColor]); + + return ( + + + + + + toPersianDigits(value)} + label={{ + value: "تعداد برنامه ها", + angle: -90, + position: "insideLeft", + fill: "#94a3b8", + fontSize: 11, + offset: 0, + dy: 0, + style: { textAnchor: "middle" }, + }} + /> + + `${formatNumber(Math.round(v))}`} + /> + + + + + ); +}); + +const MemoizedVerticalBarChart = VerticalBarChart; + export function ManageIdeasTechPage() { const [ideas, setIdeas] = useState([]); const [loading, setLoading] = useState(false); @@ -446,7 +575,7 @@ export function ManageIdeasTechPage() { } }; - const toPersianDigits = (input: string | number): string => { + const toPersianDigits = useCallback((input: string | number): string => { const str = String(input); const map: Record = { "0": "۰", @@ -461,7 +590,7 @@ export function ManageIdeasTechPage() { "9": "۹", }; return str.replace(/[0-9]/g, (d) => map[d] ?? d); - }; + }, []); const formatDate = (dateString: string | null) => { if (!dateString || dateString === "null" || dateString.trim() === "") { @@ -492,15 +621,15 @@ export function ManageIdeasTechPage() { }; // Chart configuration for shadcn/ui - const chartConfig: ChartConfig = { + const chartConfig: ChartConfig = useMemo(() => ({ count: { label: "تعداد", }, - }; + }), []); // Color palette for idea status // Specific colors for idea statuses - const getChartStatusColor = (status: string) => { + const getChartStatusColor = useCallback((status: string) => { switch (status) { case "اجرا شده": return "#69C8EA"; @@ -513,7 +642,7 @@ export function ManageIdeasTechPage() { default: return "#6B7280"; } - }; + }, []); const statusColorPalette = ["#3AEA83", "#69C8EA", "#F76276", "#FFD700", "#A757FF", "#E884CE", "#C3BF8B", "#FB7185"]; @@ -601,124 +730,7 @@ export function ManageIdeasTechPage() { } }; - // Custom Vertical Bar Chart Component using shadcn/ui - const VerticalBarChart = () => { - if (loadingChart) { - return ( -
- {/* Chart title skeleton */} -
- {/* Chart area skeleton */} -
- {/* Y-axis labels */} -
- {Array.from({ length: 4 }).map((_, i) => ( -
- ))} -
- - {/* Bars skeleton */} -
- {Array.from({ length: 4 }).map((_, i) => ( -
- {/* Bar */} -
- {/* X-axis label */} -
-
- ))} -
-
-
- ); - } - - if (!chartData.length) { - return ( -
-

وضعیت ایده ها

-

هیچ داده‌ای یافت نشد

-
- ); - } - - // Prepare data for recharts - const rechartData = chartData.map((item) => ({ - status: item.idea_status, - count: item.idea_status_count, - fill: getChartStatusColor(item.idea_status), - })); - - return ( - - - - - - toPersianDigits(value)} - label={{ -value: "تعداد برنامه ها" , -angle: -90, -position: "insideLeft", -fill: "#94a3b8", -fontSize: 11, -offset: 0, -dy: 0, -style: { textAnchor: "middle" }, -}} - /> - - `${formatNumber(Math.round(v))}`} - /> - - - - - ); - }; return ( @@ -727,7 +739,7 @@ style: { textAnchor: "middle" },
{/* People Ranking Table */}
-

+

رتبه بندی نوآوران

@@ -819,7 +831,7 @@ style: { textAnchor: "middle" }, {/* Main Ideas Table */}
-

+

لیست ایده ها

@@ -827,7 +839,7 @@ style: { textAnchor: "middle" },
@@ -941,10 +953,17 @@ style: { textAnchor: "middle" }, {/* Chart Section */} - - + + -
+
{loadingStats ? (
diff --git a/app/components/dashboard/project-management/product-innovation-page.tsx b/app/components/dashboard/project-management/product-innovation-page.tsx index c1c8679..7d1aa1b 100644 --- a/app/components/dashboard/project-management/product-innovation-page.tsx +++ b/app/components/dashboard/project-management/product-innovation-page.tsx @@ -672,9 +672,9 @@ export function ProductInnovationPage() { return ( -
+
{/* Stats Cards */} -
+
{/* Stats Grid */}
@@ -793,7 +793,7 @@ export function ProductInnovationPage() {
{/* Data Table */} - +