product-idea #10
|
|
@ -22,7 +22,7 @@ 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 { Checkbox } from "~/components/ui/checkbox";
|
import { Checkbox } from "~/components/ui/checkbox";
|
||||||
import { CustomBarChart } from "~/components/ui/custom-bar-chart";
|
import { Bar, BarChart, LabelList } from "recharts"
|
||||||
import { FunnelChart } from "~/components/ui/funnel-chart";
|
import { FunnelChart } from "~/components/ui/funnel-chart";
|
||||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from "recharts";
|
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from "recharts";
|
||||||
import {
|
import {
|
||||||
|
|
@ -138,7 +138,6 @@ export function ProductInnovationPage() {
|
||||||
const [pageSize] = useState(20);
|
const [pageSize] = useState(20);
|
||||||
const [hasMore, setHasMore] = useState(true);
|
const [hasMore, setHasMore] = useState(true);
|
||||||
const [totalCount, setTotalCount] = useState(0);
|
const [totalCount, setTotalCount] = useState(0);
|
||||||
const [actualTotalCount, setActualTotalCount] = useState(0);
|
|
||||||
const [statsLoading, setStatsLoading] = useState(false);
|
const [statsLoading, setStatsLoading] = useState(false);
|
||||||
const [stats, setStats] = useState<ProductInnovationStats>({
|
const [stats, setStats] = useState<ProductInnovationStats>({
|
||||||
new_products_revenue_share: 0,
|
new_products_revenue_share: 0,
|
||||||
|
|
@ -199,21 +198,22 @@ export function ProductInnovationPage() {
|
||||||
const observerRef = useRef<HTMLDivElement>(null);
|
const observerRef = useRef<HTMLDivElement>(null);
|
||||||
const fetchingRef = useRef(false);
|
const fetchingRef = useRef(false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleProjectDetails = async (project: ProductInnovationData) => {
|
const handleProjectDetails = async (project: ProductInnovationData) => {
|
||||||
setSelectedProjectDetails(project);
|
setSelectedProjectDetails(project);
|
||||||
setDetailsDialogOpen(true);
|
setDetailsDialogOpen(true);
|
||||||
await fetchPopupData(project.project_id);
|
await fetchPopupData(project);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPopupData = async (projectId: string) => {
|
const fetchPopupData = async (project: ProductInnovationData) => {
|
||||||
try {
|
try {
|
||||||
setPopupLoading(true);
|
setPopupLoading(true);
|
||||||
|
|
||||||
// Fetch popup stats
|
// Fetch popup stats
|
||||||
const statsResponse = await apiService.call({
|
const statsResponse = await apiService.call({
|
||||||
innovation_product_popup_function1: {
|
innovation_product_popup_function1: {
|
||||||
project_id: projectId
|
project_id: project.project_id
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -239,12 +239,12 @@ export function ProductInnovationPage() {
|
||||||
const chartData = JSON.parse(chartResponse.data);
|
const chartData = JSON.parse(chartResponse.data);
|
||||||
if (Array.isArray(chartData)) {
|
if (Array.isArray(chartData)) {
|
||||||
// Set all data for line chart
|
// Set all data for line chart
|
||||||
setAllExportData(chartData);
|
|
||||||
|
|
||||||
// Filter data for the selected project (bar chart)
|
// Filter data for the selected project (bar chart)
|
||||||
const filteredData = chartData.filter(item =>
|
const filteredData = chartData.filter(item =>
|
||||||
item.product_title === selectedProjectDetails?.title
|
item.product_title === project?.title
|
||||||
);
|
);
|
||||||
|
setAllExportData(chartData);
|
||||||
setExportChartData(filteredData);
|
setExportChartData(filteredData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -486,16 +486,15 @@ export function ProductInnovationPage() {
|
||||||
|
|
||||||
// Transform data for line chart
|
// Transform data for line chart
|
||||||
const transformDataForLineChart = (data: any[]) => {
|
const transformDataForLineChart = (data: any[]) => {
|
||||||
const seasons = [...new Set(data.map(item => item.full_season))].sort();
|
const seasons = [...new Set(data.map(item => item.full_season))];
|
||||||
const products = [...new Set(data.map(item => item.product_title))];
|
const products = [...new Set(data.map(item => item.product_title))];
|
||||||
|
|
||||||
return seasons.map(season => {
|
return seasons.map(season => {
|
||||||
const seasonData: any = { season };
|
const seasonData: any = { season };
|
||||||
products.forEach(product => {
|
products.forEach(product => {
|
||||||
const productData = data.find(item =>
|
const productData = data.find(item =>
|
||||||
item.product_title === product && item.full_season === season
|
item.product_title === product && item.full_season === season
|
||||||
);
|
);
|
||||||
seasonData[product] = productData ? Math.round(productData.export_revenue_sum / 1000000) : 0;
|
seasonData[product] = productData?.export_revenue_sum > 0 && productData ? Math.round(productData?.export_revenue_sum) : 0;
|
||||||
});
|
});
|
||||||
return seasonData;
|
return seasonData;
|
||||||
});
|
});
|
||||||
|
|
@ -550,7 +549,8 @@ export function ProductInnovationPage() {
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => handleProjectDetails(item)}
|
onClick={() => {
|
||||||
|
handleProjectDetails(item)}}
|
||||||
className="text-emerald-400 hover:text-emerald-300 hover:bg-emerald-500/20 p-2 h-auto"
|
className="text-emerald-400 hover:text-emerald-300 hover:bg-emerald-500/20 p-2 h-auto"
|
||||||
>
|
>
|
||||||
جزئیات بیشتر
|
جزئیات بیشتر
|
||||||
|
|
@ -591,6 +591,20 @@ export function ProductInnovationPage() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const seasonOrder = ["بهار", "تابستان", "پاییز", "زمستان"];
|
||||||
|
const sortedBarData = exportChartData
|
||||||
|
.sort((a, b) => {
|
||||||
|
const getSeasonIndex = (s: string) => {
|
||||||
|
const [seasonName, year] = s.split(" ");
|
||||||
|
return parseInt(year) * 4 + seasonOrder.indexOf(seasonName);
|
||||||
|
};
|
||||||
|
return getSeasonIndex(a.full_season) - getSeasonIndex(b.full_season);
|
||||||
|
})
|
||||||
|
.map((item) => ({
|
||||||
|
label: item.full_season,
|
||||||
|
value: item.export_revenue_sum < 0 ? 0 : Math.round(item.export_revenue_sum) ,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout title="نوآوری در محصول">
|
<DashboardLayout title="نوآوری در محصول">
|
||||||
<div className="p-6 space-y-4 flex justify-center gap-4">
|
<div className="p-6 space-y-4 flex justify-center gap-4">
|
||||||
|
|
@ -619,7 +633,7 @@ export function ProductInnovationPage() {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center flex-col p-1">
|
<div className="flex items-center justify-center flex-col p-1">
|
||||||
<div
|
<div
|
||||||
className="h-8 bg-gray-600 rounded mb-1 animate-pulse"
|
className="h-8 bg-gray-600 rounded animate-pulse"
|
||||||
style={{ width: "40%" }}
|
style={{ width: "40%" }}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
|
@ -631,7 +645,7 @@ export function ProductInnovationPage() {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))
|
))
|
||||||
: Object.entries(stateCard).map(([key, card] , index) => (
|
: Object.entries(stateCard).map(([key, card], index) => (
|
||||||
<Card
|
<Card
|
||||||
key={card.id}
|
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"} `}
|
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"} `}
|
||||||
|
|
@ -794,7 +808,7 @@ export function ProductInnovationPage() {
|
||||||
{columns.map((column) => (
|
{columns.map((column) => (
|
||||||
<TableCell
|
<TableCell
|
||||||
key={column.key}
|
key={column.key}
|
||||||
className="text-center whitespace-nowrap border-emerald-500/20 py-1 px-2"
|
className={`text-right whitespace-nowrap border-emerald-500/20 py-1 px-2`}
|
||||||
>
|
>
|
||||||
{renderCellContent(project, column)}
|
{renderCellContent(project, column)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
@ -823,7 +837,7 @@ export function ProductInnovationPage() {
|
||||||
<div className="p-2 px-4 bg-[#3F415A]">
|
<div className="p-2 px-4 bg-[#3F415A]">
|
||||||
<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 mb-1">
|
<div className="text-base text-gray-401">
|
||||||
کل پروژه ها :{formatNumber(stats?.count_innovation_construction_inside_projects)}
|
کل پروژه ها :{formatNumber(stats?.count_innovation_construction_inside_projects)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -836,7 +850,7 @@ 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 mb-1">میانگین :</div>
|
<div className="text-base text-gray-400">میانگین :</div>
|
||||||
<div className="font-bold">
|
<div className="font-bold">
|
||||||
{formatNumber(
|
{formatNumber(
|
||||||
((stats.average_project_score ?? 0) as number).toFixed?.(1) ?? 0
|
((stats.average_project_score ?? 0) as number).toFixed?.(1) ?? 0
|
||||||
|
|
@ -857,150 +871,11 @@ export function ProductInnovationPage() {
|
||||||
جزئیات پروژه
|
جزئیات پروژه
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 p-6">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 p-6">
|
||||||
{/* Left Column - Project Description and Charts */}
|
|
||||||
<div className="lg:col-span-2 space-y-6">
|
{/* Right Column - Stats Cards and Details */}
|
||||||
{/* Project Description */}
|
<div className="space-y-6">
|
||||||
<div className="bg-gray-800/50 rounded-lg p-6">
|
|
||||||
<h2 className="text-2xl font-bold text-white mb-4">
|
|
||||||
{selectedProjectDetails?.title}
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-300 font-persian leading-relaxed">
|
|
||||||
{selectedProjectDetails?.project_description || "-"}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{/* Project Timeline */}
|
|
||||||
<div className="mt-6">
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<span className="text-sm text-gray-400">۱۴۰۴</span>
|
|
||||||
<span className="text-sm text-gray-400">۱۴۰۵</span>
|
|
||||||
<span className="text-sm text-gray-400">۱۴۰۶</span>
|
|
||||||
<span className="text-sm text-gray-400">۱۴۰۷</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<div className="w-4 h-4 bg-emerald-500 rounded-full"></div>
|
|
||||||
<span className="text-sm text-white">ثبت ایده</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 h-1 bg-gray-600"></div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<div className="w-4 h-4 bg-emerald-500 rounded-full"></div>
|
|
||||||
<span className="text-sm text-white">تحلیل بازار</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 h-1 bg-gray-600"></div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<div className="w-4 h-4 bg-gray-600 rounded-full"></div>
|
|
||||||
<span className="text-sm text-gray-400">توسعه</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 h-1 bg-gray-600"></div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<div className="w-4 h-4 bg-gray-600 rounded-full"></div>
|
|
||||||
<span className="text-sm text-gray-400">تجاری سازی</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-emerald-400 mt-2">وضعیت فعلی: تحلیل بازار</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Export Revenue Bar Chart */}
|
|
||||||
<div className="bg-gray-800/50 rounded-lg p-6">
|
|
||||||
<h3 className="text-lg font-semibold text-white mb-4">ظرفیت صادر شده</h3>
|
|
||||||
<div className="h-64">
|
|
||||||
{popupLoading ? (
|
|
||||||
<div className="flex items-center justify-center h-full">
|
|
||||||
<div className="animate-pulse text-gray-400">در حال بارگذاری...</div>
|
|
||||||
</div>
|
|
||||||
) : exportChartData.length > 0 ? (
|
|
||||||
<CustomBarChart
|
|
||||||
title=""
|
|
||||||
loading={false}
|
|
||||||
data={exportChartData
|
|
||||||
.sort((a, b) => {
|
|
||||||
// Sort by season order
|
|
||||||
const seasonOrder = ['بهار', 'تابستان', 'پاییز', 'زمستان'];
|
|
||||||
const getSeasonIndex = (season: string) => {
|
|
||||||
const year = season.split(' ')[1];
|
|
||||||
const seasonName = season.split(' ')[0];
|
|
||||||
return parseInt(year) * 4 + seasonOrder.indexOf(seasonName);
|
|
||||||
};
|
|
||||||
return getSeasonIndex(a.full_season) - getSeasonIndex(b.full_season);
|
|
||||||
})
|
|
||||||
.map(item => ({
|
|
||||||
label: item.full_season,
|
|
||||||
value: Math.round(item.export_revenue_sum / 1000000), // Convert to millions
|
|
||||||
color: "bg-emerald-400",
|
|
||||||
labelColor: "text-white"
|
|
||||||
}))}
|
|
||||||
barHeight="h-6"
|
|
||||||
showAxisLabels={true}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="flex items-center justify-center h-full text-gray-400">
|
|
||||||
دادهای برای نمایش وجود ندارد
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Export Revenue Line Chart */}
|
|
||||||
<div className="bg-gray-800/50 rounded-lg p-6">
|
|
||||||
<h3 className="text-lg font-semibold text-white mb-4">ظرفیت صادر شده</h3>
|
|
||||||
<div className="h-64">
|
|
||||||
{popupLoading ? (
|
|
||||||
<div className="flex items-center justify-center h-full">
|
|
||||||
<div className="animate-pulse text-gray-400">در حال بارگذاری...</div>
|
|
||||||
</div>
|
|
||||||
) : allExportData.length > 0 ? (
|
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
|
||||||
<LineChart data={transformDataForLineChart(allExportData)}>
|
|
||||||
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
|
||||||
<XAxis
|
|
||||||
dataKey="season"
|
|
||||||
stroke="#9CA3AF"
|
|
||||||
fontSize={12}
|
|
||||||
/>
|
|
||||||
<YAxis
|
|
||||||
stroke="#9CA3AF"
|
|
||||||
fontSize={12}
|
|
||||||
domain={[0, 1000]}
|
|
||||||
/>
|
|
||||||
<Tooltip
|
|
||||||
contentStyle={{
|
|
||||||
backgroundColor: '#1F2937',
|
|
||||||
border: '1px solid #374151',
|
|
||||||
borderRadius: '8px',
|
|
||||||
color: '#F9FAFB'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Legend />
|
|
||||||
{[...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="monotone"
|
|
||||||
dataKey={product}
|
|
||||||
stroke={colors[index % colors.length]}
|
|
||||||
strokeWidth={2}
|
|
||||||
dot={{ fill: colors[index % colors.length], strokeWidth: 2, r: 4 }}
|
|
||||||
name={`محصول ${index + 1}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</LineChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
) : (
|
|
||||||
<div className="flex items-center justify-center h-full text-gray-400">
|
|
||||||
دادهای برای نمایش وجود ندارد
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right Column - Stats Cards and Details */}
|
|
||||||
<div className="space-y-6">
|
|
||||||
{/* Stats Cards */}
|
{/* Stats Cards */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="bg-gray-800/50 rounded-lg p-4">
|
<div className="bg-gray-800/50 rounded-lg p-4">
|
||||||
|
|
@ -1013,7 +888,7 @@ export function ProductInnovationPage() {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="text-2xl font-bold text-emerald-400 mb-1">
|
<div className="text-2xl font-bold text-emerald-400">
|
||||||
{formatNumber(popupStats.new_products_export)}
|
{formatNumber(popupStats.new_products_export)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-300">میلیون ریال</div>
|
<div className="text-sm text-gray-300">میلیون ریال</div>
|
||||||
|
|
@ -1035,7 +910,7 @@ export function ProductInnovationPage() {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="text-2xl font-bold text-emerald-400 mb-1">
|
<div className="text-2xl font-bold text-emerald-400">
|
||||||
{formatNumber(popupStats.import_impact)}
|
{formatNumber(popupStats.import_impact)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-300">میلیون ریال</div>
|
<div className="text-sm text-gray-300">میلیون ریال</div>
|
||||||
|
|
@ -1106,6 +981,203 @@ export function ProductInnovationPage() {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Left Column - Project Description and Charts */}
|
||||||
|
<div className="lg:col-span-2 space-y-6">
|
||||||
|
{/* Project Description */}
|
||||||
|
<div className="bg-gray-800/50 rounded-lg p-6">
|
||||||
|
<h2 className="text-2xl font-bold text-white mb-4">
|
||||||
|
{selectedProjectDetails?.title}
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-300 font-persian leading-relaxed">
|
||||||
|
{selectedProjectDetails?.project_description || "-"}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Project Timeline */}
|
||||||
|
<div className="mt-6">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<span className="text-sm text-gray-400">۱۴۰۴</span>
|
||||||
|
<span className="text-sm text-gray-400">۱۴۰۵</span>
|
||||||
|
<span className="text-sm text-gray-400">۱۴۰۶</span>
|
||||||
|
<span className="text-sm text-gray-400">۱۴۰۷</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="w-4 h-4 bg-emerald-500 rounded-full"></div>
|
||||||
|
<span className="text-sm text-white">ثبت ایده</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 h-1 bg-gray-600"></div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="w-4 h-4 bg-emerald-500 rounded-full"></div>
|
||||||
|
<span className="text-sm text-white">تحلیل بازار</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 h-1 bg-gray-600"></div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="w-4 h-4 bg-gray-600 rounded-full"></div>
|
||||||
|
<span className="text-sm text-gray-400">توسعه</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 h-1 bg-gray-600"></div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="w-4 h-4 bg-gray-600 rounded-full"></div>
|
||||||
|
<span className="text-sm text-gray-400">تجاری سازی</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-emerald-400 mt-2">وضعیت فعلی: تحلیل بازار</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Export Revenue Bar Chart */}
|
||||||
|
<div className="bg-gray-800/50 rounded-lg p-6">
|
||||||
|
<h3 className="text-lg font-semibold text-white mb-4">ظرفیت صادر شده</h3>
|
||||||
|
<div className="h-64">
|
||||||
|
{popupLoading ? (
|
||||||
|
<div className="flex items-center justify-center h-full">
|
||||||
|
<div className="animate-pulse text-gray-400">در حال بارگذاری...</div>
|
||||||
|
</div>
|
||||||
|
) : exportChartData.length > 0 ? (
|
||||||
|
<ResponsiveContainer width="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}
|
||||||
|
/>
|
||||||
|
</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-gray-800/50 rounded-lg p-6">
|
||||||
|
<h3 className="text-lg font-semibold text-white mb-4">ظرفیت صادر شده</h3>
|
||||||
|
<div className="h-64">
|
||||||
|
{popupLoading ? (
|
||||||
|
<div className="flex items-center justify-center h-full">
|
||||||
|
<div className="animate-pulse 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>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,7 @@ export function Sidebar({
|
||||||
if (item.id === "strategic-alignment") {
|
if (item.id === "strategic-alignment") {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
key={item.id}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full text-right",
|
"w-full text-right",
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user