inogen/app/components/dashboard/project-management/process-innovation-page.tsx
Saeed 699548c674
Refactor process impacts chart to use new CustomBarChart component (#3)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-08-13 18:15:02 +03:30

890 lines
33 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect, useCallback, useRef } from "react";
import { DashboardLayout } from "../layout";
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,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "~/components/ui/table";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "~/components/ui/dialog";
import {
ChevronUp,
ChevronDown,
RefreshCw,
ExternalLink,
} from "lucide-react";
import apiService from "~/lib/api";
import toast from "react-hot-toast";
import {Funnel, Wrench , CirclePause , DollarSign} from "lucide-react"
interface ProcessInnovationData {
project_no: string;
title: string;
project_status: string;
project_rating: string;
reduce_prevention_production_stops: string;
throat_removal: string;
amount_currency_reduction: string;
Reduce_rate_failure: string;
}
interface SortConfig {
field: string;
direction: "asc" | "desc";
}
interface StatsCard {
id: string;
title: string;
value: string;
description: string;
icon: React.ReactNode;
color: string;
}
interface InnovationStats {
totalProjects: number;
averageScore: number;
productionStopsPreventionSum: number; // مجموع جلوگیری از توقفات تولید
bottleneckRemovalCount: number; // تعداد رفع گلوگاه
currencyReductionSum: number; // مجموع کاهش ارز بری (میلیون ریال)
frequentFailuresReductionSum: number; // مجموع کاهش خرابی های پرتکرار
percentProductionStops: number; // درصد مقایسه‌ای جلوگیری از توقفات تولید
percentBottleneckRemoval: number; // درصد مقایسه‌ای رفع گلوگاه
percentCurrencyReduction: number; // درصد مقایسه‌ای کاهش ارز بری
percentFailuresReduction: number; // درصد مقایسه‌ای کاهش خرابی‌های پرتکرار
}
const columns = [
{ key: "select", label: "", sortable: false, width: "50px" },
{ key: "project_no", label: "شماره پروژه", sortable: true, width: "140px" },
{ key: "title", label: "عنوان پروژه", sortable: true, width: "400px" },
{ key: "project_status", label: "وضعیت پروژه", sortable: true, width: "140px" },
{ key: "project_rating", label: "امتیاز پروژه", sortable: true, width: "140px" },
{ key: "details", label: "جزئیات پروژه", sortable: false, width: "140px" }
];
export function ProcessInnovationPage() {
const [projects, setProjects] = useState<ProcessInnovationData[]>([]);
const [loading, setLoading] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize] = useState(20);
const [hasMore, setHasMore] = useState(true);
const [totalCount, setTotalCount] = useState(0);
const [actualTotalCount, setActualTotalCount] = useState(0);
const [statsLoading, setStatsLoading] = useState(false);
const [stats, setStats] = useState<InnovationStats>({
totalProjects: 0,
averageScore: 0,
productionStopsPreventionSum: 0,
bottleneckRemovalCount: 0,
currencyReductionSum: 0,
frequentFailuresReductionSum: 0,
percentProductionStops: 0,
percentBottleneckRemoval: 0,
percentCurrencyReduction: 0,
percentFailuresReduction: 0,
});
const [sortConfig, setSortConfig] = useState<SortConfig>({
field: "start_date",
direction: "asc",
});
const [selectedProjects, setSelectedProjects] = useState<Set<string>>(new Set());
const [detailsDialogOpen, setDetailsDialogOpen] = useState(false);
const [selectedProjectDetails, setSelectedProjectDetails] = useState<ProcessInnovationData | null>(null);
const observerRef = useRef<HTMLDivElement>(null);
const fetchingRef = useRef(false);
// Selection handlers
const handleSelectAll = () => {
if (selectedProjects.size === projects.length) {
setSelectedProjects(new Set());
} else {
setSelectedProjects(new Set(projects.map(p => p.project_no)));
}
};
const handleSelectProject = (projectNo: string) => {
const newSelected = new Set(selectedProjects);
if (newSelected.has(projectNo)) {
newSelected.delete(projectNo);
} else {
newSelected.add(projectNo);
}
setSelectedProjects(newSelected);
};
const handleProjectDetails = (project: ProcessInnovationData) => {
setSelectedProjectDetails(project);
setDetailsDialogOpen(true);
};
const formatNumber = (value: string | number) => {
if (!value) return "0";
const numericValue = typeof value === "string" ? parseFloat(value) : value;
if (isNaN(numericValue)) return "0";
return new Intl.NumberFormat("fa-IR").format(numericValue);
};
// Stats cards data - computed from projects data
const statsCards: StatsCard[] = [
{
id: "production-stops-prevention",
title: "جلوگیری از توقفات تولید",
value: formatNumber(stats.productionStopsPreventionSum.toFixed?.(1) ?? stats.productionStopsPreventionSum),
description: "ظرفیت افزایش یافته",
icon: <CirclePause/>,
color: "text-emerald-400",
},
{
id: "bottleneck-removal",
title: "رفع گلوگاه",
value: formatNumber(stats.bottleneckRemovalCount),
description: "تعداد رفع گلوگاه",
icon: <Funnel />,
color: "text-emerald-400"
},
{
id: "currency-reduction",
title: "کاهش ارز بری",
value: formatNumber(stats.currencyReductionSum.toFixed?.(0) ?? stats.currencyReductionSum),
description: "دلار کاهش یافته",
icon: <DollarSign/>,
color: "text-emerald-400",
},
{
id: "frequent-failures-reduction",
title: "کاهش خرابیهای پرتکرار",
value: formatNumber(stats.frequentFailuresReductionSum.toFixed?.(1) ?? stats.frequentFailuresReductionSum),
description: "مجموع درصد کاهش خرابی",
icon: <Wrench/>,
color: "text-emerald-400",
}
];
const fetchProjects = async (reset = false) => {
if (fetchingRef.current) {
return;
}
try {
fetchingRef.current = true;
if (reset) {
setLoading(true);
setCurrentPage(1);
} else {
setLoadingMore(true);
}
const pageToFetch = reset ? 1 : currentPage;
const response = await apiService.select({
ProcessName: "project",
OutputFields: [
"project_no",
"title",
"project_status",
"project_rating",
"reduce_prevention_production_stops",
"throat_removal",
"amount_currency_reduction",
"Reduce_rate_failure",
],
Pagination: { PageNumber: pageToFetch, PageSize: pageSize },
Sorts: [[sortConfig.field, sortConfig.direction]],
Conditions: [["type_of_innovation", "=", "نوآوری در فرآیند"]],
});
if (response.state === 0) {
const dataString = response.data;
if (dataString && typeof dataString === "string") {
try {
const parsedData = JSON.parse(dataString);
if (Array.isArray(parsedData)) {
if (reset) {
setProjects(parsedData);
setTotalCount(parsedData.length);
} else {
setProjects((prev) => [...prev, ...parsedData]);
setTotalCount((prev) => prev + parsedData.length);
}
setHasMore(parsedData.length === pageSize);
} else {
if (reset) {
setProjects([]);
setTotalCount(0);
}
setHasMore(false);
}
} catch (parseError) {
console.error("Error parsing project data:", parseError);
if (reset) {
setProjects([]);
setTotalCount(0);
}
setHasMore(false);
}
} else {
if (reset) {
setProjects([]);
setTotalCount(0);
}
setHasMore(false);
}
} else {
toast.error(response.message || "خطا در دریافت اطلاعات پروژه‌ها");
if (reset) {
setProjects([]);
setTotalCount(0);
}
setHasMore(false);
}
} catch (error) {
console.error("Error fetching projects:", error);
toast.error("خطا در دریافت اطلاعات پروژه‌ها");
if (reset) {
setProjects([]);
setTotalCount(0);
}
setHasMore(false);
} finally {
setLoading(false);
setLoadingMore(false);
fetchingRef.current = false;
}
};
const loadMore = useCallback(() => {
if (!loadingMore && hasMore && !loading) {
setCurrentPage((prev) => prev + 1);
}
}, [loadingMore, hasMore, loading]);
useEffect(() => {
fetchProjects(true);
fetchTotalCount();
fetchStats();
}, [sortConfig]);
useEffect(() => {
if (currentPage > 1) {
fetchProjects(false);
}
}, [currentPage]);
useEffect(() => {
const scrollContainer = document.querySelector('.overflow-auto');
const handleScroll = () => {
if (!scrollContainer || !hasMore || loadingMore) return;
const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
const scrollPercentage = (scrollTop + clientHeight) / scrollHeight;
if (scrollPercentage >= 0.9) {
loadMore();
}
};
if (scrollContainer) {
scrollContainer.addEventListener('scroll', handleScroll);
}
return () => {
if (scrollContainer) {
scrollContainer.removeEventListener('scroll', handleScroll);
}
};
}, [loadMore, hasMore, loadingMore]);
const handleSort = (field: string) => {
fetchingRef.current = false;
setSortConfig((prev) => ({
field,
direction:
prev.field === field && prev.direction === "asc" ? "desc" : "asc",
}));
setCurrentPage(1);
setProjects([]);
setHasMore(true);
};
const fetchTotalCount = async () => {
try {
const response = await apiService.select({
ProcessName: "project",
OutputFields: ["count(project_no)"],
Conditions: [["type_of_innovation", "=", "نوآوری در فرآیند"]],
});
if (response.state === 0) {
const dataString = response.data;
if (dataString && typeof dataString === "string") {
try {
const parsedData = JSON.parse(dataString);
if (Array.isArray(parsedData) && parsedData[0]) {
const count = parsedData[0].project_no_count || 0;
setActualTotalCount(count);
// Keep stats in sync if backend stats not yet loaded
setStats((prev) => ({ ...prev, totalProjects: count }));
}
} catch (parseError) {
console.error("Error parsing count data:", parseError);
}
}
}
} catch (error) {
console.error("Error fetching total count:", error);
}
};
// Fetch aggregated stats from backend call API (innovation_process_function)
const fetchStats = async () => {
try {
setStatsLoading(true);
const raw = await apiService.callInnovationProcess<any>({
innovation_process_function: {
},
});
let payload: any = raw?.data;
if (typeof payload === "string") {
try { payload = JSON.parse(payload); } catch {}
}
const parseNum = (v: unknown): number => {
if (v == null) return 0;
if (typeof v === "number") return v;
if (typeof v === "string") {
const cleaned = v.replace(/,/g, "").trim();
const n = parseFloat(cleaned);
return isNaN(n) ? 0 : n;
}
return 0;
};
const normalized: InnovationStats = {
totalProjects: parseNum(payload?.count_innovation_process_projects),
averageScore: parseNum(payload?.average_project_score),
productionStopsPreventionSum: parseNum(payload?.sum_stopping_production),
bottleneckRemovalCount: parseNum(payload?.count_throat_removal),
currencyReductionSum: parseNum(payload?.sum_reduction_value_currency),
frequentFailuresReductionSum: parseNum(payload?.sum_reducing_breakdowns),
percentProductionStops: parseNum(payload?.percent_sum_stopping_production),
percentBottleneckRemoval: parseNum(payload?.percent_throat_removal),
percentCurrencyReduction: parseNum(payload?.percent_reduction_value_currency),
percentFailuresReduction: parseNum(payload?.percent_reducing_breakdowns),
};
setStats(normalized);
} catch (error) {
console.error("Error fetching stats:", error);
} finally {
setStatsLoading(false);
}
};
const handleRefresh = () => {
fetchingRef.current = false;
setCurrentPage(1);
setProjects([]);
setHasMore(true);
fetchProjects(true);
fetchTotalCount();
fetchStats();
};
const formatCurrency = (amount: string | number) => {
if (!amount) return "0 ریال";
const numericAmount =
typeof amount === "string"
? parseFloat(amount.replace(/,/g, ""))
: amount;
if (isNaN(numericAmount)) return "0 ریال";
return new Intl.NumberFormat("fa-IR").format(numericAmount) + " ریال";
};
const formatPercentage = (value: string | number) => {
if (!value) return "0%";
const numericValue = typeof value === "string" ? parseFloat(value) : value;
if (isNaN(numericValue)) return "0%";
return `${numericValue.toFixed(1)}%`;
};
const getStatusColor = (status: string) => {
switch (status?.toLowerCase()) {
case "فعال":
return "#3AEA83";
case "متوقف":
return "#F76276";
case "تکمیل شده":
return "#32CD32";
default:
return "#6B7280";
}
};
const getRatingColor = (rating: string) => {
const ratingNum = parseFloat(rating);
if (isNaN(ratingNum)) return "#6B7280";
if (ratingNum >= 8) return "#3AEA83";
if (ratingNum >= 6) return "#69C8EA";
if (ratingNum >= 4) return "#FFD700";
return "#F76276";
};
const renderCellContent = (item: ProcessInnovationData, column: any) => {
const value = item[column.key as keyof ProcessInnovationData];
switch (column.key) {
case "select":
return (
<Checkbox
checked={selectedProjects.has(item.project_no)}
onCheckedChange={() => handleSelectProject(item.project_no)}
className="data-[state=checked]:bg-emerald-600 data-[state=checked]:border-emerald-600"
/>
);
case "details":
return (
<Button
variant="ghost"
size="sm"
onClick={() => handleProjectDetails(item)}
className="text-emerald-400 hover:text-emerald-300 hover:bg-emerald-500/20 p-2 h-auto"
>
جزئیات بیشتر
</Button>
);
case "amount_currency_reduction":
return (
<span className="font-medium text-emerald-400">
{formatCurrency(String(value))}
</span>
);
case "project_no":
return (
<Badge
variant="outline"
className="font-mono"
>
{String(value)}
</Badge>
);
case "title":
return <span className="font-medium text-white">{String(value)}</span>;
case "project_status":
return (
<Badge
variant="outline"
className="font-medium border-2"
style={{
border:"none",
}}
>
{String(value)}
</Badge>
);
case "project_rating":
return (
<Badge
variant="outline"
className="text-lg text-center border-none"
>
{formatNumber(String(value))}
</Badge>
);
case "reduce_prevention_production_stops":
case "throat_removal":
case "Reduce_rate_failure":
return (
<span className="font-medium text-blue-400">
{formatNumber(String(value))}
</span>
);
default:
return <span className="text-gray-300">{String(value) || "-"}</span>;
}
};
return (
<DashboardLayout title="نوآوری در فرآیند">
<div className="p-6 space-y-4">
{/* Stats Cards */}
<div className="flex gap-6">
<div className="space-y-6 w-full">
{/* Stats Grid */}
<div className="grid grid-cols-2 gap-3">
{loading || statsLoading ? (
// Loading skeleton for stats cards - matching new design
Array.from({ length: 4 }).map((_, index) => (
<Card key={`skeleton-${index}`} className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl overflow-hidden">
<CardContent className="p-2">
<div className="flex flex-col justify-between gap-2">
<div className="flex justify-between items-center border-b-2 mx-4 border-gray-500/20">
<div className="h-6 bg-gray-600 rounded animate-pulse" style={{ width: '60%' }} />
<div className="p-3 bg-emerald-500/20 rounded-full w-fit">
<div className="w-6 h-6 bg-gray-600 rounded animate-pulse" />
</div>
</div>
<div className="flex items-center justify-center flex-col p-1">
<div className="h-8 bg-gray-600 rounded mb-1 animate-pulse" style={{ width: '40%' }} />
<div className="h-4 bg-gray-600 rounded animate-pulse" style={{ width: '80%' }} />
</div>
</div>
</CardContent>
</Card>
))
) : (
statsCards.map((card) => (
<Card key={card.id} className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-gray-700/50">
<CardContent className="p-2">
<div className="flex flex-col justify-between gap-2">
<div className="flex justify-between items-center border-b-2 mx-4 border-gray-500/20">
<h3 className="text-lg font-bold text-white font-persian">
{card.title}
</h3>
<div className={`p-3 gird placeitems-center rounded-full w-fit `}>
{card.icon}
</div>
</div>
<div className="flex items-center justify-center flex-col p-1">
<p className={`text-3xl font-bold ${card.color} mb-1`}>
{card.value}
</p>
<p className="text-sm text-gray-300 font-persian">
{card.description}
</p>
</div>
</div>
</CardContent>
</Card>
))
)}
</div>
</div>
{/* Process Impacts Chart */}
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl w-full overflow-hidden">
<CardContent className="p-4">
<CustomBarChart
title="تاثیرات فرآیندی به صورت درصد مقایسه ای"
loading={statsLoading}
data={[
{
label: "کاهش توقفات تولید",
value: stats.percentProductionStops || 0,
color: "bg-emerald-400",
labelColor: "text-white"
},
{
label: "رفع گلوگاه تولید",
value: stats.percentBottleneckRemoval || 0,
color: "bg-emerald-400",
labelColor: "text-white"
},
{
label: "کاهش ارز بری",
value: stats.percentCurrencyReduction || 0,
color: "bg-emerald-400",
labelColor: "text-white"
},
{
label: "کاهش خرابی پر تکرار",
value: stats.percentFailuresReduction || 0,
color: "bg-emerald-400",
labelColor: "text-white"
}
]}
barHeight="h-5"
showAxisLabels={true}
/>
</CardContent>
</Card>
</div>
{/* Data Table */}
<Card className="bg-transparent backdrop-blur-sm rounded-2xl overflow-hidden">
<CardContent className="p-0">
<div className="relative">
<Table containerClassName="overflow-auto custom-scrollbar max-h-[calc(90vh-400px)]">
<TableHeader>
<TableRow className="bg-[#3F415A]">
{columns.map((column) => (
<TableHead
key={column.key}
className="text-right font-persian whitespace-nowrap text-gray-200 font-medium sticky top-0 z-20 bg-[#3F415A]"
style={{ width: column.width }}
>
{column.key === "select" ? (
<div className="flex items-center justify-center">
<Checkbox
checked={selectedProjects.size === projects.length && projects.length > 0}
onCheckedChange={handleSelectAll}
className="data-[state=checked]:bg-emerald-600 data-[state=checked]:border-emerald-600"
/>
</div>
) : column.sortable ? (
<button
onClick={() => handleSort(column.key)}
className="flex items-center gap-2"
>
<span>{column.label}</span>
{sortConfig.field === column.key ? (
sortConfig.direction === "asc" ? (
<ChevronUp className="w-4 h-4" />
) : (
<ChevronDown className="w-4 h-4" />
)
) : (
<div className="w-4 h-4" />
)}
</button>
) : (
column.label
)}
</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{loading ? (
// Skeleton loading rows (compact)
Array.from({ length: 10 }).map((_, index) => (
<TableRow key={`skeleton-${index}`} className="text-sm leading-tight h-8">
{columns.map((column) => (
<TableCell
key={column.key}
className="text-right whitespace-nowrap border-emerald-500/20 py-1 px-2"
>
<div className="flex items-center gap-2">
<div className="w-2.5 h-2.5 bg-gray-600 rounded-full animate-pulse" />
<div className="h-2.5 bg-gray-600 rounded animate-pulse" style={{ width: `${Math.random() * 60 + 40}%` }} />
</div>
</TableCell>
))}
</TableRow>
))
) : projects.length === 0 ? (
<TableRow>
<TableCell
colSpan={columns.length}
className="text-center py-8"
>
<span className="text-gray-400 font-persian">
هیچ پروژهای یافت نشد
</span>
</TableCell>
</TableRow>
) : (
projects.map((project, index) => (
<TableRow key={`${project.project_no}-${index}`} className="text-sm leading-tight h-8">
{columns.map((column) => (
<TableCell
key={column.key}
className={`text-right whitespace-nowrap border-emerald-500/20 py-1 px-2 ${column.key==="select" ? "flex justify-center items-center" :""}`}
>
{renderCellContent(project, column)}
</TableCell>
))}
</TableRow>
))
)}
</TableBody>
</Table>
</div>
{/* Infinite scroll trigger */}
<div ref={observerRef} className="h-auto">
{loadingMore && (
<div className="flex items-center justify-center py-1">
<div className="flex items-center gap-2">
<RefreshCw className="w-4 h-4 animate-spin text-emerald-400" />
<span className="font-persian text-gray-300 text-xs">
</span>
</div>
</div>
)}
</div>
</CardContent>
{/* Selection Summary */}
{/* {selectedProjects.size > 0 && (
<div className="px-4 py-3 bg-emerald-500/10 border-t border-emerald-500/20">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-2 h-2 bg-emerald-500 rounded-full"></div>
<span className="text-emerald-400 font-medium font-persian">
{selectedProjects.size} پروژه انتخاب شده
</span>
</div>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setSelectedProjects(new Set())}
className="border-emerald-500/30 text-emerald-400 hover:bg-emerald-500/20 hover:text-emerald-300"
>
لغو انتخاب
</Button>
</div>
</div>
</div>
)} */}
{/* Footer */}
<div className="p-2 px-4 bg-gray-700/50">
<div className="grid grid-cols-6 gap-4 text-sm text-gray-300 font-persian">
<div className="text-center gap-2 items-center flex">
<div className="text-base text-gray-401 mb-1"> کل پروژه ها : {formatNumber(stats.totalProjects || actualTotalCount)}</div>
</div>
{/* Project number column - empty */}
<div></div>
{/* Title column - empty */}
<div></div>
{/* Project status column - empty */}
<div></div>
{/* Project rating column - show average */}
<div className="flex justify-center items-center gap-2">
<div className="text-base text-gray-400 mb-1"> میانگین امتیاز :</div>
<div className="font-bold">
{formatNumber(((stats.averageScore ?? 0) as number).toFixed?.(1) ?? (stats.averageScore ?? 0))}
</div>
</div>
{/* Details column - show total count */}
</div>
</div>
</Card>
</div>
{/* Project Details Dialog */}
<Dialog open={detailsDialogOpen} onOpenChange={setDetailsDialogOpen}>
<DialogContent className="bg-[#2A2D3E] border-gray-700 max-w-2xl max-h-[80vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="text-white font-persian text-right">
جزئیات پروژه
</DialogTitle>
</DialogHeader>
{selectedProjectDetails && (
<div className="space-y-6 text-right">
{/* Project Header */}
<div className="bg-gray-700/30 rounded-lg p-4">
<h3 className="text-lg font-bold text-white font-persian mb-2">
{selectedProjectDetails.title}
</h3>
<div className="flex items-center gap-4">
<Badge
variant="outline"
className="font-mono text-emerald-400 border-emerald-500/50"
>
{selectedProjectDetails.project_no}
</Badge>
<Badge
variant="outline"
className="font-medium border-2"
style={{
color: getStatusColor(selectedProjectDetails.project_status),
borderColor: getStatusColor(selectedProjectDetails.project_status),
backgroundColor: `${getStatusColor(selectedProjectDetails.project_status)}20`,
}}
>
{selectedProjectDetails.project_status}
</Badge>
</div>
</div>
{/* Project Metrics */}
<div className="grid grid-cols-2 gap-4">
<div className="bg-gray-700/20 rounded-lg p-4">
<h4 className="text-sm font-medium text-gray-300 font-persian mb-2">
امتیاز پروژه
</h4>
<Badge
variant="outline"
className="text-lg font-bold border-2"
style={{
color: getRatingColor(selectedProjectDetails.project_rating),
borderColor: getRatingColor(selectedProjectDetails.project_rating),
backgroundColor: `${getRatingColor(selectedProjectDetails.project_rating)}20`,
}}
>
{formatNumber(selectedProjectDetails.project_rating)}
</Badge>
</div>
<div className="bg-gray-700/20 rounded-lg p-4">
<h4 className="text-sm font-medium text-gray-300 font-persian mb-2">
کاهش توقفات تولید
</h4>
<span className="text-lg font-bold text-blue-400">
{formatNumber(selectedProjectDetails.reduce_prevention_production_stops)}
</span>
</div>
<div className="bg-gray-700/20 rounded-lg p-4">
<h4 className="text-sm font-medium text-gray-300 font-persian mb-2">
رفع گلوگاه تولید
</h4>
<span className="text-lg font-bold text-blue-400">
{formatNumber(selectedProjectDetails.throat_removal)}
</span>
</div>
<div className="bg-gray-700/20 rounded-lg p-4">
<h4 className="text-sm font-medium text-gray-300 font-persian mb-2">
کاهش ارز بری
</h4>
<span className="text-lg font-bold text-emerald-400">
{formatCurrency(selectedProjectDetails.amount_currency_reduction)}
</span>
</div>
<div className="bg-gray-700/20 rounded-lg p-4 col-span-2">
<h4 className="text-sm font-medium text-gray-300 font-persian mb-2">
کاهش خرابی پر تکرار
</h4>
<span className="text-lg font-bold text-blue-400">
{formatNumber(selectedProjectDetails.Reduce_rate_failure)}
</span>
</div>
</div>
{/* Action Buttons */}
<div className="flex justify-end gap-3 pt-4 border-t border-gray-700">
<Button
variant="outline"
onClick={() => setDetailsDialogOpen(false)}
className="border-gray-600 text-gray-300 hover:bg-gray-700"
>
بستن
</Button>
</div>
</div>
)}
</DialogContent>
</Dialog>
</DashboardLayout>
);
}