veza/apps/web/dev_audit/frontend/01_architecture_analysis.md
senke 5f88c56113 fix: UI remediation Phase 1 (S0-S5) + Phase 2 Sprint 6 shadow system
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>
2026-02-12 10:13:44 +01:00

13 KiB

Phase A — Architecture Frontend Analysis

Score Architecture : 7/10


A1. Structure des dossiers

Organisation réelle

src/
├── app/          → Shell applicatif (App.tsx)
├── components/   → Composants partagés (30+ sous-dossiers)
├── config/       → Configuration (env, features flags)
├── context/      → React Context (AuthContext, AudioContext)
├── features/     → Modules feature-based (24 features)
├── hooks/        → Hooks partagés (~30 hooks)
├── lib/          → Init librairies (i18n, sentry)
├── locales/      → Traductions i18n
├── mocks/        → MSW handlers
├── pages/        → Pages legacy (auth, marketplace)
├── providers/    → AuthProvider
├── router/       → Routing centralisé
├── schemas/      → Schémas Zod
├── services/     → Services API (~35 services)
├── stores/       → Zustand stores partagés
├── stories/      → Storybook decorators
├── test/         → Test setup
├── types/        → Types globaux et générés
└── utils/        → Utilitaires (~25 fichiers)

Séparation features/ vs components/

Positif : La codebase utilise un pattern feature-based dans features/ avec des modules autonomes contenant chacun api/, components/, hooks/, services/, store/, pages/. [features/auth/store/authStore.ts], [features/playlists/hooks/usePlaylist.ts], [features/tracks/api/trackApi.ts]

Problème majeur : Dualité non résolue entre :

  • components/views/ (analytics-view, cart-view, chat-view, discover-view, etc. — ~20 vues) et features/*/pages/ — deux patterns coexistent pour le même rôle [components/views/analytics-view/], [features/dashboard/pages/]
  • pages/auth/ (Login.tsx, Register.tsx) et features/auth/pages/ (LoginPage.tsx, RegisterPage.tsx) — duplication directe [pages/auth/Login.tsx vs features/auth/pages/LoginPage.tsx]
  • context/AuthContext.tsx et features/auth/store/authStore.tsdeux sources de vérité pour l'auth [context/AuthContext.tsx:54, features/auth/store/authStore.ts:55]
  • providers/AuthProvider.tsx et context/AuthContext.tsx — coexistence redondante

Barrel exports (index.ts)

Présents et cohérents dans les feature views refactorées :

  • components/views/analytics-view/index.ts
  • components/views/cart-view/index.ts
  • components/views/settings-view/index.ts
  • features/*/index.tsabsents dans la plupart des features

Routes colocalisées

Les routes sont centralisées dans router/routeConfig.tsx [routeConfig.tsx:57-109], pas colocalisées avec les features. C'est un choix acceptable pour un projet de cette taille, mais limite la découvrabilité.

Dossiers morts ou orphelins

  • pages/auth/probablement orphelin : contient Login.tsx et Register.tsx mais les routes pointent vers features/auth/pages/ via LazyComponent [routeConfig.tsx:59-63]
  • components/views/*.tsx (fichiers plats type AnalyticsView.tsx, CartView.tsx) — semblent être des wrappers legacy vers les sous-dossiers refactorés
  • stories/ — contient uniquement decorators.tsx, pourrait être dans .storybook/

Verdict structure : Organisation feature-based ambitieuse mais migration incomplète. La dualité components/views/ vs features/*/pages/ et les vestiges legacy (pages/, context/AuthContext.tsx) créent de la confusion. -2 points.


A2. Séparation des responsabilités

Composants > 100 lignes (source, hors tests)

