Phase 1:
- S0: Fix open redirect (safeNavigate), delete AuthContext/legacy auth, encrypt API keys, gitignore .env files
- S1: Split client.ts god object into 5 modules, unify toast system, delete unused Sidebar
- S2: Add glass button variant, migrate 32 z-index to SUMI tokens, fix card dark mode
- S3: Skip nav link, aria-hidden on icons, focus-visible ring fixes, alt attrs, aria-live regions
- S4: React.memo on list items, fix key={index}, loading=lazy on images
- S5: Branded loading screen, page transitions respect reduced-motion, LikeButton micro-interaction, i18n sidebar/header
Phase 2 Sprint 6:
- Wire Tailwind shadow utilities to SUMI tokens in @theme block (fixes 50+ files)
- Define shadow-card/shadow-card-hover tokens
- Remove dark:shadow-none workarounds from card.tsx (SUMI handles per-theme shadows)
Co-authored-by: Cursor <cursoragent@cursor.com>
9.5 KiB
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 :
client.ts(2237L) →httpClient.ts,validators.ts,caching.ts,interceptors.ts,metrics.tsusePlaylist.ts(631L) →usePlaylistCrud.ts,usePlaylistCollaboration.ts,usePlaylistAnalytics.tsuseChat.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é parauthStore.login()ETAuthContext.login()— deux chemins d'exécution pour la même action. - Toast :
toast()(react-hot-toast) etaddToast()(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 pasany⚠️
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.