← Назадimport { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
import { Button } from './ui/button';
import { RefreshCw, TrendingUp, Activity, CheckCircle2, AlertTriangle, XCircle } from 'lucide-react';
interface DailyReportData {
generated: string;
period: {
from: string;
to: string;
};
summary: string;
data: {
services: {
healthy: number;
warning: number;
error: number;
downtimes: Array<{
project: string;
status: string;
timestamp: string;
details: any;
}>;
};
tasks: {
new: number;
completed: number;
inProgress: number;
byProject: Record<string, {
new: number;
completed: number;
inProgress: number;
total: number;
}>;
};
system: {
cpu: {
usage: string;
cores: number;
};
memory: {
used: string;
total: string;
percent: string;
};
disk: Array<{
mount: string;
used: string;
total: string;
percent: string;
}>;
};
};
}
async function fetchDailyReport(): Promise<DailyReportData> {
const response = await fetch('/api/reports/daily');
if (!response.ok) {
throw new Error('Failed to fetch daily report');
}
return response.json();
}
export function DailyReport() {
const [isRefreshing, setIsRefreshing] = useState(false);
const { data, isLoading, error, refetch } = useQuery({
queryKey: ['dailyReport'],
queryFn: fetchDailyReport,
refetchInterval: 5 * 60 * 1000, // Auto-refresh every 5 minutes
});
const handleRefresh = async () => {
setIsRefreshing(true);
await refetch();
setTimeout(() => setIsRefreshing(false), 1000);
};
if (isLoading) {
return (
<Card className="bg-slate-800/50 border-slate-700">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<Activity className="h-5 w-5" />
AI Ежедневный отчёт
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center justify-center py-8">
<RefreshCw className="h-6 w-6 animate-spin text-blue-400" />
</div>
</CardContent>
</Card>
);
}
if (error) {
return (
<Card className="bg-slate-800/50 border-slate-700">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<Activity className="h-5 w-5" />
AI Ежедневный отчёт
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-red-400">
Ошибка загрузки отчёта: {error instanceof Error ? error.message : 'Unknown error'}
</div>
</CardContent>
</Card>
);
}
if (!data) {
return null;
}
const { services, tasks, system } = data.data;
const generatedDate = new Date(data.generated);
return (
<Card className="bg-slate-800/50 border-slate-700">
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-white flex items-center gap-2">
<Activity className="h-5 w-5" />
AI Ежедневный отчёт
</CardTitle>
<CardDescription className="text-slate-400 mt-1">
Последние 24 часа • Обновлено: {generatedDate.toLocaleTimeString('ru-RU')}
</CardDescription>
</div>
<Button
variant="ghost"
size="sm"
onClick={handleRefresh}
disabled={isRefreshing}
className="border-slate-600 hover:bg-slate-700"
>
<RefreshCw className={`h-4 w-4 ${isRefreshing ? 'animate-spin' : ''}`} />
</Button>
</div>
</CardHeader>
<CardContent className="space-y-6">
{/* Summary Cards */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{/* Services Status */}
<div className="bg-slate-900/50 rounded-lg p-4 border border-slate-700">
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-slate-400">Сервисы</span>
{services.error > 0 ? (
<XCircle className="h-5 w-5 text-red-400" />
) : services.warning > 0 ? (
<AlertTriangle className="h-5 w-5 text-yellow-400" />
) : (
<CheckCircle2 className="h-5 w-5 text-green-400" />
)}
</div>
<div className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Здоровы</span>
<span className="text-sm font-semibold text-green-400">{services.healthy}</span>
</div>
{services.warning > 0 && (
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Предупреждения</span>
<span className="text-sm font-semibold text-yellow-400">{services.warning}</span>
</div>
)}
{services.error > 0 && (
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Ошибки</span>
<span className="text-sm font-semibold text-red-400">{services.error}</span>
</div>
)}
</div>
</div>
{/* Tasks Activity */}
<div className="bg-slate-900/50 rounded-lg p-4 border border-slate-700">
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-slate-400">Задачи</span>
<Activity className="h-5 w-5 text-blue-400" />
</div>
<div className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Новых</span>
<span className="text-sm font-semibold text-blue-400 flex items-center gap-1">
{tasks.new > 0 && <TrendingUp className="h-3 w-3" />}
{tasks.new}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Завершено</span>
<span className="text-sm font-semibold text-green-400 flex items-center gap-1">
{tasks.completed > 0 && <CheckCircle2 className="h-3 w-3" />}
{tasks.completed}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">В работе</span>
<span className="text-sm font-semibold text-orange-400">{tasks.inProgress}</span>
</div>
</div>
</div>
{/* System Health */}
<div className="bg-slate-900/50 rounded-lg p-4 border border-slate-700">
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-slate-400">Система</span>
<Activity className="h-5 w-5 text-purple-400" />
</div>
<div className="space-y-1">
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">CPU</span>
<span className="text-sm font-semibold text-purple-400">{system.cpu.usage}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">RAM</span>
<span className="text-sm font-semibold text-purple-400">{system.memory.percent}</span>
</div>
{system.disk.length > 0 && (
<div className="flex items-center justify-between">
<span className="text-xs text-slate-500">Диск</span>
<span className="text-sm font-semibold text-purple-400">{system.disk[0].percent}</span>
</div>
)}
</div>
</div>
</div>
{/* Notable Events */}
{services.downtimes.length > 0 && (
<div className="bg-slate-900/50 rounded-lg p-4 border border-slate-700">
<h4 className="text-sm font-semibold text-white mb-3 flex items-center gap-2">
<AlertTriangle className="h-4 w-4 text-yellow-400" />
Проблемы за последние 24 часа
</h4>
<div className="space-y-2">
{services.downtimes.slice(0, 5).map((downtime, idx) => (
<div key={idx} className="flex items-center gap-2 text-xs">
<span className={`px-2 py-1 rounded ${
downtime.status === 'error'
? 'bg-red-900/30 text-red-400'
: 'bg-yellow-900/30 text-yellow-400'
}`}>
{downtime.project}
</span>
<span className="text-slate-500">
{new Date(downtime.timestamp).toLocaleTimeString('ru-RU')}
</span>
</div>
))}
</div>
</div>
)}
{/* Active Projects */}
{Object.keys(tasks.byProject).length > 0 && (
<div className="bg-slate-900/50 rounded-lg p-4 border border-slate-700">
<h4 className="text-sm font-semibold text-white mb-3">Активность по проектам</h4>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{Object.entries(tasks.byProject)
.filter(([_, stats]) => stats.new > 0 || stats.completed > 0)
.slice(0, 6)
.map(([project, stats]) => (
<div key={project} className="space-y-1">
<div className="text-xs font-medium text-slate-300">{project}</div>
<div className="flex items-center gap-2 text-xs text-slate-500">
{stats.new > 0 && (
<span className="text-blue-400">+{stats.new}</span>
)}
{stats.completed > 0 && (
<span className="text-green-400">✓{stats.completed}</span>
)}
</div>
</div>
))}
</div>
</div>
)}
</CardContent>
</Card>
);
}