log-center/web/src/pages/Dashboard.tsx
zyc b178d24e73
Some checks failed
Build and Deploy Log Center / build-and-deploy (push) Failing after 5m9s
fix pr
2026-02-25 16:35:28 +08:00

152 lines
5.7 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Bug, CalendarPlus, TrendingUp, AlertTriangle } from 'lucide-react';
import { getStats, type DashboardStats } from '../api';
const SOURCE_LABELS: Record<string, string> = {
runtime: '运行时',
cicd: 'CI/CD',
deployment: '部署',
};
const STATUS_LABELS: Record<string, string> = {
NEW: '新发现',
VERIFYING: '验证中',
CANNOT_REPRODUCE: '无法复现',
PENDING_FIX: '等待审核',
FIXING: '修复中',
FIXED: '已修复',
VERIFIED: '已验证',
DEPLOYED: '已部署',
FIX_FAILED: '修复失败',
};
export default function Dashboard() {
const [stats, setStats] = useState<DashboardStats | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchStats = async () => {
try {
const response = await getStats();
setStats(response.data);
} catch (error) {
console.error('Failed to fetch stats:', error);
} finally {
setLoading(false);
}
};
fetchStats();
}, []);
if (loading) {
return (
<div className="loading">
<div className="spinner"></div>
</div>
);
}
if (!stats) {
return <div className="loading"></div>;
}
return (
<div>
<div className="page-header">
<h1 className="page-title"></h1>
<p className="page-subtitle"></p>
</div>
<div className="stats-grid">
<div className="stat-card">
<div className="stat-icon accent">
<Bug size={18} />
</div>
<div className="stat-label"></div>
<div className="stat-value accent">{stats.total_bugs}</div>
</div>
<div className="stat-card">
<div className="stat-icon warning">
<CalendarPlus size={18} />
</div>
<div className="stat-label"></div>
<div className="stat-value warning">{stats.today_bugs}</div>
</div>
<div className="stat-card">
<div className="stat-icon success">
<TrendingUp size={18} />
</div>
<div className="stat-label"></div>
<div className="stat-value success">{stats.fix_rate}%</div>
</div>
<div className="stat-card">
<div className="stat-icon error">
<AlertTriangle size={18} />
</div>
<div className="stat-label"></div>
<div className="stat-value error">
{(stats.status_distribution['NEW'] || 0) +
(stats.status_distribution['PENDING_FIX'] || 0)}
</div>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px' }}>
<div className="table-container table-compact">
<div className="table-header">
<h3 className="table-title"></h3>
</div>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{Object.entries(stats.status_distribution).map(([status, count]) => (
<tr key={status}>
<td>
<span className={`status-badge status-${status}`}>
{STATUS_LABELS[status] || status}
</span>
</td>
<td>{count}</td>
</tr>
))}
</tbody>
</table>
</div>
{stats.source_distribution && (
<div className="table-container table-compact">
<div className="table-header">
<h3 className="table-title"></h3>
</div>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{Object.entries(stats.source_distribution).map(([source, count]) => (
<tr key={source}>
<td>
<span className={`source-badge source-${source}`}>
{SOURCE_LABELS[source] || source}
</span>
</td>
<td>{count}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
);
}