log-center/web/src/pages/RepairList.tsx
zyc 20a3b0b374
All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 1m42s
add UI
2026-02-10 14:55:28 +08:00

152 lines
6.0 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { getRepairReports, type RepairReport, getProjects } from '../api';
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">Repair Reports</h1>
<p className="page-subtitle">AI-powered bug repair attempts and their results</p>
</div>
<div className="filters">
<select
className="filter-select"
value={filters.project_id}
onChange={(e) => handleFilterChange('project_id', e.target.value)}
>
<option value="">All Projects</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">No reports found</div>
) : (
<table>
<thead>
<tr>
<th>ID</th>
<th>Project</th>
<th>Bug ID</th>
<th>Modified Files</th>
<th>Test Result</th>
<th>Status</th>
<th>Date</th>
<th>Actions</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} files</td>
<td>
<span className={report.test_passed ? 'test-pass' : 'test-fail'}>
{report.test_passed ? 'PASS' : 'FAIL'}
</span>
</td>
<td>
<span className={`status-badge status-${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">
View
</Link>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
{totalPages > 1 && (
<div className="pagination">
<button
disabled={filters.page === 1}
onClick={() => setFilters((prev) => ({ ...prev, page: prev.page - 1 }))}
>
Previous
</button>
<span className="pagination-info">
Page {filters.page} of {totalPages}
</span>
<button
disabled={filters.page === totalPages}
onClick={() => setFilters((prev) => ({ ...prev, page: prev.page + 1 }))}
>
Next
</button>
</div>
)}
</div>
);
}