refactor and fix the styles in ecosystem and product-innovation

This commit is contained in:
Saeed AB 2025-09-18 14:11:51 +03:30
parent aed286660a
commit 58331eed7a
2 changed files with 186 additions and 333 deletions

View File

@ -21,6 +21,8 @@ import toast from "react-hot-toast";
import { Badge } from "~/components/ui/badge"; import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button"; import { Button } from "~/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { MetricCard } from "~/components/ui/metric-card";
import { BaseCard } from "~/components/ui/base-card";
import { Checkbox } from "~/components/ui/checkbox"; import { Checkbox } from "~/components/ui/checkbox";
import { Bar, BarChart, LabelList } from "recharts" import { Bar, BarChart, LabelList } from "recharts"
import { import {
@ -608,25 +610,25 @@ export function ProductInnovationPage() {
size="sm" size="sm"
onClick={() => { onClick={() => {
handleProjectDetails(item)}} handleProjectDetails(item)}}
className="text-emerald-400 hover:text-emerald-300 hover:bg-emerald-500/20 p-2 h-auto" className="text-emerald-400 underline underline-offset-4 font-ligth text-base hover:bg-emerald-500/20 p-2 h-auto"
> >
جزئیات بیشتر جزئیات بیشتر
</Button> </Button>
); );
case "project_no": case "project_no":
return ( return (
<Badge variant="outline" className="font-mono"> <Badge variant="outline" className="font-mono text-base font-light">
{String(value)} {String(value)}
</Badge> </Badge>
); );
case "title": case "title":
return <span className="font-medium text-white">{String(value)}</span>; return <span className="font-light text-base text-white">{String(value)}</span>;
case "project_status": case "project_status":
return ( return (
<div className="flex items-center gap-1"> <div className="flex items-center text-base font-light gap-1">
<Badge <Badge
variant={statusColor(value as projectStatus)} variant={statusColor(value as projectStatus)}
className="font-medium border-2 p-0 block w-2 h-2 rounded-full" className="font-semibold text-base border-2 p-0 block w-2 h-2 rounded-full"
style={{ style={{
border: "none", border: "none",
}} }}
@ -638,13 +640,13 @@ export function ProductInnovationPage() {
return ( return (
<Badge <Badge
variant="outline" variant="outline"
className={`text-lg text-center border-none mx-auto`} className={`font-semibold text-base text-center border-none mx-auto`}
> >
{formatNumber(String(value))} {formatNumber(String(value))}
</Badge> </Badge>
); );
default: default:
return <span className="text-gray-300">{String(value) || "-"}</span>; return <span className="text-white text-base font-light">{String(value) || "-"}</span>;
} }
}; };
@ -670,87 +672,86 @@ export function ProductInnovationPage() {
<div className="space-y-6 w-full"> <div className="space-y-6 w-full">
{/* Stats Grid */} {/* Stats Grid */}
<div className="grid grid-cols-2 grid-rows-2 gap-5 h-full"> <div className="grid grid-cols-2 grid-rows-2 gap-5 h-full">
{loading || statsLoading {loading || statsLoading ? (
? // Loading skeleton for stats cards - matching new design // Loading skeleton for stats cards - matching new design
Array.from({ length: 3 }).map((_, index) => ( Array.from({ length: 3 }).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 [&>*:first-child]:row-span-1" className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl overflow-hidden [&>*:first-child]:row-span-1"
> >
<CardContent className="p-2"> <CardContent className="p-2">
<div className="flex flex-col justify-between gap-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="flex justify-between items-center border-b-2 mx-4 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 animate-pulse mb-1"
style={{ width: "40%" }}
/>
<div
className="h-4 bg-gray-600 rounded animate-pulse"
style={{ width: "80%" }}
/>
</div>
</div>
</CardContent>
</Card>
))
) : (
<>
{/* First card (Metric/Matrix style) - span two columns */}
<div className="col-span-2">
<MetricCard
title={stateCard.revenueNewProducts.title}
value={stateCard.revenueNewProducts.value}
percentValue={stateCard.revenueNewProducts.percent}
valueLabel={stateCard.revenueNewProducts.description}
percentLabel={stateCard.revenueNewProducts.descriptionPercent}
/>
</div> </div>
</div>
<div className="flex items-center justify-center flex-col p-1"> {/* Second card */}
<div <div>
className="h-8 bg-gray-600 rounded animate-pulse" <BaseCard title={stateCard.newProductExports.title} className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-gray-700/50">
style={{ width: "40%" }} <div className="flex items-center justify-center flex-col">
/> <div className="flex items-center gap-4">
<div <div className="text-center">
className="h-4 bg-gray-600 rounded animate-pulse" <p className="text-3xl font-bold mb-1 text-pr-green">{stateCard.newProductExports.value}</p>
style={{ width: "80%" }} <div className="text-xs text-gray-400 font-persian">{stateCard.newProductExports.description}</div>
/> </div>
</div> </div>
</div>
</BaseCard>
</div> </div>
</CardContent>
</Card> {/* Third card - basic BaseCard */}
)) <div>
: Object.entries(stateCard).map(([key, card], index) => ( <BaseCard title={stateCard.impactOnImports.title} className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-gray-700/50">
<Card <div className="flex items-center justify-center flex-col">
key={card.id} <div className="flex items-center gap-4">
className={`bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-gray-700/50 ${index !== 0 ? "row-start-2 " : "col-span-2"} `} <div className="text-center">
> <p className="text-3xl font-bold mb-1 text-pr-red">{stateCard.impactOnImports.value}</p>
<CardContent className="p-2 h-full"> <div className="text-xs text-gray-400 font-persian">{stateCard.impactOnImports.description}</div>
<div className="grid grid-cols-2 justify-between gap-2 h-full"> </div>
<div className="flex justify-between rows-start-1 col-span-2 items-center border-b-2 mx-4 border-gray-500/20"> </div>
<h3 className="text-lg text-white font-persian py-2"> </div>
{card.title} </BaseCard>
</h3>
<div
className={`p-3 gird placeitems-center rounded-full w-fit `}
>
</div> </div>
</div> </>
<div className={`flex items-center row-start-2 justify-center flex-col row-start-2 p-1 my-auto ${card?.percent ? "col-span-1 col-start-1" : "col-span-2"}`}> )}
<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>
{card?.percent && <span className="text-gray-600 row-start-2 col-span-2 self-center col-start-2 font-thin text-5xl">/</span>}
{card?.percent && <div className="flex col-span-1 items-center row-start-2 my-auto col-start-2 justify-center flex-col p-1 my-auto">
<p
className={`text-3xl font-bold ${card.color} mb-1`}
>
{card?.percent}
</p>
<p className="text-sm text-gray-300 font-persian">
{card.descriptionPercent}
</p>
</div>}
</div>
</CardContent>
</Card>
))}
</div> </div>
</div> </div>
{/* Funnel Chart */} {/* Funnel Chart */}
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] h-full backdrop-blur-sm rounded-2xl w-full overflow-hidden"> <Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] h-full backdrop-blur-sm rounded-2xl w-full overflow-hidden">
<CardContent className="p-6"> <CardContent className="px-0 py-4">
<FunnelChart <FunnelChart
title="قيف فرآیند پروژه ها" title="قيف فرآیند پروژه ها"
data={[ data={[
@ -795,7 +796,7 @@ export function ProductInnovationPage() {
{columns.map((column) => ( {columns.map((column) => (
<TableHead <TableHead
key={column.key} key={column.key}
className="text-center font-persian whitespace-nowrap text-gray-200 font-medium sticky top-0 z-20 bg-[#3F415A]" className="text-center font-persian whitespace-nowrap text-white font-medium sticky top-0 z-20 bg-pr-gray text-sm font-semibold"
style={{ width: column.width }} style={{ width: column.width }}
> >
{column.sortable ? ( {column.sortable ? (
@ -891,10 +892,10 @@ export function ProductInnovationPage() {
</CardContent> </CardContent>
{/* Footer */} {/* Footer */}
<div className="p-2 px-4 bg-[#3F415A]"> <div className="p-2 px-4 bg-pr-gray">
<div className="flex gap-4 text-sm text-gray-300 font-persian justify-between sm:flex-col xl:flex-row"> <div className="flex gap-4 text-sm text-gray-300 font-persian justify-between sm:flex-col xl:flex-row">
<div className="text-center gap-2 items-center xl:w-1/3 pr-36 sm:w-full"> <div className="text-center gap-2 items-center xl:w-1/3 pr-36 sm:w-full">
<div className="text-base text-gray-401"> <div className="text-sm font-semibold text-white">
کل پروژه ها :{formatNumber(stats?.count_innovation_construction_inside_projects)} کل پروژه ها :{formatNumber(stats?.count_innovation_construction_inside_projects)}
</div> </div>
</div> </div>
@ -907,8 +908,8 @@ export function ProductInnovationPage() {
<span className="block w-7 h-2.5 bg-pink-400 rounded-tr-xl rounded-br-xl"></span> <span className="block w-7 h-2.5 bg-pink-400 rounded-tr-xl rounded-br-xl"></span>
</div> </div>
<div className="flex justify-center items-center gap-2"> <div className="flex justify-center items-center gap-2">
<div className="text-base text-gray-400">میانگین :</div> <div className="text-bold text-sm text-white">میانگین :</div>
<div className="font-bold"> <div className="font-bold text-sm text-white">
{formatNumber( {formatNumber(
((stats.average_project_score ?? 0) as number).toFixed?.(1) ?? 0 ((stats.average_project_score ?? 0) as number).toFixed?.(1) ?? 0
)} )}
@ -924,7 +925,7 @@ export function ProductInnovationPage() {
<Dialog open={detailsDialogOpen} onOpenChange={setDetailsDialogOpen}> <Dialog open={detailsDialogOpen} onOpenChange={setDetailsDialogOpen}>
<DialogContent className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] max-w-7xl max-h-[95vh] overflow-y-auto"> <DialogContent className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] max-w-7xl max-h-[95vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-white mr-4 border-b-2 border-gray-600 pb-2 font-persian text-right"> <DialogTitle className="text-white mr-4 border-b-2 border-gray-600 pb-2 text-sm font-semibold font-persian text-right">
شرح پروژه شرح پروژه
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
@ -934,17 +935,17 @@ export function ProductInnovationPage() {
<div className="space-y-4"> <div className="space-y-4">
{/* Stats Cards */} {/* Stats Cards */}
<div className="space-y-4"> <div className="space-y-4">
<h3 className="font-bold text-lg">{selectedProjectDetails?.title}</h3> <h3 className="font-bold text-base">{selectedProjectDetails?.title}</h3>
<p className="py-2">{selectedProjectDetails?.project_description}</p> <p className="py-2">{selectedProjectDetails?.project_description}</p>
</div> </div>
<Timeline /> <Timeline />
{/* Technical Knowledge */} {/* Technical Knowledge */}
<div className=" rounded-lg py-2 mb-0"> <div className=" rounded-lg py-2 mb-0">
<h3 className="text-sm text-gray-400 mb-2">دانش فنی محصول جدید</h3> <h3 className="text-sm text-white font-semibold mb-2">دانش فنی محصول جدید</h3>
<div className="flex gap-4 items-center"> <div className="flex gap-4 items-center">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-sm text-white">توسعه درونزا</span> <span className="text-sm text-white font-light">توسعه درونزا</span>
<Checkbox <Checkbox
checked={selectedProjectDetails?.developed_technology_type === "توسعه درونزا"} checked={selectedProjectDetails?.developed_technology_type === "توسعه درونزا"}
@ -953,7 +954,7 @@ export function ProductInnovationPage() {
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-sm text-white">همکاری فناورانه</span> <span className="text-sm text-white font-light">همکاری فناورانه</span>
<Checkbox <Checkbox
checked={selectedProjectDetails?.developed_technology_type === "همکاری فناوری"} checked={selectedProjectDetails?.developed_technology_type === "همکاری فناوری"}
@ -962,7 +963,7 @@ export function ProductInnovationPage() {
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-sm text-white">سایر</span> <span className="text-sm text-white font-light">سایر</span>
<Checkbox <Checkbox
checked={selectedProjectDetails?.developed_technology_type === "سایر"} checked={selectedProjectDetails?.developed_technology_type === "سایر"}
className="data-[state=checked]:bg-emerald-600 data-[state=checked]:border-emerald-600" className="data-[state=checked]:bg-emerald-600 data-[state=checked]:border-emerald-600"
@ -973,7 +974,7 @@ export function ProductInnovationPage() {
{/* Standards */} {/* Standards */}
<div className="rounded-lg py-4"> <div className="rounded-lg py-4">
<h3 className="text-sm text-gray-400 mb-4"> <h3 className="text-sm text-white font-semibold mb-4">
استانداردهای ملی و بینالمللی اخذ شده استانداردهای ملی و بینالمللی اخذ شده
</h3> </h3>
@ -985,7 +986,7 @@ export function ProductInnovationPage() {
).map((standard, index) => ( ).map((standard, index) => (
<div key={index} className="flex items-center gap-2"> <div key={index} className="flex items-center gap-2">
<div className="w-2 h-2 bg-emerald-500 rounded-full"></div> <div className="w-2 h-2 bg-emerald-500 rounded-full"></div>
<span className="text-sm text-white">{standard}</span> <span className="text-sm text-white font-light">{standard}</span>
</div> </div>
))} ))}
</div> </div>
@ -997,21 +998,21 @@ export function ProductInnovationPage() {
</div> </div>
{/* Knowledge-based Certificate Button */} {/* Knowledge-based Certificate Button */}
<div className="justify-self-centerr py-1 mx-auto"> <div className="justify-self-centerr grid py-1 mx-auto">
{selectedProjectDetails?.knowledge_based_certificate_obtained === "خیر" ? ( {selectedProjectDetails?.knowledge_based_certificate_obtained === "خیر" ? (
<div className=" border border-red-600 rounded-lg p-2 text-center"> <div className=" border border-pr-red mx-auto rounded-lg p-2 text-center">
<button className="text-red-400 font-medium"> <button className="text-pr-red font-bold text-sm">
گواهی دانشبنیان ندارد گواهی دانشبنیان ندارد
</button> </button>
</div> </div>
) : ( ) : (
<Card className="justify-self-center border-emerald-600 bg-transparent py-0"> <Card className="justify-self-center border-pr-green bg-transparent py-0">
<CardContent className="p-2 text-center"> <CardContent className="p-2 text-center">
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <Button
variant="default" variant="default"
className=" text-emerald-400 hover:bg-transparent cursor-pointer bg-transparent" className=" text-pr-green font-bold text-sm hover:bg-transparent cursor-pointer bg-transparent"
> >
مشاهده اطلاعات گواهی دانشبنیان مشاهده اطلاعات گواهی دانشبنیان
</Button> </Button>
@ -1070,237 +1071,89 @@ export function ProductInnovationPage() {
</div> </div>
) : ( ) : (
<div className="lg:col-span-2 border-r-2 flex flex-col gap-2 pr-4 pb-2 border-r-[#5F6284]/50"> <div className="lg:col-span-2 border-r-2 flex flex-col gap-2 pr-4 pb-2 border-r-[#5F6284]/50">
{/* Project Description */} {/* Project Description - two MetricCards side by side */}
<div className=" rounded-lg pt-4 flex w-full gap-2"> <div className="rounded-lg pt-4 grid grid-cols-2 gap-4 w-full">
<Card <MetricCard
className="bg-[linear-gradient(to_bottom_left,#464861,45%,#111628)] flex-1 backdrop-blur-sm border-gray-700/50 col-span-2" title="میزان صادارت محصول جدید"
> value={Math.round(popupStats?.new_products_export > 0 ? popupStats?.new_products_export : 0)}
<CardContent className="p-2 h-full"> percentValue={Math.round(popupStats?.new_products_export_percent > 0 ? popupStats?.new_products_export_percent : 0)}
<div className="grid grid-cols-2 justify-between gap-2 h-full"> valueLabel="میلیون ریال"
<div className="flex justify-between rows-start-1 col-span-2 items-center border-b-2 mx-4 border-gray-500/20"> percentLabel="درصد به کل صادرات"
<h3 className="text-lg text-white font-persian py-2"> />
میزان صادارت محصول جدید
</h3>
</div>
<div className="flex col-span-1 items-center row-start-2 my-auto col-start-1 justify-center flex-col p-1 my-auto">
<p
className={`text-2xl font-bold mb-1`}
>
{formatNumber(Math.round(popupStats?.new_products_export > 0 ? popupStats?.new_products_export : 0)) || formatNumber(0)}
</p>
<p className="text-xs font-thin text-gray-300 font-persian">
میلیون ریال
</p>
</div>
<span className="text-gray-600 row-start-2 self-center col-start-2 font-thin text-5xl">/</span>
<div className="flex col-span-1 items-center row-start-2 my-auto col-start-2 justify-center flex-col p-1 my-auto">
<p
className={`text-2xl font-bold mb-1`}
>
{formatNumber(Math.round(popupStats?.new_products_export_percent > 0 ? popupStats?.new_products_export_percent : 0)) || formatNumber(0)}%
</p>
<p className="text-xs font-thin text-gray-300 font-persian">
درصد به کل صادرات
</p>
</div>
</div>
</CardContent>
</Card>
<Card <MetricCard
className="bg-[linear-gradient(to_bottom_left,#464861,45%,#111628)] flex-1 backdrop-blur-sm border-gray-700/50 col-span-2" title="تاثیر در واردات"
> value={Math.round(popupStats?.import_impact > 0 ? popupStats?.import_impact : 0)}
<CardContent className="p-2 h-full"> percentValue={Math.round(popupStats?.import_impact_percent > 0 ? popupStats?.import_impact_percent : 0)}
<div className="grid grid-cols-2 justify-between gap-2 h-full"> valueLabel="میلیون ریال"
<div className="flex justify-between rows-start-1 col-span-2 items-center border-b-2 mx-4 border-gray-500/20"> percentLabel="درصد صرفه جویی"
<h3 className="text-lg text-white font-persian py-2"> />
تاثیر در واردات </div>
</h3>
</div>
<div className="flex col-span-1 items-center row-start-2 my-auto col-start-1 justify-center flex-col p-1 my-auto">
<p
className={`text-2xl font-bold mb-1`}
>
{formatNumber(Math.round(popupStats?.import_impact > 0 ? popupStats?.import_impact : 0)) || formatNumber(0)}
</p>
<p className="text-xs font-thin text-gray-300 font-persian">
میلیون ریال
</p>
</div>
<span className="text-gray-600 row-start-2 self-center col-start-2 font-thin text-5xl">/</span>
<div className="flex col-span-1 items-center row-start-2 my-auto col-start-2 justify-center flex-col p-1 my-auto">
<p
className={`text-2xl font-bold mb-1`}
>
{formatNumber(Math.round(popupStats?.import_impact_percent > 0 ? popupStats?.import_impact_percent : 0)) || formatNumber(0)}%
</p>
<p className="text-xs font-thin text-gray-300 font-persian">
درصد صرفه جویی
</p>
</div>
</div>
</CardContent>
</Card>
{/* Export Revenue Bar Chart */}
<div className="bg-[linear-gradient(to_bottom_left,#464861,45%,#111628)] rounded-lg px-6 py-4">
</div> <h3 className="text-sm font-semibold text-white">ظرفیت صادر شده</h3>
<div className="h-60">
{/* Export Revenue Bar Chart */} {exportChartData.length > 0 ? (
<div className="bg-[linear-gradient(to_bottom_left,#464861,45%,#111628)] rounded-lg px-6 py-4"> <ResponsiveContainer width="100%" height="100%">
<h3 className="text-lg font-semibold text-white">ظرفیت صادر شده</h3> <BarChart
<div className="h-60"> className="aspect-auto w-full"
{popupLoading ? ( data={sortedBarData}
<div className="flex items-center justify-center h-full"> barGap={15}
<div className="animate-pulse my-auto text-gray-400">در حال بارگذاری...</div> barSize={30}
</div> margin={{ top: 18 }}
) : exportChartData.length > 0 ? ( >
<ResponsiveContainer width="100%" height="100%" > <CartesianGrid vertical={false} stroke="#475569" />
<BarChart className="aspect-auto w-full" <XAxis
data={sortedBarData} dataKey="label"
barGap={15} tickLine={false}
barSize={30} axisLine={false}
margin={{ stroke="#C3C3C3"
top : 18 tickMargin={8}
}} tickFormatter={(value: string) => `${value.split(" ")[0]} ${formatNumber(value.split(" ")[1]).replaceAll('٬','')}`}
> fontSize={11}
<CartesianGrid vertical={false} stroke="#475569" />
<XAxis
dataKey="label"
tickLine={false}
axisLine={false}
stroke="#C3C3C3"
tickMargin={8}
tickFormatter={(value: string) => `${value.split(" ")[0]} ${formatNumber(value.split(" ")[1]).replaceAll('٬','')}`}
/>
<YAxis
tickLine={false}
axisLine={false}
stroke="#9CA3AF"
fontSize={11} tick={{ dx: -50 }} tickFormatter={(value: number) => `${formatNumber(value)} میلیون`}/>
<Bar
dataKey="value"
fill="#10B981"
radius={10}
>
<LabelList
formatter={(value : number) => `${formatNumber(value)}`}
position="top"
offset={15}
fill="F9FAFB"
className="fill-foreground"
fontSize={16}
/> />
</Bar> <YAxis tickLine={false} axisLine={false} stroke="#9CA3AF" fontSize={11} tick={{ dx: -50 }} tickFormatter={(value: number) => `${formatNumber(value)} میلیون`} />
<Bar dataKey="value" fill="#10B981" radius={10}>
</BarChart> <LabelList formatter={(value: number) => `${formatNumber(value)}`} position="top" offset={15} fill="F9FAFB" className="fill-foreground" fontSize={16} />
</ResponsiveContainer> </Bar>
) : ( </BarChart>
<div className="flex items-center justify-center h-full text-gray-400"> </ResponsiveContainer>
دادهای برای نمایش وجود ندارد ) : (
</div> <div className="flex items-center justify-center h-full text-gray-400">دادهای برای نمایش وجود ندارد</div>
)} )}
</div> </div>
</div> </div>
{/* Export Revenue Line Chart */} {/* Export Revenue Line Chart */}
<div className="bg-[linear-gradient(to_bottom_left,#464861,45%,#111628)] rounded-lg px-6 py-4"> <div className="bg-[linear-gradient(to_bottom_left,#464861,45%,#111628)] rounded-lg px-6 py-4">
<h3 className="text-lg font-semibold text-white">ظرفیت صادر شده</h3> <h3 className="text-sm font-semibold text-white">ظرفیت صادر شده</h3>
<div className="h-60"> <div className="h-60">
{popupLoading ? ( {allExportData.length > 0 ? (
<div className="flex items-center justify-center "> <ResponsiveContainer width="100%" height="100%">
<div className="animate-pulse my-auto text-gray-400">در حال بارگذاری...</div> <LineChart className="aspect-auto w-full" data={transformDataForLineChart(allExportData)} margin={{ top: 20, right: 30, left: 10, bottom: 50 }}>
</div> <CartesianGrid vertical={false} stroke="#374151" />
) : allExportData.length > 0 ? ( <XAxis dataKey="season" stroke="#9CA3AF" fontSize={11} tick={({ x, y, payload }) => (
<ResponsiveContainer width="100%" height="100%"> <g transform={`translate(${x},${y + 10})`}>
<LineChart <text x={-40} y={15} dy={0} textAnchor="end" fill="#9CA3AF" fontSize={11} transform="rotate(-45)">{(payload as any).value}</text>
accessibilityLayer </g>
className="aspect-auto w-full " )} />
data={transformDataForLineChart(allExportData)} <YAxis tickLine={false} axisLine={false} stroke="#9CA3AF" fontSize={11} tick={{ dx: -50 }} tickFormatter={(value) => `${formatNumber(value)} میلیون`} />
margin={{ top: 20, right: 30, left: 10, bottom: 50 }} <Tooltip formatter={(value: number) => `${formatNumber(value)} میلیون`} contentStyle={{ backgroundColor: "#1F2937", border: "1px solid #374151", borderRadius: "6px", padding: "6px 10px", fontSize: "11px", color: "#F9FAFB" }} />
> <Legend layout="vertical" verticalAlign="middle" align="right" iconType={"plainline"} className="!flex" wrapperStyle={{ fontSize: 11, paddingLeft: 12, gap: 10 }} />
<CartesianGrid vertical={false} stroke="#374151" /> {[...new Set(allExportData.map((item) => item.product_title))].slice(0, 5).map((product, index) => {
<XAxis const colors = ["#10B981", "#EF4444", "#3B82F6", "#F59E0B", "#8B5CF6"];
dataKey="season" return <Line key={product} type="linear" dot={false} activeDot={{ r: 5 }} dataKey={product} stroke={colors[index % colors.length]} strokeWidth={2} />;
stroke="#9CA3AF" })}
fontSize={11} </LineChart>
tick={({ x, y, payload }) => ( </ResponsiveContainer>
<g transform={`translate(${x},${y + 10})`}> ) : (
<text <div className="flex items-center justify-center h-full text-gray-400">دادهای برای نمایش وجود ندارد</div>
x={-40} )}
y={15} </div>
dy={0}
textAnchor="end"
fill="#9CA3AF"
fontSize={11}
transform="rotate(-45)"
>
{payload.value}
</text>
</g>
)}
/>
<YAxis
tickLine={false}
axisLine={false}
stroke="#9CA3AF"
fontSize={11}
tick={{ dx: -50 }}
tickFormatter={(value) => `${formatNumber(value)} میلیون`} // 👈 اضافه کردن M کنار اعداد
/>
<Tooltip
formatter={(value : number) => `${formatNumber(value)} میلیون`}
contentStyle={{
backgroundColor: "#1F2937",
border: "1px solid #374151",
borderRadius: "6px",
padding: "6px 10px",
fontSize: "11px",
color: "#F9FAFB",
}}
/>
<Legend
layout="vertical"
verticalAlign="middle"
align="right"
iconType={"plainline"}display={"flex !important"}
className="!flex"
wrapperStyle={{ fontSize: 11 , paddingLeft : 12 , display : "flex !important" , gap : 10} }
/>
{[...new Set(allExportData.map((item) => item.product_title))]
.slice(0, 5)
.map((product, index) => {
const colors = ["#10B981", "#EF4444", "#3B82F6", "#F59E0B", "#8B5CF6"];
return (
<Line
key={product.product_title}
type="linear"
dot={false}
activeDot={{ r: 5 }}
dataKey={product}
stroke={colors[index % colors.length]}
strokeWidth={2}
/>
);
})}
</LineChart>
</ResponsiveContainer>
) : (
<div className="flex items-center justify-center h-full text-gray-400">
دادهای برای نمایش وجود ندارد
</div>
)}
</div> </div>
</div> </div>
</div> )}
)}
</div> </div>

View File

@ -24,18 +24,18 @@ export function FunnelChart({ data, title, className = "" }: FunnelChartProps) {
return ( return (
<div className={`w-full ${className}`}> <div className={`w-full ${className}`}>
{title && ( {title && (
<h3 className="text-lg font-semibold text-white mb-4 py-2 text-right border-b-2 border-gray-400/20"> <h3 className="text-sm px-4 font-semibold text-white mb-4 py-2 text-right border-b-2 border-gray-400/20">
{title} {title}
</h3> </h3>
)} )}
<div className="flex flex-col items-center gap-2 space-y-2"> <div className="flex px-4 flex-col items-center gap-2 space-y-2">
{/* Start Process Line */} {/* Start Process Line */}
<div className="flex items-center w-full gap-10 mt-6"> <div className="flex items-center w-full gap-10 mt-6 px-4">
<div className="text-lg text-gray-600 min-w-[max-content]">ابتدا فرآیند</div> <div className="text-sm font-normal text-[#5F6284] min-w-[max-content]">ابتدا فرآیند</div>
<div className="flex items-center w-full gap-4"> <div className="flex items-center w-full gap-4">
<div className="w-full h-0.5 bg-gray-600 relative"> <div className="w-full h-0.5 bg-gray-600 relative">
<div className="text-2xl text-white absolute left-1/2 -translate-x-1/2 top-[-1rem] -translate-y-1/2">۱۰۰%</div> <div className="text-base text-white font-semibold absolute left-1/2 -translate-x-1/2 top-[-1rem] -translate-y-1/2">۱۰۰%</div>
<div className="absolute -top-1 left-0 w-1 h-3 bg-gray-600"></div> <div className="absolute -top-1 left-0 w-1 h-3 bg-gray-600"></div>
<div className="absolute -top-1 right-0 w-1 h-3 bg-gray-600"></div> <div className="absolute -top-1 right-0 w-1 h-3 bg-gray-600"></div>
</div> </div>
@ -50,7 +50,7 @@ export function FunnelChart({ data, title, className = "" }: FunnelChartProps) {
return ( return (
<div key={index} className="grid grid-cols-[6rem_1fr] gap-2 w-full"> <div key={index} className="grid grid-cols-[6rem_1fr] gap-2 w-full">
<div className="text-lg text-white cols-start-1 justify-self-start font-thin min-w-[max-content] text-center"> <div className="text-sm font-light text-white cols-start-1 justify-self-start font-thin min-w-[max-content] text-center">
{item.label} {item.label}
</div> </div>
<div className="flex items-center gap-10 w-full cols-start-2 flex items-center justify-center w-full"> <div className="flex items-center gap-10 w-full cols-start-2 flex items-center justify-center w-full">
@ -60,7 +60,7 @@ export function FunnelChart({ data, title, className = "" }: FunnelChartProps) {
className="bg-[#3BC47A] h-8 rounded-2xl flex items-center justify-center text-lg relative" className="bg-[#3BC47A] h-8 rounded-2xl flex items-center justify-center text-lg relative"
style={{ width: `${barWidth}%` }} style={{ width: `${barWidth}%` }}
> >
<span className="text-[#3F415A] font-semibold"> <span className="text-pr-gray text-base font-semibold">
{item.value.toLocaleString('fa-IR')} {item.value.toLocaleString('fa-IR')}
</span> </span>
</div> </div>
@ -73,15 +73,15 @@ export function FunnelChart({ data, title, className = "" }: FunnelChartProps) {
</div> </div>
{/* End Process Line */} {/* End Process Line */}
<div className="flex items-center w-full gap-10"> <div className="flex items-center w-full gap-10 px-4">
<div className="text-lg text-gray-600 min-w-[max-content]">انتها فرآیند</div> <div className="text-sm text-[#5F6284] min-w-[max-content]">انتها فرآیند</div>
<div className="flex items-center w-full gap-4"> <div className="flex items-center w-full gap-4">
{(() => { {(() => {
const lastValue = data[data.length - 1]?.value ?? 0; const lastValue = data[data.length - 1]?.value ?? 0;
const percent = toPercent(lastValue); const percent = toPercent(lastValue);
return ( return (
<div style={{ width: `${percent}%` }} className={`mx-auto h-0.5 bg-gray-600 relative ${percent === 0 ? "hidden" : ""}`}> <div style={{ width: `${percent}%` }} className={`mx-auto h-0.5 bg-gray-600 relative ${percent === 0 ? "hidden" : ""}`}>
<div className="text-2xl text-white absolute left-1/2 -translate-x-1/2 bottom-[-2.5rem] -translate-y-1">{formatNumber(percent)}%</div> <div className="text-base font-semibold text-white absolute left-1/2 -translate-x-1/2 bottom-[-2.5rem] -translate-y-1">{formatNumber(percent)}%</div>
<div className="absolute -top-1 left-0 w-1 h-3 bg-gray-600"></div> <div className="absolute -top-1 left-0 w-1 h-3 bg-gray-600"></div>
<div className="absolute -top-1 right-0 w-1 h-3 bg-gray-600"></div> <div className="absolute -top-1 right-0 w-1 h-3 bg-gray-600"></div>
</div> </div>