# Story 4.8: Page Translation - Progress & Download Status: done ## Story En tant qu'**utilisateur**, Je veux **voir la progression de la traduction en temps réel et télécharger le fichier traduit**, de sorte que **je sache quand mon fichier est prêt et puisse l'utiliser**. ## Acceptance Criteria 1. **Submit Translation**: Au clic sur "Translate", POST vers `/api/v1/translate` avec `file`, `source_lang`, `target_lang`, `mode`, `provider` (si LLM) 2. **Job ID Storage**: Stocker le `job_id` retourné (202 response: `{data: {id, status: "processing"}, meta: {...}}`) 3. **Progress Polling**: Poll `GET /api/v1/translations/{job_id}` toutes les 2 secondes pendant `status === "processing"` 4. **Progress Display**: Afficher progress bar (0-100%), `current_step`, et temps restant estimé (`meta.estimated_remaining_seconds`) 5. **Status Transitions**: `processing` → `completed` (succès) ou `failed` (erreur) 6. **Error Display**: Si `failed`, afficher `error_message` du backend avec toast notification 7. **Download Button**: Quand `completed`, afficher bouton "Download" qui GET `/api/v1/download/{job_id}` 8. **Download Filename**: Header `Content-Disposition` fourni par backend → `{original_name}_translated.{ext}` 9. **Success Toast**: Afficher confirmation après téléchargement réussi 10. **State Reset**: Option "New Translation" pour réinitialiser et uploader un nouveau fichier 11. **Merge Directive**: UI Progress depuis `frontend/src/components/file-uploader.tsx` (lignes 592-609) 12. **Colocation**: Composants/hooks/types dans `frontend/src/app/dashboard/translate/` ## Tasks / Subtasks - [x] **Task 1: Étendre types.ts** (AC: #1, #2) - [x] 1.1 Ajouter `TranslationJob`, `TranslationStatus`, `TranslationProgress` - [x] 1.2 Ajouter `UseTranslationSubmitReturn` interface - [x] **Task 2: Créer useTranslationSubmit.ts hook** (AC: #1, #2, #3, #4, #5) - [x] 2.1 State: `jobId`, `status`, `progress`, `currentStep`, `error`, `estimatedRemaining` - [x] 2.2 `submitTranslation(file, config)`: POST `/api/v1/translate` → return job_id - [x] 2.3 `pollProgress()`: setInterval 2s → GET `/api/v1/translations/{job_id}` - [x] 2.4 Cleanup interval on unmount ou quand status !== "processing" - [x] 2.5 Return: `{ submitTranslation, jobId, status, progress, currentStep, error, estimatedRemaining, reset }` - [x] **Task 3: Créer TranslationProgress.tsx** (AC: #4, #6) - [x] 3.1 Props: `progress`, `currentStep`, `estimatedRemaining`, `error` - [x] 3.2 Progress bar avec pourcentage (shadcn/ui Progress) - [x] 3.3 Afficher `current_step` textuel (ex: "Translating slide 5/10") - [x] 3.4 Afficher temps restant formaté (ex: "2 min remaining") - [x] 3.5 Error state: message rouge si `status === "failed"` - [x] 3.6 Copier styles depuis `file-uploader.tsx` (lignes 592-609) - [x] **Task 4: Créer TranslationComplete.tsx** (AC: #7, #8, #9) - [x] 4.1 Props: `jobId`, `fileName`, `onNewTranslation` - [x] 4.2 Download button: GET `/api/v1/download/{job_id}` → trigger browser download - [x] 4.3 Success toast après download - [x] 4.4 "New Translation" button → callback `onNewTranslation` - [x] 4.5 Checkmark icon + message "Translation Complete!" - [x] 4.6 Copier styles depuis `file-uploader.tsx` (lignes 627-649) - [x] **Task 5: Modifier page.tsx pour intégrer le flow complet** (AC: #1, #10) - [x] 5.1 State flow: Upload → Config → Submit → Progress → Complete - [x] 5.2 Intégrer `useTranslationSubmit` hook - [x] 5.3 Conditionnel: afficher Progress si `status === "processing"` - [x] 5.4 Conditionnel: afficher Complete si `status === "completed"` - [x] 5.5 Conditionnel: afficher Error si `status === "failed"` - [x] 5.6 `handleTranslate` → `submitTranslation(file, config.getConfig())` - [x] 5.7 `handleNewTranslation` → reset all states - [x] **Task 6: Vérifier le build et tester** (AC: Tous) - [x] 6.1 `npm run build` → 0 erreurs TypeScript - [x] 6.2 Tester submit translation → job_id retourné - [x] 6.3 Tester polling progress → progress bar updates - [x] 6.4 Tester download → fichier téléchargé - [x] 6.5 Tester error handling → message affiché - [x] 6.6 Tester new translation → reset complet ## Dev Notes ### 🏗️ Stack Technique | Technologie | Version | |-------------|---------| | Next.js | 16.0.6 (App Router) | | React | 19.2.0 | | TanStack Query | v5.90.21 (optionnel - polling manuel OK) | | Tailwind CSS | configuré | | Lucide React | disponible | | shadcn/ui | Progress, Button, Card, Toast | ### 📁 Structure Cible (Colocation Pattern) ``` frontend/src/app/dashboard/translate/ ├── page.tsx # Existant — à modifier ├── types.ts # Existant — à étendre ├── useFileUpload.ts # Existant — pas de modif ├── FileDropZone.tsx # Existant — pas de modif ├── FilePreview.tsx # Existant — pas de modif ├── useTranslationConfig.ts # Existant — pas de modif ├── LanguageSelector.tsx # Existant — pas de modif ├── TranslationModeToggle.tsx # Existant — pas de modif ├── ProviderSelector.tsx # Existant — pas de modif ├── useTranslationSubmit.ts # 🆕 Hook submit + polling ├── TranslationProgress.tsx # 🆕 Progress bar + status └── TranslationComplete.tsx # 🆕 Download + success ``` **⚠️ Règle absolue (architecture.md):** ``` 🚨 FICHIERS SPÉCIAUX: page.tsx, layout.tsx → TOUJOURS minuscules 🚨 COLOCATION: Components/hooks/types dans le dossier de leur page ``` ### 🔗 API Endpoints Backend (Déjà implémentés) | Endpoint | Méthode | Request | Response | |----------|---------|---------|----------| | `/api/v1/translate` | POST | FormData: file, source_lang, target_lang, mode, provider | 202: `{data: {id, status: "processing"}, meta: {rate_limit_remaining}}` | | `/api/v1/translations/{job_id}` | GET | — | `{data: {id, status, progress_percent, current_step, ...}, meta: {estimated_remaining_seconds}}` | | `/api/v1/download/{job_id}` | GET | — | Binary file + Content-Disposition header | ### References - [Source: _bmad-output/planning-artifacts/epics.md#Story-4.8] — Story requirements - [Source: _bmad-output/planning-artifacts/architecture.md#Frontend-Architecture] — TanStack Query + colocation - [Source: _bmad-output/planning-artifacts/architecture.md#API-Response-Formats] — Format réponse API - [Source: routes/translate_routes.py#L1064-1151] — GET /api/v1/translations/{job_id} endpoint - [Source: routes/translate_routes.py#L1188-1317] — GET /api/v1/download/{job_id} endpoint - [Source: frontend/src/components/file-uploader.tsx#L592-649] — UI Progress + Download patterns - [Source: _bmad-output/implementation-artifacts/4-7-page-translation-configuration.md] — Context config, types existants ## Dev Agent Record ### Agent Model Used Claude Sonnet 4 (claude-sonnet-4@20250514) ### Debug Log References N/A ### Completion Notes List - ✅ Étendu types.ts avec TranslationStatus, TranslationJob, TranslationSubmitResponse, TranslationStatusResponse, UseTranslationSubmitReturn - ✅ Créé useTranslationSubmit.ts hook avec submitTranslation, pollProgress (2s interval), cleanup on unmount, reset - ✅ Créé TranslationProgress.tsx avec progress bar (shadcn/ui Progress), current_step display, temps restant formaté, error state - ✅ Créé TranslationComplete.tsx avec download button (GET /api/v1/download), success toast, "New Translation" button - ✅ Modifié page.tsx pour intégrer le flow complet: Upload → Config → Submit → Progress → Complete/Failed - ✅ Build Next.js réussi (0 erreurs TypeScript) ### File List **Fichiers créés:** - frontend/src/app/dashboard/translate/useTranslationSubmit.ts - frontend/src/app/dashboard/translate/TranslationProgress.tsx - frontend/src/app/dashboard/translate/TranslationComplete.tsx **Fichiers modifiés:** - frontend/src/app/dashboard/translate/types.ts - frontend/src/app/dashboard/translate/page.tsx - _bmad-output/implementation-artifacts/sprint-status.yaml (in-progress → review) ## Senior Developer Review (AI) **Reviewer:** Code Review Agent **Date:** 2026-02-23 **Outcome:** Changes Requested → Fixed → Approved **Issues Found:** 9 (3 High, 4 Medium, 2 Low) ### Critical Issues Fixed 1. **AC #6 Not Fully Implemented** (HIGH) - Error toast notification added in page.tsx useEffect 2. **Silent Polling Failures** (HIGH) - Polling failures now tracked and surfaced to user after 3 consecutive failures 3. **Race Condition** (HIGH) - Added `isPollingRef` to prevent overlapping poll requests ### Medium Issues Fixed 4. **Memory Leak** (MEDIUM) - Blob URL now properly managed with useRef and cleanup on unmount 5. **No Upload Progress** (MEDIUM) - Submit button now shows "Uploading..." with spinner during file upload 6. **No Retry Tracking** (MEDIUM) - Added `pollingFailures` state and `MAX_POLLING_FAILURES` constant 7. **Accessibility** (MEDIUM) - Added ARIA attributes to Progress component and error alerts ### Code Quality Improvements Applied - Added `isPolling` and `pollingFailures` to hook return type - Enhanced error messages for network failures - Added visual indicator when connection is lost during translation - Proper cleanup of intervals and blob URLs on component unmount ## Change Log - 2026-02-23: Story created - ready for development - 2026-02-23: Implementation complete - all 6 tasks completed, build successful, ready for review - 2026-02-23: Code review complete - 9 issues identified and fixed, all HIGH/MEDIUM issues resolved