"use client"; import { useEffect, useState } from "react"; import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts"; import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; import { CustomBarChart } from "~/components/ui/custom-bar-chart"; import { useStoredDate } from "~/hooks/useStoredDate"; import apiService from "~/lib/api"; import { EventBus, formatNumber } from "~/lib/utils"; import type { CalendarDate } from "~/types/util.type"; export interface CompanyDetails { id: string; label: string; category: string; stageid: number; fields: { F: string; N: string; V: string; T: number; U: string; S: boolean; }[]; description?: string; } export interface InfoPanelProps { selectedCompany: CompanyDetails | null; } interface EcosystemCounts { knowledge_based_count: string; consultant_count: string; startup_count: string; innovation_center_count: string; accelerator_count: string; university_count: string; fund_count: string; company_count: string; actor_count: string; mou_count: string; } interface ProcessActorsResponse { start_year: string; total_count: number; } interface ProcessActorsData { year: string; value: number; } export function InfoPanel({ selectedCompany }: InfoPanelProps) { const [counts, setCounts] = useState(null); const [processData, setProcessData] = useState([]); const [isLoading, setIsLoading] = useState(true); // const [date, setDate] = useState(); const [date, setDate] = useStoredDate(); useEffect(() => { const handler = (date: CalendarDate) => { if (date) setDate(date); }; EventBus.on("dateSelected", handler); return () => { EventBus.off("dateSelected", handler); }; }, []); useEffect(() => { if (date.end && date.start) fetchCounts(); }, [date]); const fetchCounts = async () => { setIsLoading(true); try { const [countsRes, processRes] = await Promise.all([ apiService.call({ ecosystem_count_function: { start_date: date?.start || null, end_date: date?.end || null, }, }), apiService.call({ process_creating_actors_function: { start_date: date?.start || null, end_date: date?.end || null, }, }), ]); setCounts( JSON.parse(JSON.parse(countsRes.data).ecosystem_count_function)[0] ); // Process the years data and fill missing years const processedData = processYearsData( JSON.parse(JSON.parse(processRes?.data)?.process_creating_actors) ); setProcessData(processedData); } catch (err) { console.error("Failed to fetch data:", err); } finally { setIsLoading(false); } }; // Helper function to safely parse numbers const parseNumber = (value: string | undefined): number => { if (!value || value === "") return 0; const parsed = parseInt(value, 10); return isNaN(parsed) ? 0 : parsed; }; // Helper function to process years data and fill missing years const processYearsData = ( data: ProcessActorsResponse[] ): ProcessActorsData[] => { if (!data || data.length === 0) return []; const years = data .map((item) => parseInt(item.start_year)) .sort((a, b) => a - b); const minYear = years[0]; const maxYear = years[years.length - 1]; const result: ProcessActorsData[] = []; // Create a map for quick lookup const dataMap = data.reduce( (acc, item) => { acc[item.start_year] = item.total_count; return acc; }, {} as Record ); for (let year = minYear; year <= maxYear; year++) { result.push({ year: year.toString(), value: dataMap[year.toString()] || 0, }); } return result; }; // Convert Persian years to display format without commas const formatPersianYear = (year: string): string => { const map: Record = { "0": "۰", "1": "۱", "2": "۲", "3": "۳", "4": "۴", "5": "۵", "6": "۶", "7": "۷", "8": "۸", "9": "۹", }; return year.replace(/[0-9]/g, (d) => map[d] ?? d); }; // Transform counts into chart-friendly data const barData = counts ? [ { label: "دانش بنیان", value: parseNumber(counts.knowledge_based_count), }, { label: "مشاور", value: parseNumber(counts.consultant_count) }, { label: "استارتاپ", value: parseNumber(counts.startup_count) }, { label: "مرکز نوآوری", value: parseNumber(counts.innovation_center_count), }, { label: "شتابدهنده", value: parseNumber(counts.accelerator_count) }, { label: "دانشگاه", value: parseNumber(counts.university_count) }, { label: "صندوق های مالی", value: parseNumber(counts.fund_count) }, { label: "شرکت", value: parseNumber(counts.company_count) }, ] : []; if (isLoading) { return ( {/* Header Skeleton */}
{/* Actor Count Skeleton */}
{/* Bar Chart Skeleton */}
{Array.from({ length: 8 }).map((_, i) => (
{/* Label skeleton */}
{/* Bar skeleton */}
{/* Value skeleton */}
))}
{/* Area Chart Skeleton */}
{/* Chart skeleton */}
{/* Y-axis skeleton */}
{/* X-axis skeleton */}
{/* Area chart skeleton */} {/* Data points skeleton */} {Array.from({ length: 4 }).map((_, i) => (
))}
{/* Footer Skeleton */}
); } if (isLoading) { return ( {/* Header Skeleton */}
{/* Actor Count Skeleton */}
{/* Bar Chart Skeleton */}
{Array.from({ length: 8 }).map((_, i) => (
{/* Label skeleton */}
{/* Bar skeleton */}
{/* Value skeleton */}
))}
{/* Area Chart Skeleton */}
{/* Chart skeleton */}
{/* Y-axis skeleton */}
{/* X-axis skeleton */}
{/* Area chart skeleton */} {/* Data points skeleton */} {Array.from({ length: 4 }).map((_, i) => (
))}
{/* Footer Skeleton */}
); } if (!counts) { return (
خطا در بارگذاری داده‌ها.
); } return (
وضعیت زیست‌بوم فناوری و نوآوری تعداد تفاهم نامه ها {formatNumber(counts.mou_count)} تعداد بازیگران {formatNumber(counts.actor_count)} {/* Actor Count Display */} تنوع بازیگران {/* Middle - Bar Chart */}
({ label: item.label, value: item.value, valueSuffix: "", valuePrefix: "", maxValue: Math.max(...barData.map((d) => d.value)), }))} barHeight="h-5" showAxisLabels={false} />
{/* Area Chart Section */}
روند ایجاد بازیگران در طول سال‌ها
{processData.length > 0 ? ( formatNumber(value)} /> } /> {/* ✅ Use gradient for fill */} ( {/* Small circle */} {/* Year label above point */} {formatPersianYear(payload.year)} )} /> ) : (
داده‌ای برای نمایش وجود ندارد
)}
); } export default InfoPanel;