diff --git a/public/upload.html b/public/upload.html
index a0acdb0..4fe6eca 100644
--- a/public/upload.html
+++ b/public/upload.html
@@ -272,6 +272,7 @@
+
@@ -287,6 +288,7 @@
const queueInfo = document.getElementById('queueInfo');
const queueCancel = document.getElementById('queueCancel');
const queuePause = document.getElementById('queuePause');
+ const queueRetry = document.getElementById('queueRetry');
const list = document.getElementById('list');
const MAX_PARALLEL = 3; // dépôts directs (petits fichiers) simultanés
@@ -376,6 +378,8 @@
job.state = state;
if (state === 'done' || state === 'failed' || state === 'cancelled') jobs.delete(job);
else jobs.add(job);
+ if (state === 'failed') failedJobs.add(job);
+ else failedJobs.delete(job);
updateQueueRow();
const { pauseBtn, retryBtn, cancelBtn } = job.ui;
pauseBtn.hidden = state !== 'running' && state !== 'paused';
@@ -393,6 +397,7 @@
const active = new Set();
const jobs = new Set(); // jobs non terminés (en attente, en cours ou en pause)
+ const failedJobs = new Set(); // jobs en échec, relançables (« Réessayer » / « Tout réessayer »)
let allPaused = false; // « Tout mettre en pause » actif : la file ne démarre plus rien
function updateQueueRow() {
@@ -404,18 +409,24 @@
else if (job.state === 'paused') paused += 1;
else waiting += 1;
}
- if (jobs.size === 0) {
- queueRow.hidden = true;
+ const failed = failedJobs.size;
+ const hasJobs = jobs.size > 0;
+ if (!hasJobs) {
allPaused = false;
queuePause.textContent = 'Tout mettre en pause';
- return;
}
- queueRow.hidden = false;
- queueInfo.textContent =
- running +
- ' en cours' +
- (paused ? ' · ' + paused + ' en pause' : '') +
- (waiting ? ' · ' + waiting + ' en attente' : '');
+ queueRow.hidden = !hasJobs && failed === 0;
+ queuePause.hidden = !hasJobs;
+ queueCancel.hidden = !hasJobs;
+ queueRetry.hidden = failed === 0;
+ queueInfo.textContent = [
+ hasJobs ? running + ' en cours' : '',
+ paused ? paused + ' en pause' : '',
+ waiting ? waiting + ' en attente' : '',
+ failed ? failed + ' en échec' : '',
+ ]
+ .filter(Boolean)
+ .join(' · ');
}
function pump() {
@@ -908,7 +919,12 @@
pump();
}
+ function retryAll() {
+ for (const job of Array.from(failedJobs)) retryJob(job);
+ }
+
queuePause.addEventListener('click', toggleAllPaused);
+ queueRetry.addEventListener('click', retryAll);
queueCancel.addEventListener('click', cancelAll);
// items : tableau de { file, path } — path '' = fichier simple, sinon dépôt de dossier.
diff --git a/tests/pages.test.ts b/tests/pages.test.ts
index 0ea0f81..7034f80 100644
--- a/tests/pages.test.ts
+++ b/tests/pages.test.ts
@@ -95,6 +95,7 @@ describe('GET /', () => {
});
expect(res.body).toContain('id="zones"');
expect(res.body).toContain('id="queuePause"');
+ expect(res.body).toContain('id="queueRetry"');
expect(res.body).not.toContain('targetSelect');
expect(res.body).not.toContain('dirPicker');
});