821 lines
32 KiB
TypeScript
821 lines
32 KiB
TypeScript
import { useState, useEffect } from "react";
|
||
import { DashboardLayout } from "./layout";
|
||
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
||
import { Progress } from "~/components/ui/progress";
|
||
import { Badge } from "~/components/ui/badge";
|
||
import { Button } from "~/components/ui/button";
|
||
import {
|
||
BarChart,
|
||
Bar,
|
||
XAxis,
|
||
YAxis,
|
||
CartesianGrid,
|
||
Tooltip,
|
||
ResponsiveContainer,
|
||
LineChart,
|
||
Line,
|
||
} from "recharts";
|
||
import apiService from "~/lib/api";
|
||
import toast from "react-hot-toast";
|
||
import {
|
||
Calendar,
|
||
TrendingUp,
|
||
TrendingDown,
|
||
Target,
|
||
Lightbulb,
|
||
DollarSign,
|
||
Minus,
|
||
CheckCircle,
|
||
Book,
|
||
} from "lucide-react";
|
||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "~/components/ui/tabs";
|
||
import { CustomBarChart } from "~/components/ui/custom-bar-chart";
|
||
import { DashboardCustomBarChart } from "./dashboard-custom-bar-chart";
|
||
import { InteractiveBarChart } from "./interactive-bar-chart";
|
||
import { D3ImageInfo } from "./d3-image-info";
|
||
import {
|
||
Label,
|
||
PolarGrid,
|
||
PolarRadiusAxis,
|
||
RadialBar,
|
||
RadialBarChart,
|
||
} from "recharts";
|
||
import { ChartContainer } from "~/components/ui/chart";
|
||
import { formatNumber } from "~/lib/utils";
|
||
import { MetricCard } from "~/components/ui/metric-card";
|
||
import { BaseCard } from "~/components/ui/base-card";
|
||
|
||
export function DashboardHome() {
|
||
const [dashboardData, setDashboardData] = useState<any | null>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState<string | null>(null);
|
||
// Chart and schematic data from select API
|
||
const [companyChartData, setCompanyChartData] = useState<
|
||
{ category: string; capacity: number; revenue: number; cost: number , costI : number,
|
||
capacityI : number,
|
||
revenueI : number }[]
|
||
>([]);
|
||
const [totalIncreasedCapacity, setTotalIncreasedCapacity] = useState<number>(0);
|
||
|
||
useEffect(() => {
|
||
fetchDashboardData();
|
||
}, []);
|
||
|
||
const fetchDashboardData = async () => {
|
||
try {
|
||
setLoading(true);
|
||
setError(null);
|
||
|
||
// First authenticate if needed
|
||
const token = localStorage.getItem("auth_token");
|
||
if (!token) {
|
||
await apiService.login("inogen_admin", "123456");
|
||
}
|
||
|
||
// Fetch top cards data
|
||
const topCardsResponse = await apiService.call({
|
||
main_page_first_function: {},
|
||
});
|
||
|
||
// Fetch left section data
|
||
const leftCardsResponse = await apiService.call({
|
||
main_page_second_function: {},
|
||
});
|
||
|
||
const topCardsResponseData = JSON.parse(topCardsResponse?.data);
|
||
const leftCardsResponseData = JSON.parse(leftCardsResponse?.data);
|
||
|
||
console.log("API Responses:", {
|
||
topCardsResponseData,
|
||
leftCardsResponseData,
|
||
});
|
||
|
||
// Use real API data structure with English keys
|
||
const topData = topCardsResponseData || {};
|
||
const leftData = leftCardsResponseData || {};
|
||
const realData = {
|
||
topData: topData,
|
||
leftData: leftData,
|
||
chartData: leftCardsResponseData?.chartData || [],
|
||
};
|
||
setDashboardData(realData);
|
||
|
||
// Fetch company aggregates for chart and schematic (select API)
|
||
const selectPayload = {
|
||
ProcessName: "project",
|
||
OutputFields: [
|
||
"related_company",
|
||
"sum(pre_innovation_fee)",
|
||
"sum(innovation_cost_reduction)",
|
||
"sum(pre_project_production_capacity)",
|
||
"sum(increased_capacity_after_innovation)",
|
||
"sum(pre_project_income)",
|
||
"sum(increased_income_after_innovation)",
|
||
],
|
||
GroupBy: ["related_company"],
|
||
};
|
||
|
||
const selectResp = await apiService.select(selectPayload);
|
||
const selectDataRaw = ((): any => {
|
||
try {
|
||
return typeof selectResp?.data === "string"
|
||
? JSON.parse(selectResp.data)
|
||
: selectResp?.data;
|
||
} catch {
|
||
return [];
|
||
}
|
||
})();
|
||
|
||
const rows: any[] = Array.isArray(selectDataRaw) ? selectDataRaw : [];
|
||
let incCapacityTotal = 0;
|
||
const chartRows = rows.map((r) => {
|
||
const rel = r?.related_company ?? "-";
|
||
const preFee = Number(r?.pre_innovation_fee_sum ?? 0) > 0 ? r?.pre_innovation_fee_sum : 0;
|
||
const costRed = Number(r?.innovation_cost_reduction_sum ?? 0) > 0 ? r?.innovation_cost_reduction_sum : 0;
|
||
const preCap = Number(r?.pre_project_production_capacity_sum ?? 0) > 0 ? r?.pre_project_production_capacity_sum : 0;
|
||
const incCap = Number(r?.increased_capacity_after_innovation_sum ?? 0) > 0 ? r?.increased_capacity_after_innovation_sum : 0;
|
||
const preInc = Number(r?.pre_project_income_sum ?? 0) > 0 ? r?.pre_project_income_sum : 0;
|
||
const incInc = Number(r?.increased_income_after_innovation_sum ?? 0) > 0 ? r?.increased_income_after_innovation_sum : 0;
|
||
|
||
incCapacityTotal += incCap;
|
||
|
||
const capacityPct = preCap > 0 ? (incCap / preCap) * 100 : 0;
|
||
const revenuePct = preInc > 0 ? (incInc / preInc) * 100 : 0;
|
||
const costPct = preFee > 0 ? (costRed / preFee) * 100 : 0;
|
||
return {
|
||
category: rel,
|
||
capacity: isFinite(capacityPct) ? capacityPct : 0,
|
||
revenue: isFinite(revenuePct) ? revenuePct : 0,
|
||
cost: isFinite(costPct) ? costPct : 0,
|
||
costI : costRed,
|
||
capacityI : incCap,
|
||
revenueI : incInc
|
||
};
|
||
});
|
||
|
||
setCompanyChartData(chartRows);
|
||
setTotalIncreasedCapacity(incCapacityTotal);
|
||
} catch (error) {
|
||
console.error("Error fetching dashboard data:", error);
|
||
const errorMessage =
|
||
error instanceof Error ? error.message : "خطای نامشخص";
|
||
setError(`خطا در بارگذاری دادهها: ${errorMessage}`);
|
||
toast.error(`خطا در بارگذاری دادهها: ${errorMessage}`);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
// RadialBarChart data for ideas visualization
|
||
const getIdeasChartData = () => {
|
||
if (!dashboardData?.topData)
|
||
return [{ browser: "safari", visitors: 0, fill: "var(--color-safari)" }];
|
||
|
||
const registered = parseFloat(
|
||
dashboardData.topData.registered_innovation_technology_idea || "0",
|
||
);
|
||
const ongoing = parseFloat(
|
||
dashboardData.topData.ongoing_innovation_technology_ideas || "0",
|
||
);
|
||
const percentage =
|
||
registered > 0 ? Math.round((ongoing / registered) * 100) : 0;
|
||
|
||
return [
|
||
{ browser: "safari", visitors: percentage, fill: "var(--color-safari)" },
|
||
];
|
||
};
|
||
|
||
const chartData = getIdeasChartData();
|
||
|
||
const chartConfig = {
|
||
visitors: {
|
||
label: "Ideas Progress",
|
||
},
|
||
safari: {
|
||
label: "Safari",
|
||
color: "var(--chart-2)",
|
||
},
|
||
};
|
||
|
||
// Skeleton component for cards
|
||
const SkeletonCard = ({ className = "" }) => (
|
||
<div
|
||
className={`bg-gray-700/50 rounded-lg overflow-hidden animate-pulse ${className}`}
|
||
>
|
||
<div className="p-6">
|
||
<div className="h-6 bg-gray-600 rounded w-3/4 mb-4"></div>
|
||
<div className="h-4 bg-gray-600 rounded w-1/2 mb-6"></div>
|
||
<div className="h-3 bg-gray-600 rounded w-full mb-2"></div>
|
||
<div className="h-3 bg-gray-600 rounded w-5/6"></div>
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
// Skeleton for the chart
|
||
const SkeletonChart = () => (
|
||
<div className="bg-gray-700/50 rounded-lg overflow-hidden animate-pulse p-6">
|
||
<div className="flex justify-between items-center mb-6">
|
||
<div className="h-6 bg-gray-600 rounded w-1/4"></div>
|
||
<div className="flex space-x-2 rtl:space-x-reverse">
|
||
<div className="h-8 w-24 bg-gray-600 rounded"></div>
|
||
<div className="h-8 w-24 bg-gray-600 rounded"></div>
|
||
<div className="h-8 w-24 bg-gray-600 rounded"></div>
|
||
</div>
|
||
</div>
|
||
<div className="h-64 bg-gray-800/50 rounded-lg flex items-end space-x-1 rtl:space-x-reverse p-4">
|
||
{[...Array(12)].map((_, i) => (
|
||
<div key={i} className="flex-1 flex space-x-1 rtl:space-x-reverse">
|
||
<div
|
||
className="w-full bg-blue-400/30 rounded-t-sm"
|
||
style={{ height: `${Math.random() * 80 + 20}%` }}
|
||
></div>
|
||
<div
|
||
className="w-full bg-green-400/30 rounded-t-sm"
|
||
style={{ height: `${Math.random() * 80 + 20}%` }}
|
||
></div>
|
||
<div
|
||
className="w-full bg-red-400/30 rounded-t-sm"
|
||
style={{ height: `${Math.random() * 80 + 20}%` }}
|
||
></div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div className="flex justify-between mt-4">
|
||
{[...Array(6)].map((_, i) => (
|
||
<div key={i} className="h-3 bg-gray-600 rounded w-1/6"></div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
if (loading) {
|
||
return (
|
||
<DashboardLayout>
|
||
<div className="p-3 pb-0 grid grid-cols-3 gap-4 animate-pulse">
|
||
{/* Top Cards Row */}
|
||
<div className="flex justify-between gap-6 [&>*]:w-full col-span-3">
|
||
<SkeletonCard />
|
||
<SkeletonCard />
|
||
<SkeletonCard />
|
||
<SkeletonCard />
|
||
</div>
|
||
|
||
{/* Middle Section */}
|
||
<div className="col-span-2 space-y-6 h-full">
|
||
{/* Chart Section */}
|
||
<SkeletonChart />
|
||
</div>
|
||
|
||
{/* Right Sidebar */}
|
||
<div className="space-y-2">
|
||
<SkeletonCard />
|
||
<SkeletonCard />
|
||
<SkeletonCard />
|
||
<SkeletonCard />
|
||
</div>
|
||
</div>
|
||
</DashboardLayout>
|
||
);
|
||
}
|
||
|
||
if (error || !dashboardData) {
|
||
return (
|
||
<DashboardLayout>
|
||
<div className="">
|
||
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-red-500/50">
|
||
<CardContent className="">
|
||
<div className="flex flex-col items-center justify-center space-y-4">
|
||
<div className="p-4 bg-red-500/20 rounded-full">
|
||
<CheckCircle className="w-12 h-12 text-red-400" />
|
||
</div>
|
||
<h3 className="text-xl font-bold text-red-400 text-center">
|
||
خطا در بارگذاری دادهها
|
||
</h3>
|
||
<p className="text-gray-300 text-center max-w-md">
|
||
{error ||
|
||
"خطای نامشخص در بارگذاری دادههای داشبورد رخ داده است"}
|
||
</p>
|
||
<Button
|
||
onClick={fetchDashboardData}
|
||
variant="outline"
|
||
className="border-red-500/50 text-red-400 hover:bg-red-500/10"
|
||
>
|
||
تلاش مجدد
|
||
</Button>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</DashboardLayout>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<DashboardLayout>
|
||
<div className="grid gird-cols-3 p-3 pb-0 gap-4">
|
||
{/* Top Cards Row - Redesigned to match other components */}
|
||
<div className="flex justify-between gap-6 [&>*]:w-full col-span-3">
|
||
{/* Ideas Card */}
|
||
<BaseCard title="ایدههای فناوری و نوآوری">
|
||
<div className="flex items-center gap-2 justify-center flex-row-reverse">
|
||
<ChartContainer
|
||
config={chartConfig}
|
||
className="aspect-square w-[6rem] h-auto"
|
||
>
|
||
<RadialBarChart
|
||
data={[
|
||
{
|
||
browser: "ideas",
|
||
visitors:
|
||
parseFloat(
|
||
dashboardData.topData
|
||
?.registered_innovation_technology_idea || "0",
|
||
) > 0
|
||
? Math.round(
|
||
(parseFloat(
|
||
dashboardData.topData
|
||
?.ongoing_innovation_technology_ideas ||
|
||
"0",
|
||
) /
|
||
parseFloat(
|
||
dashboardData.topData
|
||
?.registered_innovation_technology_idea ||
|
||
"1",
|
||
)) *
|
||
100,
|
||
)
|
||
: 0,
|
||
fill: "green",
|
||
},
|
||
]}
|
||
startAngle={90}
|
||
endAngle={
|
||
90 +
|
||
((parseFloat(
|
||
dashboardData.topData
|
||
?.registered_innovation_technology_idea || "0",
|
||
) > 0
|
||
? Math.round(
|
||
(parseFloat(
|
||
dashboardData.topData
|
||
?.ongoing_innovation_technology_ideas || "0",
|
||
) /
|
||
parseFloat(
|
||
dashboardData.topData
|
||
?.registered_innovation_technology_idea ||
|
||
"1",
|
||
)) *
|
||
100,
|
||
)
|
||
: 0) /
|
||
100) *
|
||
360
|
||
}
|
||
innerRadius={35}
|
||
outerRadius={55}
|
||
>
|
||
<PolarGrid
|
||
gridType="circle"
|
||
radialLines={false}
|
||
stroke="none"
|
||
className="first:fill-red-400 last:fill-background"
|
||
polarRadius={[38, 31]}
|
||
/>
|
||
<RadialBar
|
||
dataKey="visitors"
|
||
background
|
||
cornerRadius={5}
|
||
/>
|
||
<PolarRadiusAxis
|
||
tick={false}
|
||
tickLine={false}
|
||
axisLine={false}
|
||
>
|
||
<Label
|
||
content={({ viewBox }) => {
|
||
if (viewBox && "cx" in viewBox && "cy" in viewBox) {
|
||
return (
|
||
<text
|
||
x={viewBox.cx}
|
||
y={viewBox.cy}
|
||
textAnchor="middle"
|
||
dominantBaseline="middle"
|
||
>
|
||
<tspan
|
||
x={viewBox.cx}
|
||
y={viewBox.cy}
|
||
className="fill-foreground text-lg font-bold"
|
||
>
|
||
%
|
||
{formatNumber(
|
||
parseFloat(
|
||
dashboardData.topData
|
||
?.registered_innovation_technology_idea ||
|
||
"0",
|
||
) > 0
|
||
? Math.round(
|
||
(parseFloat(
|
||
dashboardData.topData
|
||
?.ongoing_innovation_technology_ideas ||
|
||
"0",
|
||
) /
|
||
parseFloat(
|
||
dashboardData.topData
|
||
?.registered_innovation_technology_idea ||
|
||
"1",
|
||
)) *
|
||
100,
|
||
)
|
||
: 0,
|
||
)}
|
||
</tspan>
|
||
</text>
|
||
);
|
||
}
|
||
}}
|
||
/>
|
||
</PolarRadiusAxis>
|
||
</RadialBarChart>
|
||
</ChartContainer>
|
||
<div className="font-bold font-persian text-center">
|
||
<div className="flex flex-col justify-between items-center gap-2">
|
||
<span className="flex font-bold items-center gap-1 text-base">
|
||
<div className="font-light text-sm">ثبت شده :</div>
|
||
{formatNumber(
|
||
dashboardData.topData
|
||
?.registered_innovation_technology_idea || "0",
|
||
)}
|
||
</span>
|
||
<span className="flex items-center gap-1 font-bold text-base">
|
||
<div className="font-light text-sm">در حال اجرا :</div>
|
||
{formatNumber(
|
||
dashboardData.topData
|
||
?.ongoing_innovation_technology_ideas || "0",
|
||
)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</BaseCard>
|
||
{/* Revenue Card */}
|
||
<MetricCard
|
||
title="افزایش درآمد مبتنی بر فناوری و نوآوری"
|
||
value={dashboardData.topData?.technology_innovation_based_revenue_growth || "0"}
|
||
percentValue={Math.round(dashboardData.topData?.technology_innovation_based_revenue_growth_percent) || "0"}
|
||
percentLabel="درصد به کل درآمد"
|
||
/>
|
||
|
||
{/* Cost Reduction Card */}
|
||
<MetricCard
|
||
title="کاهش هزینه ها مبتنی بر فناوری و نوآوری"
|
||
value={Math.round(parseFloat(dashboardData.topData?.technology_innovation_based_cost_reduction?.replace(/,/g, "") || "0") / 1000000)}
|
||
percentValue={Math.round(dashboardData.topData?.technology_innovation_based_cost_reduction_percent) || "0"}
|
||
percentLabel="درصد به کل هزینه"
|
||
/>
|
||
|
||
{/* Budget Ratio Card */}
|
||
<BaseCard title="نسبت تحقق بودجه فناوی و نوآوری">
|
||
<div className="flex items-center gap-2 justify-center flex-row-reverse">
|
||
<ChartContainer
|
||
config={chartConfig}
|
||
className="aspect-square w-[6rem] h-auto"
|
||
>
|
||
<RadialBarChart
|
||
data={[
|
||
{
|
||
browser: "budget",
|
||
visitors: parseFloat(
|
||
dashboardData.topData
|
||
?.innovation_budget_achievement_percent || "0",
|
||
),
|
||
fill: "green",
|
||
},
|
||
]}
|
||
startAngle={90}
|
||
endAngle={
|
||
90 +
|
||
(dashboardData.topData
|
||
?.innovation_budget_achievement_percent /
|
||
100) *
|
||
360
|
||
}
|
||
innerRadius={35}
|
||
outerRadius={55}
|
||
>
|
||
<PolarGrid
|
||
gridType="circle"
|
||
radialLines={false}
|
||
stroke="none"
|
||
className="first:fill-red-400 last:fill-background"
|
||
polarRadius={[38, 31]}
|
||
/>
|
||
<RadialBar
|
||
dataKey="visitors"
|
||
background
|
||
cornerRadius={5}
|
||
/>
|
||
<PolarRadiusAxis
|
||
tick={false}
|
||
tickLine={false}
|
||
axisLine={false}
|
||
>
|
||
<Label
|
||
content={({ viewBox }) => {
|
||
if (viewBox && "cx" in viewBox && "cy" in viewBox) {
|
||
return (
|
||
<text
|
||
x={viewBox.cx}
|
||
y={viewBox.cy}
|
||
textAnchor="middle"
|
||
dominantBaseline="middle"
|
||
>
|
||
<tspan
|
||
x={viewBox.cx}
|
||
y={viewBox.cy}
|
||
className="fill-foreground text-lg font-bold"
|
||
>
|
||
%
|
||
{formatNumber(
|
||
Math.round(
|
||
dashboardData.topData
|
||
?.innovation_budget_achievement_percent ||
|
||
0,
|
||
),
|
||
)}
|
||
</tspan>
|
||
</text>
|
||
);
|
||
}
|
||
}}
|
||
/>
|
||
</PolarRadiusAxis>
|
||
</RadialBarChart>
|
||
</ChartContainer>
|
||
<div className="font-bold font-persian text-center">
|
||
<div className="flex flex-col justify-between items-center gap-2">
|
||
<span className="flex font-bold items-center text-base gap-1 mr-auto">
|
||
<div className="font-light text-sm">مصوب :</div>
|
||
{formatNumber(
|
||
Math.round(
|
||
parseFloat(
|
||
dashboardData.topData?.approved_innovation_budget_achievement_ratio?.replace(
|
||
/,/g,
|
||
"",
|
||
) || "0",
|
||
) / 1000000000,
|
||
),
|
||
)}
|
||
</span>
|
||
<span className="flex items-center gap-1 text-base font-bold mr-auto">
|
||
<div className="font-light text-sm">جذب شده :</div>
|
||
{formatNumber(
|
||
Math.round(
|
||
parseFloat(
|
||
dashboardData.topData?.allocated_innovation_budget_achievement_ratio?.replace(
|
||
/,/g,
|
||
"",
|
||
) || "0",
|
||
) / 1000000000,
|
||
),
|
||
)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</BaseCard>
|
||
</div>
|
||
|
||
{/* Main Content with Tabs */}
|
||
<Tabs
|
||
defaultValue="charts"
|
||
className="grid overflow-hidden rounded-lg grid-rows-[max-content] items-center col-span-2 row-start-2 bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)]"
|
||
>
|
||
<div className="flex items-center border-b border-gray-600 justify-between gap-2">
|
||
<p className="p-6 font-persian font-semibold text-lg ">
|
||
تحقق ارزش ها
|
||
</p>
|
||
<TabsList className="bg-transparent py-2 border m-6 border-gray-600">
|
||
<TabsTrigger value="canvas" className="">
|
||
شماتیک
|
||
</TabsTrigger>
|
||
<TabsTrigger value="charts" className=" text-white font-light ">
|
||
مقایسه ای
|
||
</TabsTrigger>
|
||
</TabsList>
|
||
</div>
|
||
|
||
<TabsContent value="charts" className="w-ful h-full">
|
||
<InteractiveBarChart data={companyChartData} />
|
||
</TabsContent>
|
||
|
||
<TabsContent value="canvas" className="w-ful h-full">
|
||
<div className="p-4 h-full">
|
||
<D3ImageInfo
|
||
companies={
|
||
companyChartData.map((item) => {
|
||
const imageMap: Record<string, string> = {
|
||
"بسپاران": "/besparan.png",
|
||
"خوارزمی": "/khwarazmi.png",
|
||
"فراورش 1": "/faravash1.png",
|
||
"فراورش 2": "/faravash2.png",
|
||
"کیمیا": "/kimia.png",
|
||
"آب نیرو": "/abniro.png",
|
||
};
|
||
|
||
return {
|
||
id: item.category,
|
||
name: item.category,
|
||
imageUrl: imageMap[item.category] || "/placeholder.png",
|
||
cost: item?.costI || 0,
|
||
capacity: item?.capacityI || 0,
|
||
revenue: item?.revenueI || 0,
|
||
};
|
||
})
|
||
}
|
||
/>
|
||
</div>
|
||
</TabsContent>
|
||
</Tabs>
|
||
|
||
{/* Left Section - Status Cards */}
|
||
<div className="space-y-4 row-start-2 col-span-1">
|
||
{/* Technology Intensity */}
|
||
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-gray-700/50">
|
||
<CardContent className="p-4">
|
||
<div className="flex items-center justify-center gap-1 px-4">
|
||
<CardTitle className="text-white text-sm min-w-[100px]">
|
||
شدت فناوری
|
||
</CardTitle>
|
||
<p className="text-base text-left">
|
||
%
|
||
{formatNumber(
|
||
Math.round(
|
||
dashboardData.leftData?.technology_intensity || 0,
|
||
),
|
||
)}
|
||
</p>
|
||
<Progress
|
||
value={parseFloat(
|
||
dashboardData.leftData?.technology_intensity || "0",
|
||
)}
|
||
className="h-4 flex-1"
|
||
/>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Program Status */}
|
||
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm">
|
||
<CardContent className="py-4 px-0">
|
||
<DashboardCustomBarChart
|
||
title="وضعیت برنامههای فناوری و نوآوری"
|
||
loading={loading}
|
||
data={[
|
||
{
|
||
label: "اجرا شده",
|
||
value: parseFloat(
|
||
dashboardData?.leftData?.executed_project || "0",
|
||
),
|
||
color: "bg-pr-green",
|
||
},
|
||
{
|
||
label: "در حال اجرا",
|
||
value: parseFloat(
|
||
dashboardData?.leftData?.in_progress_project || "0",
|
||
),
|
||
color: "bg-pr-blue",
|
||
},
|
||
{
|
||
label: "برنامهریزی شده",
|
||
value: parseFloat(
|
||
dashboardData?.leftData?.planned_project || "0",
|
||
),
|
||
color: "bg-pr-red",
|
||
},
|
||
]}
|
||
/>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Publications */}
|
||
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm">
|
||
<CardHeader className="pb-2 border-b-2 border-gray-500/20">
|
||
<CardTitle className="text-white text-sm">
|
||
انتشارات فناوری و نوآوری
|
||
</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="p-4">
|
||
<div className="grid grid-cols-2 grid-rows-2 gap-4 justify-center">
|
||
<div className="flex items-center justify-center gap-4">
|
||
<div className="flex items-center gap-2">
|
||
<Book className="w-4 h-4 text-blue-400" />
|
||
<span className="text-base">کتاب:</span>
|
||
</div>
|
||
<span className="text-base font-bold ">
|
||
{formatNumber(
|
||
dashboardData.leftData?.printed_books_count || "0",
|
||
)}
|
||
</span>
|
||
</div>
|
||
<div className="flex items-center justify-center gap-4">
|
||
<div className="flex items-center gap-2">
|
||
<Book className="w-4 h-4 text-purple-400" />
|
||
<span className="text-sm">پتنت:</span>
|
||
</div>
|
||
<span className="text-base font-bold ">
|
||
{formatNumber(
|
||
dashboardData.leftData?.registered_patents_count || "0",
|
||
)}
|
||
</span>
|
||
</div>
|
||
<div className="flex items-center justify-center gap-4">
|
||
<div className="flex items-center gap-2">
|
||
<Book className="w-4 h-4 text-yellow-400" />
|
||
<span className="text-sm">گزارش:</span>
|
||
</div>
|
||
<span className="text-base font-bold ">
|
||
{formatNumber(
|
||
dashboardData.leftData?.published_reports_count || "0",
|
||
)}
|
||
</span>
|
||
</div>
|
||
<div className="flex items-center justify-center gap-4">
|
||
<div className="flex items-center gap-2">
|
||
<Book className="w-4 h-4 text-green-400" />
|
||
<span className="text-sm">مقاله:</span>
|
||
</div>
|
||
<span className="text-base font-bold ">
|
||
{formatNumber(
|
||
dashboardData.leftData?.printed_articles_count || "0",
|
||
)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Promotion */}
|
||
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm">
|
||
<CardHeader className="pb-2 border-b-2 border-gray-500/20">
|
||
<CardTitle className="text-white text-sm">
|
||
ترویج فناوری و نوآوری
|
||
</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="p-4">
|
||
<div className="grid grid-cols-2 grid-rows-2 gap-4 justify-center">
|
||
<div className="flex items-center justify-center gap-4">
|
||
<div className="flex items-center gap-2">
|
||
<Book className="w-4 h-4 text-purple-400" />
|
||
<span className="text-sm">کنفرانس:</span>
|
||
</div>
|
||
<span className="text-base font-bold ">
|
||
{formatNumber(
|
||
dashboardData.leftData?.attended_conferences_count || "0",
|
||
)}
|
||
</span>
|
||
</div>
|
||
<div className="flex items-center justify-center gap-4">
|
||
<div className="flex items-center gap-2">
|
||
<Book className="w-4 h-4 text-blue-400" />
|
||
<span className="text-sm">شرکت در رویداد:</span>
|
||
</div>
|
||
<span className="text-base font-bold ">
|
||
{formatNumber(
|
||
dashboardData.leftData?.attended_events_count || "0",
|
||
)}
|
||
</span>
|
||
</div>
|
||
<div className="flex items-center justify-center gap-4">
|
||
<div className="flex items-center gap-2">
|
||
<Book className="w-4 h-4 text-yellow-400" />
|
||
<span className="text-sm">نمایشگاه:</span>
|
||
</div>
|
||
<span className="text-base font-bold ">
|
||
{formatNumber(
|
||
dashboardData.leftData?.attended_exhibitions_count || "0",
|
||
)}
|
||
</span>
|
||
</div>
|
||
<div className="flex items-center justify-center gap-4">
|
||
<div className="flex items-center gap-2">
|
||
<Book className="w-4 h-4 text-green-400" />
|
||
<span className="text-sm">برگزاری رویداد:</span>
|
||
</div>
|
||
<span className="text-base font-bold ">
|
||
{formatNumber(
|
||
dashboardData.leftData?.organized_events_count || "0",
|
||
)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
</DashboardLayout>
|
||
|
||
);
|
||
}
|
||
|
||
export default DashboardHome;
|