Add cancel job button and improve jobs table UI
- Remove "All Jobs" title
- Move Status column to front of table
- Add Cancel button for in-progress jobs
- Add cancel endpoint: POST /pypi/cache/cancel/{package_name}
- Add btn-danger CSS styling
This commit is contained in:
@@ -794,3 +794,11 @@ export async function retryAllPyPICacheTasks(): Promise<PyPICacheRetryResponse>
|
||||
});
|
||||
return handleResponse<PyPICacheRetryResponse>(response);
|
||||
}
|
||||
|
||||
export async function cancelPyPICacheTask(packageName: string): Promise<PyPICacheRetryResponse> {
|
||||
const response = await fetch(`/pypi/cache/cancel/${encodeURIComponent(packageName)}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
return handleResponse<PyPICacheRetryResponse>(response);
|
||||
}
|
||||
|
||||
@@ -429,6 +429,17 @@
|
||||
border-color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #ef4444;
|
||||
border-color: #ef4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background-color: #dc2626;
|
||||
border-color: #dc2626;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 0.25rem 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getPyPICacheActiveTasks,
|
||||
retryPyPICacheTask,
|
||||
retryAllPyPICacheTasks,
|
||||
cancelPyPICacheTask,
|
||||
} from '../api';
|
||||
import { PyPICacheStatus, PyPICacheTask, PyPICacheActiveTask } from '../types';
|
||||
import './AdminJobsPage.css';
|
||||
@@ -25,6 +26,7 @@ function AdminJobsPage() {
|
||||
// Action states
|
||||
const [retryingPackage, setRetryingPackage] = useState<string | null>(null);
|
||||
const [retryingAll, setRetryingAll] = useState(false);
|
||||
const [cancelingPackage, setCancelingPackage] = useState<string | null>(null);
|
||||
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
||||
|
||||
// Auto-refresh
|
||||
@@ -110,6 +112,19 @@ function AdminJobsPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCancelPackage(packageName: string) {
|
||||
setCancelingPackage(packageName);
|
||||
try {
|
||||
const result = await cancelPyPICacheTask(packageName);
|
||||
setSuccessMessage(result.message);
|
||||
await loadData();
|
||||
} catch (err) {
|
||||
setStatusError(err instanceof Error ? err.message : 'Failed to cancel');
|
||||
} finally {
|
||||
setCancelingPackage(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (authLoading) {
|
||||
return <div className="admin-jobs-page">Loading...</div>;
|
||||
}
|
||||
@@ -217,18 +232,10 @@ function AdminJobsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Unified Jobs Section */}
|
||||
{/* Jobs Section */}
|
||||
<section className="jobs-section">
|
||||
<div className="section-header">
|
||||
<h2>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
|
||||
<polyline points="3.27 6.96 12 12.01 20.73 6.96"/>
|
||||
<line x1="12" y1="22.08" x2="12" y2="12"/>
|
||||
</svg>
|
||||
All Jobs
|
||||
</h2>
|
||||
{failedTasks.length > 0 && (
|
||||
{failedTasks.length > 0 && (
|
||||
<div className="section-header">
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={handleRetryAll}
|
||||
@@ -236,8 +243,8 @@ function AdminJobsPage() {
|
||||
>
|
||||
{retryingAll ? 'Retrying...' : `Retry All Failed (${failedTasks.length})`}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loadingStatus && !cacheStatus ? (
|
||||
<p className="loading-text">Loading job status...</p>
|
||||
@@ -249,9 +256,9 @@ function AdminJobsPage() {
|
||||
<table className="jobs-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<th>Type</th>
|
||||
<th>Package</th>
|
||||
<th>Status</th>
|
||||
<th>Depth</th>
|
||||
<th>Attempts</th>
|
||||
<th>Details</th>
|
||||
@@ -262,13 +269,6 @@ function AdminJobsPage() {
|
||||
{allJobs.map((job) => {
|
||||
return (
|
||||
<tr key={job.id} className={`job-row ${job.status}`}>
|
||||
<td>
|
||||
<span className="type-badge pypi">PyPI</span>
|
||||
</td>
|
||||
<td className="package-name">
|
||||
{job.status === 'in_progress' && <span className="spinner"></span>}
|
||||
{job.package}
|
||||
</td>
|
||||
<td className="status-cell">
|
||||
{job.status === 'in_progress' ? (
|
||||
<span className="status-badge working">Working</span>
|
||||
@@ -278,6 +278,13 @@ function AdminJobsPage() {
|
||||
<span className="status-badge pending">Pending</span>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<span className="type-badge pypi">PyPI</span>
|
||||
</td>
|
||||
<td className="package-name">
|
||||
{job.status === 'in_progress' && <span className="spinner"></span>}
|
||||
{job.package}
|
||||
</td>
|
||||
<td>{job.depth}</td>
|
||||
<td>{job.attempts}</td>
|
||||
<td className="details-cell">
|
||||
@@ -305,6 +312,15 @@ function AdminJobsPage() {
|
||||
{retryingPackage === job.package ? '...' : 'Retry'}
|
||||
</button>
|
||||
)}
|
||||
{job.status === 'in_progress' && (
|
||||
<button
|
||||
className="btn btn-sm btn-danger"
|
||||
onClick={() => handleCancelPackage(job.package)}
|
||||
disabled={cancelingPackage === job.package}
|
||||
>
|
||||
{cancelingPackage === job.package ? '...' : 'Cancel'}
|
||||
</button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user