log-center/web/src/pages/RepairList.tsx
zyc 7ee724f8ac
All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 1m20s
fix cn
2026-02-12 10:53:03 +08:00

164 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { getRepairReports, type RepairReport, getProjects } from '../api';
const STATUS_LABELS: Record<string, string> = {
NEW: '新发现',
VERIFYING: '验证中',
CANNOT_REPRODUCE: '无法复现',
PENDING_FIX: '待修复',
FIXING: '修复中',
FIXED: '已修复',
VERIFIED: '已验证',
DEPLOYED: '已部署',
FIX_FAILED: '修复失败',
};
export default function RepairList() {
const [reports, setReports] = useState<RepairReport[]>([]);
const [loading, setLoading] = useState(true);
const [projects, setProjects] = useState<string[]>([]);
const [filters, setFilters] = useState({
project_id: '',
page: 1,
});
const [totalPages, setTotalPages] = useState(1);
useEffect(() => {
const fetchProjects = async () => {
try {
const res = await getProjects();
setProjects(res.data.projects);
} catch (err) {
console.error(err);
}
};
fetchProjects();
}, []);
useEffect(() => {
const fetchReports = async () => {
setLoading(true);
try {
const res = await getRepairReports({
page: filters.page,
project_id: filters.project_id || undefined,
});
setReports(res.data.items);
setTotalPages(res.data.total_pages);
} catch (err) {
console.error(err);
} finally {
setLoading(false);
}
};
fetchReports();
}, [filters]);
const handleFilterChange = (key: string, value: string) => {
setFilters((prev) => ({ ...prev, [key]: value, page: 1 }));
};
return (
<div>
<div className="page-header">
<div className="title-row">
<div>
<h1 className="page-title"></h1>
<p className="page-subtitle">AI </p>
</div>
<div className="filters">
<select
className="filter-select"
value={filters.project_id}
onChange={(e) => handleFilterChange('project_id', e.target.value)}
>
<option value=""></option>
{projects.map((p) => (
<option key={p} value={p}>{p}</option>
))}
</select>
</div>
</div>
</div>
<div className="table-container">
{loading ? (
<div className="loading">
<div className="spinner"></div>
</div>
) : reports.length === 0 ? (
<div className="empty-state"></div>
) : (
<table>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{reports.map((report) => (
<tr key={report.id}>
<td>#{report.id}</td>
<td>{report.project_id}</td>
<td>
<Link to={`/bugs/${report.error_log_id}`}>
#{report.error_log_id}
</Link>
</td>
<td>{report.modified_files.length} </td>
<td>
<span className={report.test_passed ? 'test-pass' : 'test-fail'}>
{report.test_passed ? '通过' : '失败'}
</span>
</td>
<td>
<span className={`status-badge status-${report.status}`}>
{STATUS_LABELS[report.status] || report.status}
</span>
</td>
<td className="cell-secondary">
{new Date(report.created_at).toLocaleString()}
</td>
<td>
<Link to={`/repairs/${report.id}`} className="btn-link">
</Link>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
{totalPages > 1 && (
<div className="pagination">
<button
disabled={filters.page === 1}
onClick={() => setFilters((prev) => ({ ...prev, page: prev.page - 1 }))}
>
</button>
<span className="pagination-info">
{filters.page} {totalPages}
</span>
<button
disabled={filters.page === totalPages}
onClick={() => setFilters((prev) => ({ ...prev, page: prev.page + 1 }))}
>
</button>
</div>
)}
</div>
);
}