veza/apps/web/dev_audit/frontend/08_technical_debt_frontend.md

176 lines
9.5 KiB
Markdown
Raw Normal View History

# Phase H — Dette Technique Frontend
---
## H1. Complexité excessive
### Fichiers > 300 lignes (source, hors tests/generated)
| Fichier | Lignes | Raison probable | Split possible ? | Complexité |
|---------|--------|-----------------|-----------------|-----------|
| `services/api/client.ts` | 2 237 | Client HTTP + validation + caching + retry + dedup + metrics | ✅ Oui, 5 modules | 🔴 Élevée |
| `mocks/handlers.ts` | 1 716 | MSW handlers pour toutes les routes | ✅ Par feature | 🟡 Linéaire |
| `features/tracks/api/trackApi.ts` | 848 | API tracks complète | ✅ CRUD/Upload/Share/Analytics | 🟠 Moyenne |
| `utils/optimisticUpdates.ts` | 682 | Optimistic updates multi-feature | ⚠️ Difficilement | 🟠 Moyenne |
| `features/streaming/services/playbackAnalyticsService.ts` | 656 | Analytics streaming | ⚠️ Cohérent | 🟡 Linéaire |
| `features/playlists/hooks/usePlaylist.ts` | 631 | Hook playlist (CRUD + collab + analytics) | ✅ 3 hooks | 🔴 Élevée |
| `utils/apiErrorHandler.ts` | 578 | Error parsing exhaustif | ⚠️ Cohérent | 🟡 Linéaire |
| `features/streaming/hooks/usePlaybackRealtime.ts` | 496 | WebSocket + state + analytics | ⚠️ Justifié temps réel | 🟠 Moyenne |
| `services/api/auth.ts` | 493 | Auth API (login, register, 2FA, OAuth) | ⚠️ Cohérent | 🟡 Linéaire |
| `schemas/apiRequestSchemas.ts` | 476 | Zod schemas | ❌ Normal | 🟢 Faible |
| `schemas/apiSchemas.ts` | 468 | Zod schemas | ❌ Normal | 🟢 Faible |
| `features/tracks/services/trackService.ts` | 453 | Service tracks | ⚠️ Cohérent | 🟡 Linéaire |
| `features/playlists/services/playlistService.ts` | 448 | Service playlists | ⚠️ Cohérent | 🟡 Linéaire |
| `utils/sanitize.ts` | 429 | Sanitization XSS | ❌ Critique | 🟢 Faible |
| `features/chat/hooks/useChat.ts` | 360 | Hook chat | ✅ 2-3 hooks | 🟠 Moyenne |
| `features/auth/store/authStore.ts` | 330 | Store auth | ⚠️ Acceptable | 🟡 Linéaire |
| `features/dashboard/pages/DashboardPage.tsx` | 328 | Page dashboard | ✅ Extraire sections | 🟡 Moyenne |
| `features/tracks/components/TrackListRow.tsx` | 320 | Ligne de track | ✅ Sous-composants | 🟡 Moyenne |
**Priorité de split** :
1. `client.ts` (2237L) → `httpClient.ts`, `validators.ts`, `caching.ts`, `interceptors.ts`, `metrics.ts`
2. `usePlaylist.ts` (631L) → `usePlaylistCrud.ts`, `usePlaylistCollaboration.ts`, `usePlaylistAnalytics.ts`
3. `useChat.ts` (360L) → `useChatMessages.ts`, `useChatConnection.ts`
---
## H2. Props drilling
Grâce à l'utilisation de Zustand (7 stores) et React Query, le prop drilling est **minimal**. Aucune chaîne de props > 3 niveaux intermédiaires identifiée dans le code audité.
**Pattern positif** : Les stores Zustand sont accédés directement dans les composants enfants via `useAuthStore()`, `useUIStore()`, `useCartStore()`, etc. — pas besoin de passer les props à travers les composants intermédiaires.
---
## H3. Hooks complexes
### Custom hooks > 50 lignes
| Hook | Lignes | Responsabilités | Testé ? |
|------|--------|----------------|---------|
| `features/playlists/hooks/usePlaylist.ts` | 631 | CRUD + collaboration + analytics + permissions | ✅ (595L de tests) |
| `features/streaming/hooks/usePlaybackRealtime.ts` | 496 | WebSocket + state + analytics + reconnection | ✅ (490L de tests) |
| `features/chat/hooks/useChat.ts` | 360 | Messages + connection + typing + presence | ❌ Non vérifié |
| `features/tracks/hooks/useTrackList.ts` | 286 | List + filters + sort + pagination + localStorage | ✅ (761L de tests) |
| `features/playlists/hooks/usePlaylistNotifications.ts` | 264 | Notification handling + navigation | ❌ Peu testé |
| `features/player/hooks/usePlayer.ts` | 249 | Playback control + queue + history | ✅ Tests partiels |
| `hooks/useRoutePreload.ts` | 239 | Route prefetching + intersection observer | ✅ Tests |
| `features/library/hooks/useLibraryItems.ts` | 152 | Library items + filters | ❌ Non vérifié |
**Verdict** : Les 3 plus gros hooks (usePlaylist, usePlaybackRealtime, useTrackList) sont bien testés. Les hooks de taille moyenne (useChat, usePlaylistNotifications) sont moins bien couverts.
---
## H4. Duplication
### Patterns CSS les plus répétés
| Pattern | Occurrences approximatives | Action recommandée |
|---------|---------------------------|-------------------|
| `flex items-center` | ~230 | Normal (utility-first) |
| `flex flex-col` | ~230 | Normal |
| `text-muted-foreground` | ~145 | Normal (sémantique) |
| `rounded-*` | ~230 | Normal |
| `w-full h-full` | ~90 | Normal |
Ces répétitions sont **attendues** avec Tailwind utility-first et ne constituent pas de la dette technique.
### Composants dupliqués
| Duplication | Fichiers | Impact |
|------------|---------|--------|
| `layout/Sidebar.tsx` (294L) + `ui/Sidebar.tsx` (217L) | 2 fichiers | 🟠 Confusion |
| `components/player/` (14 fichiers) + `features/player/components/` (~20 fichiers) | 34 fichiers | 🟠 Migration incomplète |
| `pages/auth/` + `features/auth/pages/` | 4+ fichiers | 🟠 Legacy |
| `context/AuthContext.tsx` + `features/auth/store/authStore.ts` | 2 fichiers | 🔴 Deux sources de vérité |
| `ui/modal.tsx` + `ui/dialog/` | 2 systèmes | 🟡 Redondance |
| `ui/dropdown-menu.tsx` + `ui/dropdown-menu/` | Fichier plat + dossier | 🟡 Legacy wrapper |
### Logique métier dupliquée
- **Auth** : `authService.login()` est appelé par `authStore.login()` ET `AuthContext.login()` — deux chemins d'exécution pour la même action.
- **Toast** : `toast()` (react-hot-toast) et `addToast()` (custom) — deux APIs pour le même feedback.
---
## H5. TypeScript
### Statistiques
| Métrique | Nombre | Zone |
|----------|--------|------|
| `: any` explicites (prod) | ~82 | Code source hors tests/generated |
| `as any` casts (prod) | ~180 | Code source hors tests/generated |
| `as any` dans `generated/api.ts` | 145 | Auto-généré — acceptable |
| `@ts-ignore` / `@ts-expect-error` | 7 fichiers | Minimal ✅ |
### Fichiers avec le plus de `as any` (source)
| Fichier | `as any` | Justification |
|---------|----------|---------------|
| `services/api/client.ts` | 48 | Error handling, interceptors — type narrowing difficile |
| `utils/typeGuards.ts` | 44 | Type guards by design — acceptable |
| `utils/toast.ts` | 11 | Wrapper react-hot-toast |
| `features/playlists/services/playlistService.ts` | 9 | API response casting |
| `utils/apiErrorHandler.ts` | 7 | Error type narrowing |
| `features/tracks/services/trackListService.ts` | 7 | API response casting |
### Configuration TypeScript
- **Mode strict complet** ✅ [tsconfig.json] : `strict: true`, `noImplicitAny`, `strictNullChecks`, `strictFunctionTypes`, `noUnusedLocals`, `noUnusedParameters`, `noImplicitReturns`, `noFallthroughCasesInSwitch`
- **`noUncheckedIndexedAccess: true`** — option avancée ✅ (peu de projets l'activent)
- **`@typescript-eslint/no-explicit-any: off`** — ESLint n'interdit pas `any` ⚠️
**Verdict TS** : Configuration stricte exemplaire mais `no-explicit-any` désactivé dans ESLint permet l'accumulation de `any`. Le comptage (~82 source + ~180 `as any`) est modéré pour un projet de 218K LOC.
---
## H6. Code mort
### Orphelins structurels identifiés
| Fichier/Dossier | Raison | Impact |
|----------------|--------|--------|
| `pages/auth/Login.tsx`, `pages/auth/Register.tsx` | Remplacés par `features/auth/pages/` | 🟡 Dead code |
| `context/AuthContext.tsx` | Remplacé par `features/auth/store/authStore.ts` | 🟠 Source de confusion |
| `providers/AuthProvider.tsx` | Wrapper de AuthContext — non utilisé dans App.tsx | 🟡 Dead code |
| `components/views/*.tsx` (fichiers plats) | Wrappers vers les sous-dossiers refactorés | 🟡 Indirection inutile |
| `ui/dropdown-menu.tsx`, `ui/tabs.tsx`, `ui/accordion.tsx` (plats) | Re-exports vers les dossiers refactorés | 🟢 Acceptable |
### Recommandation
Exécuter `npx ts-prune` ou `npx madge --extensions ts,tsx --circular src/` pour un rapport complet d'orphelins et de dépendances circulaires.
---
## H7. Dépendances inutilisées
### Potentiellement inutilisées
| Dépendance | Status | Raison |
|-----------|--------|--------|
| `@dnd-kit/utilities` | ⚠️ À vérifier | Peut être importé indirectement par `@dnd-kit/sortable` |
| `swagger-ui-dist` | ⚠️ À vérifier | `swagger-ui-react` pourrait l'importer en interne |
| `rollup-plugin-visualizer` | ✅ OK | Utilisé dans vite.config.ts (pas dans src/) |
[DONNÉES INSUFFISANTES — nécessite `npx depcheck` pour un rapport exhaustif]
---
## Priorisation de la dette
| Priorité | Élément | Impact | Effort | Ratio |
|----------|---------|--------|--------|-------|
| P0 | Split `client.ts` (2237L) | Maintenabilité, testabilité | L | Élevé |
| P0 | Résoudre dualité AuthContext vs authStore | Bugs auth, confusion | M | Très élevé |
| P1 | Supprimer `pages/auth/` legacy | Clarté code | S | Élevé |
| P1 | Unifier toast API (addToast vs toast) | Cohérence DX | M | Élevé |
| P1 | Split `usePlaylist.ts` (631L) | Maintenabilité | M | Moyen |
| P1 | Résoudre layout/Sidebar vs ui/Sidebar | Clarté | S | Élevé |
| P2 | Migrer z-[N] vers tokens SUMI | Cohérence design | M | Moyen |
| P2 | Ajouter `React.memo` sur les composants de liste | Performance | S | Moyen |
| P2 | Réduire `as any` dans client.ts (48 occ.) | Type safety | L | Faible |
| P2 | Nettoyer components/views/ wrappers legacy | Clarté | M | Moyen |
**Score dette technique implicite : 6/10** — Dette structurelle significative (migration incomplète) mais codebase fonctionnelle avec de bons patterns.