Composant Lignes Type Responsabilités mélangées ? Verdict
services/api/client.ts 2 237 Service API client + validation + caching + retry + dedup + metrics Monolithe
features/tracks/api/trackApi.ts 848 Service CRUD tracks + upload + share + analytics ⚠️ Large mais cohérent
utils/optimisticUpdates.ts 682 Utilitaire Optimistic updates multi-feature ⚠️ Acceptable
features/streaming/services/playbackAnalyticsService.ts 656 Service Analytics streaming ⚠️ Complexité justifiée
features/playlists/hooks/usePlaylist.ts 631 Hook smart CRUD + collaboration + analytics Trop de responsabilités
utils/apiErrorHandler.ts 578 Utilitaire Error parsing + categorization ⚠️ Acceptable
features/streaming/hooks/usePlaybackRealtime.ts 496 Hook smart WebSocket + state + analytics ⚠️ Justifié par temps réel
services/api/auth.ts 493 Service Auth API complète ⚠️ Cohérent
schemas/apiRequestSchemas.ts 476 Types Schémas Zod Naturellement grand
schemas/apiSchemas.ts 468 Types Schémas Zod Naturellement grand
features/tracks/services/trackService.ts 453 Service Service tracks ⚠️ Cohérent
features/playlists/services/playlistService.ts 448 Service Service playlists ⚠️ Cohérent
utils/sanitize.ts 429 Utilitaire Sanitization XSS Sécurité critique

Point critique : client.ts (2237L) est un God Object. Il cumule : instance Axios, interceptors, validation Zod, caching, request deduplication, metrics tracking, offline queue, rate limiting, CSRF. Il devrait être éclaté en 4-5 modules. [services/api/client.ts:1-80]

Positif : La séparation services/hooks/components est généralement respectée dans les features refactorées (auth, playlists, tracks, streaming).


A3. Gestion d'état

Couches identifiées

1. Zustand Stores (7 stores) :

Store Fichier Contenu Persisté Sync tabs
authStore features/auth/store/authStore.ts isAuthenticated, isLoading, error localStorage broadcastSync
uiStore stores/ui.ts theme, language, sidebarOpen, notifications localStorage broadcastSync
cartStore stores/cartStore.ts items, actions CRUD localStorage
playerStore features/player/store/playerStore.ts Playback state Probable À vérifier
chatStore features/chat/store/chatStore.ts Chat state À vérifier À vérifier
libraryStore stores/library.ts Library state À vérifier À vérifier
rateLimitStore stores/rateLimit.ts Rate limit tracking

2. React Query (TanStack Query v5) — Server state principal :

  • Utilisé dans ~30+ fichiers [features/playlists/hooks/usePlaylist.ts, features/tracks/components/LikeButton.tsx, etc.]
  • useQuery pour lectures, useMutation pour écritures
  • Optimistic updates via utils/optimisticUpdates.ts [utils/optimisticUpdates.ts]
  • Cache sync cross-tabs via utils/reactQuerySync.ts [app/App.tsx:47-54]
  • QueryClient config : staleTime: 1min, gcTime: 5min, retry: false [main.tsx:43-55]

3. React Context (4 contextes) :

  • AuthContext [context/AuthContext.tsx] — CONFLIT avec authStore : deux sources de vérité pour l'auth
  • AudioContext [context/audio-context/AudioContext.tsx] — contexte audio player
  • ThemeProvider [components/theme/ThemeProvider.tsx] — thème (mais aussi géré par uiStore)
  • ToastProvider [components/feedback/ToastProvider.tsx] — gestion toasts

4. State local (useState) — usage standard dans les composants

Diagnostique

Conflit critique : AuthContext [context/AuthContext.tsx:54] utilise authService directement avec useState pour user, tandis que authStore [features/auth/store/authStore.ts:55] utilise Zustand avec loginService et gère isAuthenticated sans user (délégué à React Query). L'App.tsx utilise authStore [app/App.tsx:4], mais AuthContext existe toujours et pourrait être importé par erreur. → Source de bugs potentiels.

Duplication thème : Le thème est géré par uiStore [stores/ui.ts:35-51] ET ThemeProvider [components/theme/ThemeProvider.tsx]. L'App.tsx applique le thème via uiStore [app/App.tsx:80-94].

Prop drilling : Minimisé grâce à Zustand + React Query. Pas de chaîne > 3 niveaux identifiée dans le code audité.


A4. Gestion des requêtes réseau

Architecture réseau

Le projet utilise un client Axios centralisé dans services/api/client.ts [services/api/client.ts:1-80] qui fournit :

  • Interceptors pour JWT (Authorization header) [client.ts:9]
  • Refresh token automatique [client.ts:10]
  • Validation Zod des requêtes et réponses [client.ts:24-25]
  • Request deduplication [client.ts:21]
  • Response caching [client.ts:22]
  • Offline queue [client.ts:20]
  • Rate limiting tracking [client.ts:27]
  • CSRF protection [client.ts:14]
  • Validation metrics [client.ts:33-41]

Patterns observés

