import { useEffect, useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { Line, LineChart, CartesianGrid, XAxis, YAxis, Tooltip, ResponsiveContainer, Legend } from "recharts"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/ui/card"; import { useLiveData } from "../providers/live-provider"; import { fetchJobMetrics, JobMetrics } from "../api/jobs"; import { fetchDeploymentMetrics, DeploymentMetrics, DeploymentRunWithProject } from "../api/deployments"; import { JobStatusBadge } from "../components/JobStatusBadge"; import { RepoIcon } from "../components/RepoIcon"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faClockRotateLeft, faListCheck, faFlaskVial, faRocket } from "@fortawesome/free-solid-svg-icons"; function formatDuration(ms?: number) { if (!ms || Number.isNaN(ms)) return "-"; const seconds = Math.round(ms / 1000); if (seconds < 60) return `${seconds}s`; const minutes = Math.floor(seconds / 60); const rem = seconds % 60; if (minutes < 60) return `${minutes}dk ${rem}s`; const hours = Math.floor(minutes / 60); return `${hours}sa ${minutes % 60}dk`; } function toYmd(date: Date) { return date.toISOString().slice(0, 10); } export function HomePage() { const [metrics, setMetrics] = useState(null); const [deploymentMetrics, setDeploymentMetrics] = useState(null); const [deployRuns, setDeployRuns] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const { jobStreams } = useLiveData(); const navigate = useNavigate(); useEffect(() => { Promise.allSettled([fetchJobMetrics(), fetchDeploymentMetrics()]) .then(([jobResult, deployResult]) => { if (jobResult.status === "fulfilled") { setMetrics(jobResult.value); } else { setMetrics({ dailyStats: [], recentRuns: [], totals: { successRate: 0, totalRuns: 0 } }); setError("Job metrikleri alınamadı"); } if (deployResult.status === "fulfilled") { setDeploymentMetrics(deployResult.value); setDeployRuns(deployResult.value.recentRuns || []); } else { setDeploymentMetrics({ dailyStats: [], recentRuns: [] }); } }) .finally(() => setLoading(false)); }, []); const chartData = useMemo(() => { if (!metrics) { const days = Array.from({ length: 7 }).map((_, idx) => { const date = new Date(); date.setDate(date.getDate() - (6 - idx)); return toYmd(date); }); return days.map((date) => ({ date, "Test Başarılı": 0, "Test Hatalı": 0, "Deploy Başarılı": 0, "Deploy Hatalı": 0 })); } const deployMap = new Map((deploymentMetrics?.dailyStats || []).map((d) => [d._id, d])); const jobMap = new Map(metrics.dailyStats.map((d) => [d._id, d])); const days = Array.from({ length: 7 }).map((_, idx) => { const date = new Date(); date.setDate(date.getDate() - (6 - idx)); return toYmd(date); }); return days.map((date) => { const job = jobMap.get(date); const deploy = deployMap.get(date); return { date, "Test Başarılı": job?.success || 0, "Test Hatalı": job?.failed || 0, "Deploy Başarılı": deploy?.success || 0, "Deploy Hatalı": deploy?.failed || 0 }; }); }, [metrics, deploymentMetrics]); const mergedRuns = useMemo(() => { if (!metrics) return []; return metrics.recentRuns.map((run, idx) => { const live = jobStreams[run.job._id]; // Sadece en güncel run (liste başı) canlı güncellemeleri alır; geçmiş kayıtlar sabit kalır. if (idx === 0 && live?.status) { return { ...run, status: live.status, finishedAt: live.lastRunAt || run.finishedAt, durationMs: live.lastDurationMs ?? run.durationMs }; } return run; }); }, [metrics, jobStreams]); const activityItems = useMemo(() => { const jobItems = mergedRuns.map((run) => ({ id: run._id, type: "test" as const, title: run.job.name, repoUrl: run.job.repoUrl, status: run.status, startedAt: run.startedAt, durationMs: run.durationMs, link: `/jobs/${run.job._id}` })); const deployItems = deployRuns.map((run) => ({ id: run._id, type: "deploy" as const, title: run.project.name, repoUrl: run.project.repoUrl, status: run.status, startedAt: run.startedAt, durationMs: run.durationMs, message: run.message, link: `/deployments/${run.project._id}` })); return [...jobItems, ...deployItems] .sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime()) .slice(0, 10); }, [mergedRuns, deployRuns]); const lastRunDuration = useMemo(() => formatDuration(mergedRuns[0]?.durationMs), [mergedRuns]); return (
Son 7 Gün Çalıştırma Trendleri Test ve Deploy sonuçları
{metrics?.totals.totalRuns ?? 0} toplam koşu
{loading ? (
Yükleniyor...
) : chartData.length === 0 ? (
Grafik verisi yok.
) : ( )}
Hızlı Metrikler Özet görünüm
Başarı Oranı {metrics?.totals.successRate ?? 0}%
Toplam Çalıştırma {metrics?.totals.totalRuns ?? 0}
Son Süre {lastRunDuration}
Etkinlik Akışı Son 10 aktivite
{activityItems.length ?? 0} kayıt
{loading &&
Yükleniyor...
} {error &&
{error}
} {!loading && activityItems.length === 0 && (
Henüz çalıştırma yok.
)} {!loading && activityItems.map((run) => ( ))}
); }