fix(relecture): un travail vide ou oublie restait affiche et impossible a retirer
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m29s

Un travail echoue ou expire cote serveur restait dans « Traductions
recentes » : sa page de relecture n'affichait rien (0 segments) avec une
erreur brute non traduite, et aucune suppression n'existait.

- serveur : nouvelle route DELETE /api/v1/translations/{job_id} —
  retire le travail de la memoire et de Redis, supprime ses segments de
  relecture ; idempotente (200 meme si le serveur avait deja oublie le
  travail) ; refuse les travaux en cours (annuler d'abord) ; proprietaire
  seul (404 pour le travail d'autrui, sans reveler son existence)
- interface : bouton corbeille sur chaque ligne des traductions recentes
  (deux clics pour confirmer) qui nettoie la liste locale ET le serveur ;
  l'historique serveur ne liste plus les travaux non termines
- page de relecture : message d'erreur traduit (fini le message brut) et
  etat « rien a relire » explicite avec explication
- libelles ajoutes dans les 13 langues de l'interface
- 5 tests backend nouveaux (tests/test_delete_translation_history.py)
This commit is contained in:
2026-09-01 21:22:05 +02:00
parent b3b5454f6f
commit 2abd0c0b26
21 changed files with 380 additions and 27 deletions

View File

@@ -22,7 +22,7 @@ interface StoredJob { jobId: string; fileName: string | null; savedAt: number }
export interface RecentJob { jobId: string; fileName: string; completedAt: number }
/** Server history (last jobs, newest first) — empty when offline/unauthenticated. */
/** Server history (last completed jobs, newest first) — empty when offline/unauthenticated. */
export async function fetchServerHistory(perPage = 6): Promise<RecentJob[]> {
if (typeof window === 'undefined') return [];
const token = localStorage.getItem('token');
@@ -33,14 +33,16 @@ export async function fetchServerHistory(perPage = 6): Promise<RecentJob[]> {
});
if (!res.ok) return [];
const json = await res.json();
return (json.data ?? []).map((j: {
id: string; file_name?: string | null;
completed_at?: string | null; created_at?: string | null;
}) => ({
jobId: j.id,
fileName: j.file_name ?? '',
completedAt: Date.parse(j.completed_at ?? j.created_at ?? '') || Date.now(),
}));
return (json.data ?? [])
.filter((j: { status?: string | null }) => (j.status ?? 'completed') === 'completed')
.map((j: {
id: string; file_name?: string | null; status?: string | null;
completed_at?: string | null; created_at?: string | null;
}) => ({
jobId: j.id,
fileName: j.file_name ?? '',
completedAt: Date.parse(j.completed_at ?? j.created_at ?? '') || Date.now(),
}));
} catch {
return [];
}
@@ -56,6 +58,31 @@ export function getRecentJobs(): RecentJob[] {
}
}
/** Remove a job from this browser's local history. */
export function removeRecentJob(jobId: string) {
try {
const list = getRecentJobs().filter(j => j.jobId !== jobId);
localStorage.setItem(RECENT_JOBS_KEY, JSON.stringify(list));
} catch { /* ignore */ }
}
/**
* Delete a job on the server (history entry + review segments).
* Silent by design: a 404 means the server already forgot the job, which
* is exactly the state the caller wants.
*/
export async function deleteServerJob(jobId: string): Promise<void> {
if (typeof window === 'undefined') return;
const token = localStorage.getItem('token');
if (!token) return;
try {
await fetch(`${API_BASE}/api/v1/translations/${jobId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
} catch { /* offline — the local entry is removed regardless */ }
}
function persistActiveJob(job: StoredJob | null) {
try {
if (!job) localStorage.removeItem(ACTIVE_JOB_KEY);