feat/green-innovation #6

Merged
Saeed0920 merged 11 commits from feat/green-innovation into main 2025-08-30 16:34:22 +03:30
Showing only changes of commit bf33d50d81 - Show all commits

View File

@ -21,19 +21,34 @@ import {
DialogTitle, DialogTitle,
} from "~/components/ui/dialog"; } from "~/components/ui/dialog";
import { import {
ChevronUp, BarChart,
ChevronDown, Bar,
RefreshCw, XAxis,
ExternalLink, YAxis,
Building2, CartesianGrid,
PickaxeIcon, Tooltip,
UserIcon, ResponsiveContainer,
UsersIcon, LineChart,
} from "lucide-react"; Line,
Rectangle,
Legend,
} from "recharts";
import apiService from "~/lib/api"; import apiService from "~/lib/api";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { Funnel, Wrench, CirclePause, DollarSign } from "lucide-react"; import {
import ProjectDetail from "../projects/project-detail"; LoaderCircle,
TrendingUp,
Key,
Sparkle,
Zap,
Flame,
Building2,
PickaxeIcon,
UsersIcon,
UserIcon,
RefreshCw,
} from "lucide-react";
import DashboardLayout from "../layout"; import DashboardLayout from "../layout";
moment.loadPersian({ usePersianDigits: true }); moment.loadPersian({ usePersianDigits: true });
@ -80,6 +95,19 @@ interface InnovationStats {
percentFailuresReduction: number; // درصد مقایسه‌ای کاهش خرابی‌های پرتکرار percentFailuresReduction: number; // درصد مقایسه‌ای کاهش خرابی‌های پرتکرار
} }
interface Params {
icon: any;
label: string;
value: string;
suffix: string;
}
interface RecycleParams {
water: Params;
food: Params;
power: Params;
oil: Params;
}
const columns = [ const columns = [
{ key: "select", label: "", sortable: false, width: "50px" }, { key: "select", label: "", sortable: false, width: "50px" },
{ key: "project_no", label: "شماره پروژه", sortable: true, width: "140px" }, { key: "project_no", label: "شماره پروژه", sortable: true, width: "140px" },
@ -131,6 +159,32 @@ export function GreenInnovationPage() {
const [detailsDialogOpen, setDetailsDialogOpen] = useState(false); const [detailsDialogOpen, setDetailsDialogOpen] = useState(false);
const [selectedProjectDetails, setSelectedProjectDetails] = const [selectedProjectDetails, setSelectedProjectDetails] =
useState<ProcessInnovationData | null>(null); useState<ProcessInnovationData | null>(null);
const [recycleParams, setRecycleParams] = useState<RecycleParams>({
water: {
icon: <Key className="text-emerald-400" size={"18px"} />,
label: "آب",
value: "1,520",
suffix: "لیتر",
},
food: {
icon: <Sparkle className="text-emerald-400" size={"18px"} />,
label: "خوراک",
value: "520",
suffix: "تن",
},
oil: {
icon: <Flame className="text-emerald-400" size={"18px"} />,
label: "سوخت",
value: "250",
suffix: "متر مربع",
},
power: {
icon: <Zap className="text-emerald-400" size={"18px"} />,
label: "برق",
value: "650",
suffix: "میلیون مگاوات",
},
});
const observerRef = useRef<HTMLDivElement>(null); const observerRef = useRef<HTMLDivElement>(null);
const fetchingRef = useRef(false); const fetchingRef = useRef(false);
@ -579,109 +633,204 @@ export function GreenInnovationPage() {
} }
}; };
const data = [
{ name: recycleParams.water.label, pv: 70, amt: 80 },
{ name: recycleParams.power.label, pv: 45, amt: 60 },
{ name: recycleParams.oil.label, pv: 90, amt: 75 },
{ name: recycleParams.food.label, pv: 30, amt: 50 },
];
return ( return (
<DashboardLayout title="نوآوری سبز"> <DashboardLayout title="نوآوری سبز">
<div className="p-6 space-y-4"> <div className="p-6 space-y-4">
{/* Stats Cards */} {/* Stats Cards */}
<div className="flex gap-6"> <div className="flex gap-6 mb-6">
<div className="space-y-6 w-full"> <div className="flex flex-col gap-11 w-full h-full">
{/* Stats Grid */} {/* Stats Grid */}
<div className="flex flex-col gap-3"> {loading || statsLoading
{loading || statsLoading ? // Loading skeleton for stats cards - matching new design
? // Loading skeleton for stats cards - matching new design Array.from({ length: 2 }).map((_, index) => (
Array.from({ length: 2 }).map((_, index) => ( <Card
<Card key={`skeleton-${index}`}
key={`skeleton-${index}`} className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl overflow-hidden"
className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl overflow-hidden" >
> <CardContent className="p-2">
<CardContent className="p-2"> <div className="flex flex-col justify-between gap-2 h-full">
<div className="flex flex-col justify-between gap-2"> <div className="flex justify-between items-center border-b-2 border-gray-500/20">
<div className="flex justify-between items-center border-b-2 border-gray-500/20"> <div
<div className="h-6 bg-gray-600 rounded animate-pulse"
className="h-6 bg-gray-600 rounded animate-pulse" style={{ width: "60%" }}
style={{ width: "60%" }} />
/> <div className="p-3 bg-emerald-500/20 rounded-full w-fit">
<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 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>
</div> </div>
</CardContent> <div className="flex items-center justify-center flex-col p-1">
</Card> <div
)) className="h-8 bg-gray-600 rounded mb-1 animate-pulse"
: statsCards.map((card) => ( style={{ width: "40%" }}
<Card />
key={card.id} <div
className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-gray-700/50" className="h-4 bg-gray-600 rounded animate-pulse"
> style={{ width: "80%" }}
<CardContent className="p-0"> />
<div className="flex flex-col justify-between gap-2"> </div>
<div className="flex justify-between items-center border-b-2 border-gray-500/20 "> </div>
<h3 className="text-lg font-bold text-white font-persian p-4"> </CardContent>
{card.title} </Card>
</h3> ))
: 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-0 h-full">
<div className="flex flex-col justify-between gap-2 h-full">
<div className="flex justify-between items-center border-b-2 border-gray-500/20 ">
<h3 className="text-lg font-bold text-white font-persian p-4">
{card.title}
</h3>
</div>
<div className="flex items-center justify-between p-6 flex-row-reverse">
<div className="flex flex-col">
<span className="text-3xl font-bold text-emerald-400 mb-1">
% {card.percent.value}
</span>
<span className="text-sm text-gray-400 font-persian">
{card.percent.description}
</span>
</div> </div>
<div className="flex items-center justify-between p-6"> <b className="block w-0.5 h-12 bg-gray-600 rotate-45" />
<span className="text-emerald-400"> <div className="flex flex-col">
<span className="text-3xl font-bold text-emerald-400 mb-1">
{card.total.value} {card.total.value}
</span> </span>
<b className="block w-0.5 h-8 bg-emerald-400 rotate-45" /> <span className="text-sm text-gray-400 font-persian">
<span className="text-emerald-400"> {card.total.description}
{card.percent.value}
</span> </span>
</div> </div>
</div> </div>
</CardContent> </div>
</Card> </CardContent>
))} </Card>
</div> ))}
</div> </div>
{/* Process Impacts Chart */} {/* Process Impacts Chart */}
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl w-full overflow-hidden"> <Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl w-full overflow-hidden">
<CardContent className="p-4"> <CardContent className="p-0 h-full overflow-hidden">
<CustomBarChart <div className="border-b-2 border-gray-500/20">
title="تاثیرات فرآیندی به صورت درصد مقایسه ای" <div className="w-full p-4 px-6">
loading={statsLoading} <span>بازیافت و بازیابی منابع</span>
data={[ </div>
{ </div>
label: "کاهش توقفات تولید", <div className="content grid gap-3 h-max p-8 box-border items-center justify-between sm:grid-cols-1 sm:overflow-auto xl:overflow-hidden xl:grid-cols-[30%_70%]">
value: stats.percentProductionStops || 0, <div className="params flex flex-col gap-3.5">
color: "bg-emerald-400", {Object.entries(recycleParams).map((el, index) => {
labelColor: "text-white", return (
}, <div className="param flex flex-row justify-between items-center">
{ <div className="flex flex-row gap-2">
label: "رفع گلوگاه تولید", {el[1].icon}
value: stats.percentBottleneckRemoval || 0, <span className="font-normal text-sm">
color: "bg-emerald-400", {el[1].label}:
labelColor: "text-white", </span>
}, </div>
{ <div className="flex flex-row gap-1.5 items-center">
label: "کاهش ارز بری", <span className="text-sm font-normal">
value: stats.percentCurrencyReduction || 0, {el[1].value}
color: "bg-emerald-400", </span>
labelColor: "text-white", <span className="text-sm">{el[1].suffix}</span>
}, </div>
{ </div>
label: "کاهش خرابی پر تکرار", );
value: stats.percentFailuresReduction || 0, })}
color: "bg-emerald-400", </div>
labelColor: "text-white",
}, <div>
]} <div className="h-72 w-[32rem]">
barHeight="h-5" <ResponsiveContainer width="100%" height="100%">
showAxisLabels={true} <BarChart
/> width={500}
height={300}
data={data}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
<CartesianGrid
stroke="#374151"
strokeDasharray="0"
vertical={false} // خط‌های عمودی (از محور X) بمونه
// horizontal={false} // خط‌های افقی (از محور Y) حذف میشه
/>
<XAxis
dataKey="name"
axisLine={false} // خط اصلی محور X محو میشه
tickLine={false}
tick={{
fill: "#fff",
fontSize: 14,
fontWeight: "normal",
dy: 10, // جابجایی عمودی (مثبت = پایین‌تر، منفی = بالاتر)
}}
/>
<YAxis
type="number"
domain={[0, 100]}
axisLine={false}
tickLine={false}
tick={{
fill: "#99a1af", // رنگ متن
fontSize: 14, // سایز فونت
// fontWeight: "bold", // ضخامت
dx: -30, // جابجایی افقی (اعداد نزدیک‌تر یا دورتر از محور)
// dy:-1
}}
tickFormatter={(val) => `${val}%`}
/>
<Bar
dataKey="pv"
fill="#3AEA83"
radius={[20, 20, 0, 0]}
barSize={14}
label={{
position: "top",
fill: "#fff",
fontWeight: "bold",
}}
/>
</BarChart>
</ResponsiveContainer>
</div>
</div>
</div>
</CardContent>
</Card>
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl w-full overflow-hidden">
<CardContent className="p-0">
<div className="border-b-2 border-gray-500/20">
<div className="flex flex-row justify-between w-full p-4 px-6">
<span>استاندارد ها و مقررات</span>
<TrendingUp />
</div>
</div>
<div className="flex flex-col gap-3 p-4 max-h-[22rem] overflow-y-scroll">
{Array.from({ length: 10 }, (index) => {
return (
<div key={`${index}-1`} className="flex gap-2">
<LoaderCircle
size={"18px"}
className="text-emerald-400"
/>
<span>استاندارد Iso 2005</span>
</div>
);
})}
</div>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>