← Назад
import { Send, Users, FileText, Activity, FolderOpen, Clock, BarChart2, ExternalLink, Map } from 'lucide-react'; import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '../ui/card'; import { Button } from '../ui/button'; import { Badge } from '../ui/badge'; import { useProject } from '../../hooks/useProjects'; interface AlphaPulseCardProps { onViewTasks: () => void; onViewFiles?: () => void; } function fmtLastPost(raw: string | null): string { if (!raw) return '—'; const d = new Date(raw + 'Z'); if (isNaN(d.getTime())) return raw; const now = Date.now(); const mins = Math.floor((now - d.getTime()) / 60000); if (mins < 60) return `${mins}м назад`; if (mins < 1440) return `${Math.floor(mins / 60)}ч назад`; return `${Math.floor(mins / 1440)}д назад`; } export function AlphaPulseCard({ onViewTasks, onViewFiles }: AlphaPulseCardProps) { const { data, isLoading, refetch } = useProject('alphapulse'); if (isLoading) { return ( <Card className="animate-pulse"> <div className="h-64 flex items-center justify-center"> <div className="text-gray-400">Loading...</div> </div> </Card> ); } const { kpis, status } = data || {}; const botStatus = kpis?.bot?.status || 'unknown'; const postsToday = kpis?.posts?.today ?? '--'; const postsTotal = kpis?.posts?.total ?? '--'; const subscribers = kpis?.channel?.subscribers; const lastPost = kpis?.timing?.lastPost ?? null; const lastType = kpis?.timing?.lastType ?? null; const nextPost = kpis?.timing?.nextPost ?? '--'; const isRunning = botStatus === 'running'; return ( <Card className={`border-l-4 ${isRunning ? 'border-l-alphapulse' : 'border-l-danger'} transition-all duration-300 hover:-translate-y-1 hover:shadow-lg hover:shadow-alphapulse/10`}> <CardHeader> <div className="flex items-start justify-between"> <div className="flex items-center gap-3"> <div className="p-3 rounded-xl bg-alphapulse/20"> <Send className="w-6 h-6 text-alphapulse" /> </div> <div> <CardTitle>AlphaPulse</CardTitle> <CardDescription>Telegram сигнальный канал</CardDescription> </div> </div> <Badge variant={isRunning ? 'success' : 'danger'}> {isRunning ? 'RUNNING' : (status?.toUpperCase() || 'UNKNOWN')} </Badge> </div> </CardHeader> <CardContent className="space-y-3"> {/* Row 1 */} <div className="grid grid-cols-3 gap-3"> <div className="bg-white/5 rounded-xl p-3 border border-white/5"> <div className="flex items-center gap-1.5 text-xs text-gray-400 uppercase mb-2"> <Activity className="w-3.5 h-3.5" /> Бот </div> <div className={`text-base font-bold ${isRunning ? 'text-alphapulse' : 'text-danger'}`}> {isRunning ? '● Online' : '● Offline'} </div> </div> <div className="bg-white/5 rounded-xl p-3 border border-white/5"> <div className="flex items-center gap-1.5 text-xs text-gray-400 uppercase mb-2"> <FileText className="w-3.5 h-3.5" /> Сегодня </div> <div className="text-2xl font-bold text-white">{postsToday}</div> <div className="text-xs text-gray-500 mt-0.5">постов</div> </div> <div className="bg-white/5 rounded-xl p-3 border border-white/5"> <div className="flex items-center gap-1.5 text-xs text-gray-400 uppercase mb-2"> <BarChart2 className="w-3.5 h-3.5" /> Всего </div> <div className="text-2xl font-bold text-white">{postsTotal}</div> <div className="text-xs text-gray-500 mt-0.5">в базе</div> </div> </div> {/* Row 2 */} <div className="grid grid-cols-3 gap-3"> <div className="bg-white/5 rounded-xl p-3 border border-white/5"> <div className="flex items-center gap-1.5 text-xs text-gray-400 uppercase mb-2"> <Clock className="w-3.5 h-3.5" /> Последний </div> <div className="text-sm font-semibold text-white">{fmtLastPost(lastPost)}</div> {lastType && ( <div className="text-xs text-gray-500 mt-0.5 capitalize">{lastType}</div> )} </div> <div className="bg-white/5 rounded-xl p-3 border border-white/5"> <div className="flex items-center gap-1.5 text-xs text-gray-400 uppercase mb-2"> <Clock className="w-3.5 h-3.5" /> Следующий </div> <div className="text-sm font-semibold text-alphapulse">{nextPost}</div> </div> <div className="bg-white/5 rounded-xl p-3 border border-white/5"> <div className="flex items-center gap-1.5 text-xs text-gray-400 uppercase mb-2"> <Users className="w-3.5 h-3.5" /> Подписч. </div> <div className="text-2xl font-bold text-white"> {subscribers != null ? typeof subscribers === 'number' ? subscribers.toLocaleString('ru') : subscribers : <span className="text-sm text-gray-500">n/a</span> } </div> </div> </div> </CardContent> <CardFooter className="gap-2 flex-wrap"> <Button variant="primary" size="sm" onClick={() => refetch()}> <Activity className="w-4 h-4" /> Обновить </Button> <Button variant="ghost" size="sm" onClick={onViewTasks}> <FileText className="w-4 h-4" /> Задачи </Button> {onViewFiles && ( <Button variant="ghost" size="sm" onClick={onViewFiles}> <FolderOpen className="w-4 h-4" /> Файлы </Button> )} <Button variant="ghost" size="sm" onClick={() => window.open('https://t.me/alphapulsexp', '_blank')} > <ExternalLink className="w-4 h-4" /> Канал </Button> <Button variant="ghost" size="sm" onClick={() => window.open('https://t.me/alphapulsexp', '_blank')} title="ROADMAP" > <Map className="w-4 h-4" /> </Button> </CardFooter> </Card> ); }