Fichier Méthode Loading Error Cancel Typé Centralisé
features/tracks/api/trackApi.ts Axios client Via React Query Via React Query Via React Query Zod
services/api/auth.ts Axios client Manual/Store parseApiError Zod
features/playlists/services/playlistService.ts Axios client Via hooks
services/websocket.ts WebSocket natif reconnect N/A ⚠️ any (8x)
context/AuthContext.tsx authService direct useState toast any (4x) ⚠️ Parallèle

Positif : Architecture réseau très mature avec dedup, caching, offline queue, validation Zod end-to-end, CSRF. C'est au-dessus de la moyenne.

Négatif : Le client.ts est un monolithe de 2237L qui concentre trop de responsabilités. L'AbortController n'est pas systématiquement utilisé pour annuler les requêtes au démontage.


A5. Routing

  • Solution : React Router DOM v6.22 [package.json]
  • Routes protégées : ProtectedRoute component wrapper [routeConfig.tsx:47-55] + ProtectedLayoutRoute pour le layout dashboard
  • Lazy loading : Toutes les routes sont lazy via LazyComponent [routeConfig.tsx:5-33] — pattern React.lazy centralisé
  • 404 : LazyNotFound route + catch-all */404 [AppRouter.tsx:30-31]
  • 500 : LazyServerError route [routeConfig.tsx:107]
  • Deep linking : Routes paramétrées (/tracks/:id, /u/:username, /playlists/*) [routeConfig.tsx:87-88]
  • ComingSoon : Routes planifiées mais non implémentées utilisent un placeholder ComingSoon [routeConfig.tsx:96-101] — propre
  • v7 migration : Flags de préparation activés v7_startTransition, v7_relativeSplatPath [main.tsx:221-222]

Verdict routing : Solide, bien structuré, lazy loading systématique. +1 point.


A6. Gestion des erreurs

Error Boundaries

  • Root level : ErrorBoundary wrapping App [app/App.tsx:171]
  • Route level : Chaque route wrappée dans ErrorBoundary [routeConfig.tsx:40-55]
  • Composant : ErrorBoundary class component avec UI de fallback, retry et go-home [components/ui/ErrorBoundary.tsx:16-77]
  • Fallback configurable : via prop fallback [ErrorBoundary.tsx:13]

Erreurs réseau

  • parseApiError centralisé [utils/apiErrorHandler.ts] — 578L, parsing exhaustif des erreurs Axios
  • formatUserFriendlyError [utils/errorMessages.ts] — messages user-friendly
  • Toast pour feedback utilisateur [utils/toast.ts]
  • Rate limit indicator [components/RateLimitIndicator.tsx]
  • Offline indicator [components/OfflineIndicator.tsx]

Logging

  • Sentry intégré [lib/sentry.ts, main.tsx:28,41]
  • Logger structuré custom [utils/logger.ts] avec niveaux et contexte

Fallback UI

  • Error states avec ErrorDisplay component [components/ui/ErrorDisplay.tsx]
  • Loading states avec spinners et skeletons (chaque view a un Skeleton)
  • Empty states gérés dans les features refactorées

Verdict erreurs : Gestion des erreurs très complète — error boundaries, parsing centralisé, Sentry, toasts, offline detection. +1 point.


Score Architecture détaillé

Critère Points Justification
Structure feature-based +2 Organisation claire en features avec séparation concerns
Migration incomplète -2 Dualité components/views vs features/pages, fichiers legacy
State management +1.5 Zustand + React Query bien intégré, sync cross-tabs
Conflit AuthContext vs authStore -1 Deux sources de vérité pour l'auth
Client API centralisé +1.5 Validation Zod, dedup, caching, offline queue
client.ts monolithe -0.5 2237L, trop de responsabilités
Routing +1.5 Lazy loading systématique, 404/500, guards
Error handling +1.5 ErrorBoundary, Sentry, toast, offline indicator
Barrel exports partiels -0.5 Incohérents entre features et components
TypeScript strict +1 Mode strict complet avec noUncheckedIndexedAccess
Total 7/10 Solide, mais dette structurelle de migration

Résumé : L'architecture est ambitieuse et globalement solide, avec des patterns modernes (feature-based, server state séparé, validation Zod). Le principal problème est la migration incomplète qui laisse coexister deux patterns architecturaux (components/views/ vs features/pages/, AuthContext vs authStore). Le client HTTP centralisé est puissant mais devenu un monolithe à découper.