191 lines
No EOL
7.1 KiB
TypeScript
191 lines
No EOL
7.1 KiB
TypeScript
import React from 'react';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { FileText, Rss, Users, Route, TrendingUp, Clock, CheckCircle } from 'lucide-react';
|
|
import { apiClient } from '../lib/api';
|
|
|
|
const Dashboard: React.FC = () => {
|
|
const { data: stats, isLoading, error } = useQuery({
|
|
queryKey: ['dashboard-stats'],
|
|
queryFn: () => apiClient.getDashboardStats(),
|
|
});
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="p-6">
|
|
<div className="animate-pulse">
|
|
<div className="h-8 bg-gray-200 rounded w-1/4 mb-6"></div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
|
{[...Array(4)].map((_, i) => (
|
|
<div key={i} className="bg-white p-6 rounded-lg shadow">
|
|
<div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
|
|
<div className="h-8 bg-gray-200 rounded w-1/2"></div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="p-6">
|
|
<div className="bg-red-50 border border-red-200 rounded-md p-4">
|
|
<p className="text-red-600">Failed to load dashboard data</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const articleStats = stats?.article_stats;
|
|
const systemStats = stats?.system_stats;
|
|
|
|
return (
|
|
<div className="p-6">
|
|
<div className="mb-8">
|
|
<h1 className="text-2xl font-bold text-gray-900">Dashboard</h1>
|
|
<p className="mt-1 text-sm text-gray-500">
|
|
Overview of your feed management system
|
|
</p>
|
|
</div>
|
|
|
|
{/* Article Statistics */}
|
|
<div className="mb-8">
|
|
<h2 className="text-lg font-semibold text-gray-900 mb-4">Article Statistics</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
<div className="bg-white p-6 rounded-lg shadow">
|
|
<div className="flex items-center">
|
|
<div className="flex-shrink-0">
|
|
<FileText className="h-8 w-8 text-blue-500" />
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-500">Articles Today</p>
|
|
<p className="text-2xl font-semibold text-gray-900">
|
|
{articleStats?.total_today || 0}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white p-6 rounded-lg shadow">
|
|
<div className="flex items-center">
|
|
<div className="flex-shrink-0">
|
|
<Clock className="h-8 w-8 text-yellow-500" />
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-500">Articles This Week</p>
|
|
<p className="text-2xl font-semibold text-gray-900">
|
|
{articleStats?.total_week || 0}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white p-6 rounded-lg shadow">
|
|
<div className="flex items-center">
|
|
<div className="flex-shrink-0">
|
|
<CheckCircle className="h-8 w-8 text-green-500" />
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-500">Approved Today</p>
|
|
<p className="text-2xl font-semibold text-gray-900">
|
|
{articleStats?.approved_today || 0}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white p-6 rounded-lg shadow">
|
|
<div className="flex items-center">
|
|
<div className="flex-shrink-0">
|
|
<TrendingUp className="h-8 w-8 text-purple-500" />
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-500">Approval Rate</p>
|
|
<p className="text-2xl font-semibold text-gray-900">
|
|
{articleStats?.approval_percentage_today?.toFixed(1) || 0}%
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* System Statistics */}
|
|
<div>
|
|
<h2 className="text-lg font-semibold text-gray-900 mb-4">System Overview</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
<div className="bg-white p-6 rounded-lg shadow">
|
|
<div className="flex items-center">
|
|
<div className="flex-shrink-0">
|
|
<Rss className="h-8 w-8 text-orange-500" />
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-500">Active Feeds</p>
|
|
<p className="text-2xl font-semibold text-gray-900">
|
|
{systemStats?.active_feeds || 0}
|
|
<span className="text-sm font-normal text-gray-500">
|
|
/{systemStats?.total_feeds || 0}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white p-6 rounded-lg shadow">
|
|
<div className="flex items-center">
|
|
<div className="flex-shrink-0">
|
|
<Users className="h-8 w-8 text-indigo-500" />
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-500">Platform Accounts</p>
|
|
<p className="text-2xl font-semibold text-gray-900">
|
|
{systemStats?.active_platform_accounts || 0}
|
|
<span className="text-sm font-normal text-gray-500">
|
|
/{systemStats?.total_platform_accounts || 0}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white p-6 rounded-lg shadow">
|
|
<div className="flex items-center">
|
|
<div className="flex-shrink-0">
|
|
<FileText className="h-8 w-8 text-cyan-500" />
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-500">Platform Channels</p>
|
|
<p className="text-2xl font-semibold text-gray-900">
|
|
{systemStats?.active_platform_channels || 0}
|
|
<span className="text-sm font-normal text-gray-500">
|
|
/{systemStats?.total_platform_channels || 0}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white p-6 rounded-lg shadow">
|
|
<div className="flex items-center">
|
|
<div className="flex-shrink-0">
|
|
<Route className="h-8 w-8 text-pink-500" />
|
|
</div>
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-gray-500">Active Routes</p>
|
|
<p className="text-2xl font-semibold text-gray-900">
|
|
{systemStats?.active_routes || 0}
|
|
<span className="text-sm font-normal text-gray-500">
|
|
/{systemStats?.total_routes || 0}
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Dashboard; |