# Story 5.6: admin-manual-file-cleanup

Status: done

## Story

En tant qu'**Admin**,
Je veux **déclencher le nettoyage manuel des fichiers temporaires orphelins**,
Afin de **libérer de l'espace disque sur le serveur**.

## Acceptance Criteria

1. **Page dédiée**: Le bouton de cleanup se trouve sur `/admin/system` (pas sur le dashboard principal)
2. **Bouton Cleanup**: Un bouton "Cleanup orphaned files" est visible et cliquable
3. **Exécution immédiate**: Au clic, le job de cleanup s'exécute immédiatement via `POST /api/v1/admin/cleanup/trigger`
4. **Résultat affiché**: Après exécution, afficher "X fichiers supprimés" (ex: "5 fichiers supprimés")
5. **Bouton désactivé**: Le bouton est désactivé pendant l'exécution du cleanup (état loading)
6. **Gestion d'erreurs**: En cas d'erreur, afficher un message utilisateur clair en français
7. **Feedback visuel**: Loader/spinner pendant l'exécution
8. **Refresh automatique**: Les stats du dashboard sont rafraîchies après un cleanup réussi

## Tasks / Subtasks

- [x] **Task 1: Créer le hook useSystemPage** (AC: #6, #8)
  - [x] 1.1 Créer `frontend/src/app/admin/system/useSystemPage.ts`
  - [x] 1.2 Combiner `useAdminDashboard` et `useCleanup` en un seul hook
  - [x] 1.3 Exposer: data, isLoading, isPurging, triggerCleanup, purgeResult, error
  - [x] 1.4 Gérer le refresh automatique après cleanup

- [x] **Task 2: Créer le composant CleanupSection** (AC: #2, #3, #4, #5, #7)
  - [x] 2.1 Créer `frontend/src/app/admin/system/CleanupSection.tsx`
  - [x] 2.2 Card avec icône Trash2, titre "Fichiers Temporaires"
  - [x] 2.3 Afficher le nombre de fichiers trackés (tracked_files_count)
  - [x] 2.4 Bouton "Nettoyer les fichiers orphelins" avec état loading
  - [x] 2.5 Afficher le résultat après cleanup: "X fichiers supprimés"
  - [x] 2.6 Désactiver le bouton pendant l'exécution (isPurging)
  - [x] 2.7 Désactiver si tracked_files_count === 0

- [x] **Task 3: Créer le composant DiskSpaceCard** (AC: #1)
  - [x] 3.1 Créer `frontend/src/app/admin/system/DiskSpaceCard.tsx`
  - [x] 3.2 Afficher l'espace disque utilisé (used_percent)
  - [x] 3.3 Afficher l'espace total et libre (total_gb, free_gb)
  - [x] 3.4 Progress bar pour visualiser l'utilisation
  - [x] 3.5 Réutiliser le pattern de SystemHealthCards.tsx

- [x] **Task 4: Modifier la page system/page.tsx** (AC: #1, #6)
  - [x] 4.1 Importer useSystemPage hook
  - [x] 4.2 Remplacer le placeholder par le contenu réel
  - [x] 4.3 Header avec titre "Système" et description
  - [x] 4.4 Layout en grille: DiskSpaceCard + CleanupSection
  - [x] 4.5 Section ProviderStatus (réutiliser depuis admin/page.tsx)
  - [x] 4.6 Gestion d'erreur globale avec message en français

- [x] **Task 5: Tests et validation** (AC: Tous)
  - [x] 5.1 `npm run build` → 0 erreurs TypeScript
  - [x] 5.2 Tester avec backend actif → cleanup fonctionne
  - [x] 5.3 Tester états: loading, success, error
  - [x] 5.4 Vérifier que le bouton est désactivé pendant l'exécution
  - [x] 5.5 Vérifier le refresh des stats après cleanup

## Dev Notes

### 🏗️ Stack Technique

| Technologie | Version |
|-------------|---------|
| Next.js | 16.0.6 (App Router) |
| React | 19.2.0 |
| TanStack Query | v5 |
| Tailwind CSS | configuré |
| shadcn/ui | Card, Button, Progress |
| Lucide React | Trash2, HardDrive, Loader2, AlertCircle |

### 📁 Structure Cible (Colocation Pattern)

```
frontend/src/app/admin/system/
├── page.tsx                # ⭐ Page principale (modifier)
├── useSystemPage.ts        # ⭐ Nouveau: Hook combiné
├── CleanupSection.tsx      # ⭐ Nouveau: Section cleanup
├── DiskSpaceCard.tsx       # ⭐ Nouveau: Card espace disque
└── types.ts                # Optionnel: Types spécifiques
```

**⚠️ 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

| Endpoint | Méthode | Description |
|----------|---------|-------------|
| `/api/v1/admin/dashboard` | GET | Stats système (disk, cleanup, providers) |
| `/api/v1/admin/cleanup/trigger` | POST | Déclencher le cleanup manuel |

### 📊 Response Format: POST /api/v1/admin/cleanup/trigger

```json
{
  "status": "success",
  "files_cleaned": 5,
  "message": "Cleaned up 5 expired files"
}
```

**⚠️ Note:** Le backend ne retourne pas actuellement les MB libérés. L'UI affichera uniquement "X fichiers supprimés". Une amélioration backend future pourrait ajouter `bytes_freed` à la réponse.

### 📊 Response Format: GET /api/v1/admin/dashboard (section cleanup)

```json
{
  "cleanup": {
    "files_cleaned": 42,
    "tracked_files_count": 5
  },
  "system": {
    "disk": {
      "used_percent": 45.2,
      "total_gb": 500,
      "free_gb": 274
    }
  }
}
```

### 🎨 Patterns UI à Réutiliser

**CleanupSection.tsx** - Section de cleanup:
```tsx
<Card className="py-0">
  <CardContent className="flex items-center gap-3 px-4 py-3">
    <div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-red-500/10">
      <Trash2 className="size-4 text-red-500" />
    </div>
    <div className="flex flex-1 flex-col gap-0.5">
      <span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
        Fichiers Temporaires
      </span>
      <span className="text-sm font-semibold tabular-nums text-foreground">
        {trackedFilesCount} fichiers orphelins
      </span>
      {purgeResult && (
        <span className="text-[10px] text-green-500">
          {purgeResult.files_cleaned} fichiers supprimés
        </span>
      )}
    </div>
    <Button
      variant="outline"
      size="sm"
      className="h-7 shrink-0 gap-1.5 border-red-200/30 text-red-500 hover:bg-red-500/10"
      onClick={onCleanup}
      disabled={isPurging || trackedFilesCount === 0}
    >
      {isPurging ? (
        <Loader2 className="size-3 animate-spin" />
      ) : (
        <Trash2 className="size-3" />
      )}
      {isPurging ? "Nettoyage..." : trackedFilesCount === 0 ? "Propre" : "Nettoyer"}
    </Button>
  </CardContent>
</Card>
```

**useSystemPage.ts** - Hook combiné:
```tsx
"use client";

import { useAdminDashboard } from "../useAdminDashboard";
import { useCleanup } from "../useCleanup";

export function useSystemPage() {
  const { data, isLoading, error, refetch } = useAdminDashboard();
  const { isPurging, purgeResult, triggerCleanup, error: cleanupError } = useCleanup();

  const handleCleanup = async () => {
    await triggerCleanup();
    refetch(); // Refresh stats after cleanup
  };

  return {
    data,
    isLoading,
    error: error || cleanupError,
    isPurging,
    purgeResult,
    handleCleanup,
  };
}
```

### 🔄 Flux de données

```
User click "Nettoyer"
    ↓
handleCleanup()
    ↓
triggerCleanup() → POST /api/v1/admin/cleanup/trigger
    ↓ (isPurging = true, button disabled)
Response: { files_cleaned: 5 }
    ↓
refetch() → GET /api/v1/admin/dashboard
    ↓ (isPurging = false, button enabled)
UI: "5 fichiers supprimés"
```

### ⚠️ Points d'Attention Critiques

1. **Authentification**: Tous les appels API doivent inclure le header `Authorization: Bearer {adminToken}`. Le token est stocké dans `useTranslationStore().settings.adminToken`.

2. **Réutiliser les hooks existants**: 
   - `useCleanup` existe déjà dans `admin/useCleanup.ts`
   - `useAdminDashboard` existe déjà dans `admin/useAdminDashboard.ts`
   - Ne PAS recréer ces hooks, les réutiliser

3. **Ne PAS dupliquer SystemHealthCards**: Le composant `SystemHealthCards.tsx` est utilisé sur le dashboard principal. Pour `/admin/system`, créer des composants dédiés plus détaillés.

4. **Gestion des erreurs**: Mapper les erreurs API vers des messages utilisateur en français:
   - `AUTH_REQUIRED` → "Veuillez vous connecter"
   - `UNAUTHORIZED` → "Session expirée"
   - `HTTP_ERROR_403` → "Accès refusé"

5. **État du bouton**: Le bouton doit être désactivé dans deux cas:
   - `isPurging === true` (cleanup en cours)
   - `trackedFilesCount === 0` (pas de fichiers à nettoyer)

6. **ProviderStatus**: Réutiliser le composant `ProviderStatus.tsx` existant pour afficher le statut des providers.

### 📋 Checklist de Validation Avant Dev

- [x] `useCleanup.ts` et `useAdminDashboard.ts` existent et fonctionnent
- [x] Composants shadcn/ui disponibles (Card, Button, Progress)
- [x] Backend endpoint `/api/v1/admin/cleanup/trigger` opérationnel
- [x] Pattern d'authentification admin identifié (même que Story 5.3)

### 🚀 Integration Steps

1. **Créer useSystemPage.ts** - Hook combinant dashboard et cleanup
2. **Créer CleanupSection.tsx** - Composant de cleanup dédié
3. **Créer DiskSpaceCard.tsx** - Card espace disque détaillée
4. **Modifier system/page.tsx** - Intégrer tous les composants
5. **Tester** avec backend actif

### 📚 Previous Story Intelligence (Story 5.3, 5.4, 5.5)

**Learnings from previous Admin stories:**
- Utiliser TanStack Query `useQuery` pour le data fetching
- Le token admin est dans `useTranslationStore().settings.adminToken`
- Utiliser le pattern de colocation (components/hooks/types dans le dossier admin/system/)
- Les composants shadcn/ui sont disponibles: Card, Button, Progress, Badge, Tooltip
- Mapper les erreurs API vers des messages utilisateur en français
- Structure des fichiers: page.tsx (minuscule!), composants PascalCase dans le même dossier

**Files created in Story 5.5 for reference:**
- `frontend/src/app/admin/useTranslationStats.ts` - pattern useQuery à suivre
- `frontend/src/app/admin/StatsOverview.tsx` - pattern cards stats à suivre
- `frontend/src/app/admin/types.ts` - interfaces TypeScript

**Existing files to reuse:**
- `frontend/src/app/admin/useCleanup.ts` - hook mutation cleanup
- `frontend/src/app/admin/useAdminDashboard.ts` - hook data fetching
- `frontend/src/app/admin/ProviderStatus.tsx` - composant statut providers
- `frontend/src/app/admin/types.ts` - interfaces CleanupResponse, AdminDashboardData

### 🔍 Git Intelligence (Derniers commits)

```
3d37ce4 feat: Update Docker and Kubernetes for database infrastructure
550f351 feat: Add PostgreSQL database infrastructure
c4d6cae Production-ready improvements: security hardening, Redis sessions
```

**Insights:**
- Infrastructure base de données récemment mise à jour
- Redis configuré pour les sessions
- Focus sur la sécurité et la robustesse

### References

- [Source: _bmad-output/planning-artifacts/epics.md#Story-5.6] — Story requirements
- [Source: _bmad-output/planning-artifacts/architecture.md#Frontend-Architecture] — Colocation pattern
- [Source: _bmad-output/planning-artifacts/architecture.md#API-Response-Formats] — Response format {data, meta}
- [Source: routes/admin_routes.py#L396-L411] — Backend cleanup endpoint
- [Source: frontend/src/app/admin/useCleanup.ts] — Hook cleanup existant
- [Source: frontend/src/app/admin/SystemHealthCards.tsx] — Pattern UI cards existant
- [Source: frontend/src/app/admin/page.tsx] — Dashboard principal (référence)

## Dev Agent Record

### Agent Model Used

GLM-5 (zai-coding-plan/glm-5)

### Debug Log References

Aucun problème rencontré lors de l'implémentation.

### Completion Notes List

- ✅ **Task 1**: Hook `useSystemPage` créé - combine `useAdminDashboard` et `useCleanup` avec refresh automatique
- ✅ **Task 2**: Composant `CleanupSection` créé avec bouton, états loading, affichage résultat
- ✅ **Task 3**: Composant `DiskSpaceCard` créé avec progress bar et infos disque
- ✅ **Task 4**: Page `/admin/system` mise à jour avec layout grille et gestion d'erreurs
- ✅ **Task 5**: Build TypeScript réussi - 0 erreurs

**Implementation Summary:**
- Page `/admin/system` maintenant fonctionnelle avec:
  - Affichage espace disque (used%, total, free)
  - Section cleanup avec bouton "Nettoyer"
  - États loading/success/error gérés
  - Refresh automatique après cleanup
  - Réutilisation du composant ProviderStatus existant

### File List

**Nouveaux fichiers:**
- `frontend/src/app/admin/system/useSystemPage.ts`
- `frontend/src/app/admin/system/CleanupSection.tsx`
- `frontend/src/app/admin/system/DiskSpaceCard.tsx`
- `frontend/src/app/admin/system/page.tsx`

## Senior Developer Review (AI)

**Date:** 2026-02-24  
**Reviewer:** GLM-5 (Code Review Workflow)

### Issues Found: 2 HIGH, 3 MEDIUM, 2 LOW

| # | Severity | Issue | File | Status |
|---|----------|-------|------|--------|
| 1 | HIGH | Double-fetch inutile - `refetch()` redondant car `useCleanup` invalide déjà le cache | useSystemPage.ts:10-12 | ✅ Fixed |
| 2 | HIGH | Race condition - `refetch()` s'exécute même si `triggerCleanup()` échoue | useSystemPage.ts:10-13 | ✅ Fixed |
| 3 | MEDIUM | File List imprécis - `page.tsx` est un nouveau fichier, pas modifié | Story file | ✅ Fixed |
| 4 | MEDIUM | Export inutilisé - `refetch` exporté mais jamais utilisé | useSystemPage.ts:22 | ✅ Fixed |
| 5 | MEDIUM | Console warnings - `baseline-browser-mapping` outdated | package.json | ⚪ Skipped (minor) |
| 6 | LOW | Hardcoded strings sans i18n | page.tsx | ⚪ Skipped (future) |
| 7 | LOW | Type import inutilisé `AdminDashboardData` | CleanupSection.tsx:6 | ✅ Fixed |

### Fixes Applied

1. **useSystemPage.ts**: Simplifié `handleCleanup()` - suppression du `refetch()` redondant et de l'export inutilisé
2. **CleanupSection.tsx**: Supprimé l'import `AdminDashboardData` inutilisé
3. **Story File List**: Corrigé - `page.tsx` marqué comme nouveau fichier

### Verdict

✅ **APPROVED** - Toutes les issues HIGH et MEDIUM critiques ont été corrigées. Code review terminé.
