refactor_#3 #14

Merged
Saeed0920 merged 2 commits from refactor_#3 into main 2025-09-18 16:06:35 +03:30
2 changed files with 186 additions and 333 deletions
Showing only changes of commit 58331eed7a - Show all commits

View File

@ -21,6 +21,8 @@ import toast from "react-hot-toast";
import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button";
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 { Bar, BarChart, LabelList } from "recharts"
import {
@ -608,25 +610,25 @@ export function ProductInnovationPage() {
size="sm"
onClick={() => {
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>
);
case "project_no":
return (
<Badge variant="outline" className="font-mono">
<Badge variant="outline" className="font-mono text-base font-light">
{String(value)}
</Badge>
);
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":
return (
<div className="flex items-center gap-1">
<div className="flex items-center text-base font-light gap-1">
<Badge
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={{
border: "none",
}}
@ -638,13 +640,13 @@ export function ProductInnovationPage() {
return (
<Badge
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))}
</Badge>
);
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">
{/* Stats Grid */}
<div className="grid grid-cols-2 grid-rows-2 gap-5 h-full">
{loading || statsLoading
? // Loading skeleton for stats cards - matching new design
Array.from({ length: 3 }).map((_, index) => (
<Card
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"
>
<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" />
{loading || statsLoading ? (
// Loading skeleton for stats cards - matching new design
Array.from({ length: 3 }).map((_, index) => (
<Card
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"
>
<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 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 className="flex items-center justify-center flex-col p-1">
<div
className="h-8 bg-gray-600 rounded animate-pulse"
style={{ width: "40%" }}
/>
<div
className="h-4 bg-gray-600 rounded animate-pulse"
style={{ width: "80%" }}
/>
</div>
{/* Second card */}
<div>
<BaseCard title={stateCard.newProductExports.title} className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-gray-700/50">
<div className="flex items-center justify-center flex-col">
<div className="flex items-center gap-4">
<div className="text-center">
<p className="text-3xl font-bold mb-1 text-pr-green">{stateCard.newProductExports.value}</p>
<div className="text-xs text-gray-400 font-persian">{stateCard.newProductExports.description}</div>
</div>
</div>
</div>
</BaseCard>
</div>
</CardContent>
</Card>
))
: Object.entries(stateCard).map(([key, card], index) => (
<Card
key={card.id}
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"} `}
>
<CardContent className="p-2 h-full">
<div className="grid grid-cols-2 justify-between gap-2 h-full">
<div className="flex justify-between rows-start-1 col-span-2 items-center border-b-2 mx-4 border-gray-500/20">
<h3 className="text-lg text-white font-persian py-2">
{card.title}
</h3>
<div
className={`p-3 gird placeitems-center rounded-full w-fit `}
>
{/* Third card - basic BaseCard */}
<div>
<BaseCard title={stateCard.impactOnImports.title} className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-gray-700/50">
<div className="flex items-center justify-center flex-col">
<div className="flex items-center gap-4">
<div className="text-center">
<p className="text-3xl font-bold mb-1 text-pr-red">{stateCard.impactOnImports.value}</p>
<div className="text-xs text-gray-400 font-persian">{stateCard.impactOnImports.description}</div>
</div>
</div>
</div>
</BaseCard>
</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>
{/* Funnel Chart */}
<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
title="قيف فرآیند پروژه ها"
data={[
@ -795,7 +796,7 @@ export function ProductInnovationPage() {
{columns.map((column) => (
<TableHead
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 }}
>
{column.sortable ? (
@ -891,10 +892,10 @@ export function ProductInnovationPage() {
</CardContent>
{/* 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="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)}
</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>
</div>
<div className="flex justify-center items-center gap-2">
<div className="text-base text-gray-400">میانگین :</div>
<div className="font-bold">
<div className="text-bold text-sm text-white">میانگین :</div>
<div className="font-bold text-sm text-white">
{formatNumber(
((stats.average_project_score ?? 0) as number).toFixed?.(1) ?? 0
)}
@ -924,7 +925,7 @@ export function ProductInnovationPage() {
<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">
<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>
</DialogHeader>
@ -934,17 +935,17 @@ export function ProductInnovationPage() {
<div className="space-y-4">
{/* Stats Cards */}
<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>
</div>
<Timeline />
{/* Technical Knowledge */}
<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 items-center gap-2">
<span className="text-sm text-white">توسعه درونزا</span>
<span className="text-sm text-white font-light">توسعه درونزا</span>
<Checkbox
checked={selectedProjectDetails?.developed_technology_type === "توسعه درونزا"}
@ -953,7 +954,7 @@ export function ProductInnovationPage() {
</div>
<div className="flex items-center gap-2">
<span className="text-sm text-white">همکاری فناورانه</span>
<span className="text-sm text-white font-light">همکاری فناورانه</span>
<Checkbox
checked={selectedProjectDetails?.developed_technology_type === "همکاری فناوری"}
@ -962,7 +963,7 @@ export function ProductInnovationPage() {
</div>
<div className="flex items-center gap-2">
<span className="text-sm text-white">سایر</span>
<span className="text-sm text-white font-light">سایر</span>
<Checkbox
checked={selectedProjectDetails?.developed_technology_type === "سایر"}
className="data-[state=checked]:bg-emerald-600 data-[state=checked]:border-emerald-600"
@ -973,7 +974,7 @@ export function ProductInnovationPage() {
{/* Standards */}
<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>
@ -985,7 +986,7 @@ export function ProductInnovationPage() {
).map((standard, index) => (
<div key={index} className="flex items-center gap-2">
<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>
@ -997,21 +998,21 @@ export function ProductInnovationPage() {
</div>
{/* 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 === "خیر" ? (
<div className=" border border-red-600 rounded-lg p-2 text-center">
<button className="text-red-400 font-medium">
<div className=" border border-pr-red mx-auto rounded-lg p-2 text-center">
<button className="text-pr-red font-bold text-sm">
گواهی دانشبنیان ندارد
</button>
</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">
<Popover>
<PopoverTrigger asChild>
<Button
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>
@ -1070,237 +1071,89 @@ export function ProductInnovationPage() {
</div>
) : (
<div className="lg:col-span-2 border-r-2 flex flex-col gap-2 pr-4 pb-2 border-r-[#5F6284]/50">
{/* Project Description */}
<div className=" rounded-lg pt-4 flex w-full gap-2">
<Card
className="bg-[linear-gradient(to_bottom_left,#464861,45%,#111628)] flex-1 backdrop-blur-sm border-gray-700/50 col-span-2"
>
<CardContent className="p-2 h-full">
<div className="grid grid-cols-2 justify-between gap-2 h-full">
<div className="flex justify-between rows-start-1 col-span-2 items-center border-b-2 mx-4 border-gray-500/20">
<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>
{/* Project Description - two MetricCards side by side */}
<div className="rounded-lg pt-4 grid grid-cols-2 gap-4 w-full">
<MetricCard
title="میزان صادارت محصول جدید"
value={Math.round(popupStats?.new_products_export > 0 ? popupStats?.new_products_export : 0)}
percentValue={Math.round(popupStats?.new_products_export_percent > 0 ? popupStats?.new_products_export_percent : 0)}
valueLabel="میلیون ریال"
percentLabel="درصد به کل صادرات"
/>
<Card
className="bg-[linear-gradient(to_bottom_left,#464861,45%,#111628)] flex-1 backdrop-blur-sm border-gray-700/50 col-span-2"
>
<CardContent className="p-2 h-full">
<div className="grid grid-cols-2 justify-between gap-2 h-full">
<div className="flex justify-between rows-start-1 col-span-2 items-center border-b-2 mx-4 border-gray-500/20">
<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?.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>
<MetricCard
title="تاثیر در واردات"
value={Math.round(popupStats?.import_impact > 0 ? popupStats?.import_impact : 0)}
percentValue={Math.round(popupStats?.import_impact_percent > 0 ? popupStats?.import_impact_percent : 0)}
valueLabel="میلیون ریال"
percentLabel="درصد صرفه جویی"
/>
</div>
</div>
{/* Export Revenue Bar Chart */}
<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>
<div className="h-60">
{popupLoading ? (
<div className="flex items-center justify-center h-full">
<div className="animate-pulse my-auto text-gray-400">در حال بارگذاری...</div>
</div>
) : exportChartData.length > 0 ? (
<ResponsiveContainer width="100%" height="100%" >
<BarChart className="aspect-auto w-full"
data={sortedBarData}
barGap={15}
barSize={30}
margin={{
top : 18
}}
>
<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}
{/* Export Revenue Bar Chart */}
<div className="bg-[linear-gradient(to_bottom_left,#464861,45%,#111628)] rounded-lg px-6 py-4">
<h3 className="text-sm font-semibold text-white">ظرفیت صادر شده</h3>
<div className="h-60">
{exportChartData.length > 0 ? (
<ResponsiveContainer width="100%" height="100%">
<BarChart
className="aspect-auto w-full"
data={sortedBarData}
barGap={15}
barSize={30}
margin={{ top: 18 }}
>
<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('٬','')}`}
fontSize={11}
/>
</Bar>
</BarChart>
</ResponsiveContainer>
) : (
<div className="flex items-center justify-center h-full text-gray-400">
دادهای برای نمایش وجود ندارد
</div>
)}
</div>
</div>
<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>
</BarChart>
</ResponsiveContainer>
) : (
<div className="flex items-center justify-center h-full text-gray-400">دادهای برای نمایش وجود ندارد</div>
)}
</div>
</div>
{/* Export Revenue Line Chart */}
<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>
<div className="h-60">
{popupLoading ? (
<div className="flex items-center justify-center ">
<div className="animate-pulse my-auto text-gray-400">در حال بارگذاری...</div>
</div>
) : allExportData.length > 0 ? (
<ResponsiveContainer width="100%" height="100%">
<LineChart
accessibilityLayer
className="aspect-auto w-full "
data={transformDataForLineChart(allExportData)}
margin={{ top: 20, right: 30, left: 10, bottom: 50 }}
>
<CartesianGrid vertical={false} stroke="#374151" />
<XAxis
dataKey="season"
stroke="#9CA3AF"
fontSize={11}
tick={({ x, y, payload }) => (
<g transform={`translate(${x},${y + 10})`}>
<text
x={-40}
y={15}
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>
)}
{/* Export Revenue Line Chart */}
<div className="bg-[linear-gradient(to_bottom_left,#464861,45%,#111628)] rounded-lg px-6 py-4">
<h3 className="text-sm font-semibold text-white">ظرفیت صادر شده</h3>
<div className="h-60">
{allExportData.length > 0 ? (
<ResponsiveContainer width="100%" height="100%">
<LineChart className="aspect-auto w-full" data={transformDataForLineChart(allExportData)} margin={{ top: 20, right: 30, left: 10, bottom: 50 }}>
<CartesianGrid vertical={false} stroke="#374151" />
<XAxis dataKey="season" stroke="#9CA3AF" fontSize={11} tick={({ x, y, payload }) => (
<g transform={`translate(${x},${y + 10})`}>
<text x={-40} y={15} dy={0} textAnchor="end" fill="#9CA3AF" fontSize={11} transform="rotate(-45)">{(payload as any).value}</text>
</g>
)} />
<YAxis tickLine={false} axisLine={false} stroke="#9CA3AF" fontSize={11} tick={{ dx: -50 }} tickFormatter={(value) => `${formatNumber(value)} میلیون`} />
<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 }} />
{[...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} 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>

View File

@ -24,18 +24,18 @@ export function FunnelChart({ data, title, className = "" }: FunnelChartProps) {
return (
<div className={`w-full ${className}`}>
{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}
</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 */}
<div className="flex items-center w-full gap-10 mt-6">
<div className="text-lg text-gray-600 min-w-[max-content]">ابتدا فرآیند</div>
<div className="flex items-center w-full gap-10 mt-6 px-4">
<div className="text-sm font-normal text-[#5F6284] min-w-[max-content]">ابتدا فرآیند</div>
<div className="flex items-center w-full gap-4">
<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 right-0 w-1 h-3 bg-gray-600"></div>
</div>
@ -50,7 +50,7 @@ export function FunnelChart({ data, title, className = "" }: FunnelChartProps) {
return (
<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}
</div>
<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"
style={{ width: `${barWidth}%` }}
>
<span className="text-[#3F415A] font-semibold">
<span className="text-pr-gray text-base font-semibold">
{item.value.toLocaleString('fa-IR')}
</span>
</div>
@ -73,15 +73,15 @@ export function FunnelChart({ data, title, className = "" }: FunnelChartProps) {
</div>
{/* End Process Line */}
<div className="flex items-center w-full gap-10">
<div className="text-lg text-gray-600 min-w-[max-content]">انتها فرآیند</div>
<div className="flex items-center w-full gap-10 px-4">
<div className="text-sm text-[#5F6284] min-w-[max-content]">انتها فرآیند</div>
<div className="flex items-center w-full gap-4">
{(() => {
const lastValue = data[data.length - 1]?.value ?? 0;
const percent = toPercent(lastValue);
return (
<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 right-0 w-1 h-3 bg-gray-600"></div>
</div>