import React, { useState, useEffect } from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; import { PROVIDERS_MAP } from '../../utils/providers'; import { fetchAllModelData, generateMonthlyData, getTotalMonthlyData, processDetailedModelData, MonthlyActivity, DetailedModelData, convertToCSV } from '../../utils/modelData'; import Link from 'next/link'; interface TrendProps { monthlyData: MonthlyActivity[]; totalData: MonthlyActivity[]; detailedData: DetailedModelData[]; error?: string; } const COLORS = Object.fromEntries( Object.entries(PROVIDERS_MAP).map(([provider, { color }]) => [provider, color]) ); COLORS['Total'] = '#4CAF50'; const CustomTooltip = ({ active, payload, label }: any) => { if (active && payload && payload.length) { return (

{label}

{payload.map((entry: any) => (

{entry.name}: {entry.value} models

))}
); } return null; }; const formatDate = (dateStr: string) => { const date = new Date(dateStr); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); }; const TrendPage: React.FC = ({ monthlyData = [], totalData = [], detailedData = [], error }) => { const [showTotal, setShowTotal] = useState(true); const [selectedProviders, setSelectedProviders] = useState([]); const [minLikes, setMinLikes] = useState(100); const [isLoading, setIsLoading] = useState(false); const [contentType, setContentType] = useState<'all' | 'models' | 'datasets'>('all'); const toggleProvider = (provider: string) => { setSelectedProviders(prev => prev.includes(provider) ? prev.filter(p => p !== provider) : [...prev, provider] ); }; // Filter total data based on content type const filteredTotalData = totalData.filter(d => { if (contentType === 'all') { return d.isDataset === null; // Show only combined counts for 'all' } return (contentType === 'datasets' && d.isDataset === true) || (contentType === 'models' && d.isDataset === false); }); // Group data by provider const providerData = Object.fromEntries( Object.keys(PROVIDERS_MAP).map(provider => { const providerMonthlyData = monthlyData.filter(d => { if (contentType === 'all') { return d.provider === provider; // Show all items for the provider } const matchesContentType = (contentType === 'datasets' && d.isDataset === true) || (contentType === 'models' && d.isDataset === false); return d.provider === provider && matchesContentType; }); // If showing all, aggregate the counts for each month if (contentType === 'all') { const aggregatedData: Record = {}; providerMonthlyData.forEach(d => { if (!aggregatedData[d.date]) { aggregatedData[d.date] = { date: d.date, count: 0, provider: d.provider, isDataset: null }; } aggregatedData[d.date].count += d.count; }); return [provider, Object.values(aggregatedData).sort((a, b) => a.date.localeCompare(b.date))]; } return [provider, providerMonthlyData || []]; }) ); // Filter and group detailed model data const filteredModels = (detailedData || []) .filter(model => { const matchesContentType = contentType === 'all' || (contentType === 'datasets' && model.isDataset) || (contentType === 'models' && !model.isDataset); return model.likes >= minLikes && (selectedProviders.length === 0 || selectedProviders.includes(model.provider)) && matchesContentType; }) .reduce>((acc, model) => { if (!acc[model.monthKey]) { acc[model.monthKey] = []; } acc[model.monthKey].push(model); return acc; }, {}); useEffect(() => { console.log('Content type changed:', contentType); console.log('Selected providers:', selectedProviders); console.log('Min likes:', minLikes); }, [contentType, selectedProviders, minLikes]); if (error) { return (

Chinese AI Model Release Trends 📈

Error loading data: {error}

Please try again later.

); } return (

Chinese AI Open Source Trends 🤗

Track the growth of Chinese AI models and datasets over time

View Heatmap

Track the growth of Chinese AI models and datasets over time

{Object.entries(PROVIDERS_MAP).map(([provider, { color }]) => ( ))}
{/* Chart */}
} /> {/* Total Line */} {showTotal && ( )} {/* Provider Lines */} {selectedProviders.map(provider => ( ))}
{/* Download Button */}
{/* Major Releases Section */}

Major Releases ({Object.values(filteredModels).flat().length})

setMinLikes(Math.max(0, parseInt(e.target.value) || 0))} className="w-20 px-2 py-1 border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white focus:outline-none focus:ring-2 focus:ring-green-500" />
{Object.entries(filteredModels) .map(([monthKey, models]) => ({ monthKey, models, sortKey: models[0]?.sortKey || '' // Use the first model's sortKey })) .sort((a, b) => b.sortKey.localeCompare(a.sortKey)) // Sort by sortKey in descending order .map(({ monthKey, models }) => (

{monthKey}

{models .sort((a, b) => b.likes - a.likes) // Sort models within each month by likes .map(model => (
{model.name}

{model.provider}

❤️ {model.likes}

{formatDate(model.createdAt)}

))}
))}
); }; export const getStaticProps = async () => { try { const modelData = await fetchAllModelData(); const monthlyData = generateMonthlyData(modelData); const totalData = getTotalMonthlyData(monthlyData); const filteredModels = processDetailedModelData(modelData); return { props: { monthlyData, totalData, filteredModels, }, // Revalidate every 12 hours revalidate: 43200, }; } catch (error) { console.error('Error in getStaticProps:', error); return { props: { monthlyData: [], totalData: [], filteredModels: {}, }, // Retry more frequently if there was an error revalidate: 3600, }; } }; export default TrendPage;