diff --git a/apps/web/.env.local b/apps/web/.env.local deleted file mode 100644 index 387f6d65e..000000000 --- a/apps/web/.env.local +++ /dev/null @@ -1,11 +0,0 @@ -# Configuration API pour développement local -# --- DOMAIN (single source of truth for frontend) --- -# Change this to switch the domain. Must match APP_DOMAIN in backend .env. -VITE_DOMAIN=veza.fr - -# Backend Go tourne sur le port 8080 (proxy via Vite, same-origin) -VITE_API_URL=/api/v1 -# WebSocket / Stream — derived from VITE_DOMAIN if omitted. -# Uncomment to override: -# VITE_WS_URL=ws://veza.fr:8081/ws -# VITE_STREAM_URL=ws://veza.fr:8082/stream diff --git a/apps/web/.env.production b/apps/web/.env.production deleted file mode 100644 index 8c90d3cde..000000000 --- a/apps/web/.env.production +++ /dev/null @@ -1,47 +0,0 @@ -# ============================================================================= -# VEZA FRONTEND - PRODUCTION CONFIGURATION -# ============================================================================= -# This file contains production-specific environment variables for the frontend -# These URLs should point to your production backend API -# ============================================================================= - -# --- API URLs (ABSOLUTE) --- -# Production backend API URL (must be absolute, not relative) -# Example: https://api.veza.com/api/v1 -VITE_API_URL=https://api.veza.com/api/v1 - -# WebSocket URL for real-time features -# Example: wss://api.veza.com/ws -VITE_WS_URL=wss://api.veza.com/ws - -# Stream server URL for media streaming -# Example: https://api.veza.com/stream -VITE_STREAM_URL=https://api.veza.com/stream - -# Upload server URL for file uploads -# Example: https://api.veza.com/upload -VITE_UPLOAD_URL=https://api.veza.com/upload - -# --- API VERSION --- -# API version to use (sent in X-API-Version header) -VITE_API_VERSION=v1 - -# --- FEATURE FLAGS --- -# Enable/disable features in production -VITE_ENABLE_VALIDATION_ALERTING=true - -# ============================================================================= -# DEPLOYMENT NOTES -# ============================================================================= -# -# For local testing with production build: -# 1. Update URLs above to match your local setup (e.g., http://veza.com:8080/api/v1) -# 2. Build: npm run build -# 3. Preview: npm run preview -# -# For actual production deployment: -# 1. Update URLs to real production domains -# 2. Ensure backend CORS allows your frontend domain -# 3. Use HTTPS for all URLs (except local testing) -# -# ============================================================================= diff --git a/apps/web/.gitignore b/apps/web/.gitignore index 173a85420..72b201001 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -1,3 +1,7 @@ +# Environment files (may contain secrets) +.env.local +.env.production + # Test / E2E artifacts (generated by Playwright visual tests) e2e/test-results-visual/ e2e/playwright-report-visual/ diff --git a/apps/web/dev_audit/frontend/00_frontend_overview.md b/apps/web/dev_audit/frontend/00_frontend_overview.md index 76db16ef1..4f1715d45 100644 --- a/apps/web/dev_audit/frontend/00_frontend_overview.md +++ b/apps/web/dev_audit/frontend/00_frontend_overview.md @@ -1,192 +1,229 @@ -# PHASE 0 — CARTOGRAPHIE FRONTEND +# Phase 0 — Frontend Overview -> **Date d'audit** : 12 février 2026 -> **Projet** : Veza — Plateforme audio collaborative -> **Scope** : `apps/web/src/` uniquement +**Date** : 2026-02-12 +**Auditeur** : Audit automatisé exhaustif +**Scope** : `apps/web/src/` exclusivement --- -## 1. Statistiques brutes +## Statistiques brutes -| Métrique | Valeur | -|----------|--------| -| Fichiers `.tsx` | **1 450** | -| Fichiers `.ts` | **620** | -| Fichiers `.css` | **17** | -| Fichiers `.scss` | **0** | -| Fichiers `.module.css` | **0** | -| Fichiers `.svg` | **5** | -| **Total fichiers source** | **~2 092** | -| **Total LOC (ts+tsx+css)** | **222 717** | -| Fichiers > 300 lignes | **~50** (dont 7 123 LOC pour `types/generated/api.ts`) | -| Fichiers > 500 lignes | **~28** | -| `as any` casts | **706** | -| `@ts-ignore` / `@ts-expect-error` | **8** | -| `dangerouslySetInnerHTML` | **2** occurrences | -| `eval()` / `new Function()` | **0** | -| `localStorage` / `sessionStorage` accès | **~20** occurrences | -| Inline styles (`style={{}}`) | **~80** fichiers | -| TODO / FIXME / HACK | **~15** annotations actives | -| `console.log/debug/info` | **~55** occurrences (hors tests) | +### Répartition des fichiers + +| Type | Nombre | +|------|--------| +| `.tsx` | 1 450 | +| `.ts` | 621 | +| `.css` | 5 | +| `.scss` | 0 | +| `.module.css` | 0 | +| `.svg` | 5 | +| **Total fichiers** | **2 081** | +| **LOC total** | **~218 500** | + +### Fichiers > 300 lignes (source uniquement, hors tests) + +| Fichier | Lignes | Nature | +|---------|--------|--------| +| `src/types/generated/api.ts` | 7 123 | Types auto-générés OpenAPI | +| `src/services/api/client.ts` | 2 237 | Client HTTP centralisé | +| `src/mocks/handlers.ts` | 1 716 | MSW mock handlers | +| `src/features/tracks/api/trackApi.ts` | 848 | API tracks | +| `src/utils/optimisticUpdates.ts` | 682 | Logique optimistic updates | +| `src/features/streaming/services/playbackAnalyticsService.ts` | 656 | Analytics streaming | +| `src/features/playlists/hooks/usePlaylist.ts` | 631 | Hook playlist principal | +| `src/utils/apiErrorHandler.ts` | 578 | Gestion erreurs API | +| `src/features/streaming/hooks/usePlaybackRealtime.ts` | 496 | Hook streaming temps réel | +| `src/services/api/auth.ts` | 493 | Service auth API | +| `src/schemas/apiRequestSchemas.ts` | 476 | Schémas Zod requêtes | +| `src/schemas/apiSchemas.ts` | 468 | Schémas Zod réponses | +| `src/features/tracks/services/trackService.ts` | 453 | Service tracks | +| `src/features/playlists/services/playlistService.ts` | 448 | Service playlists | +| `src/utils/sanitize.ts` | 429 | Sanitization XSS | + +**Observations** : +- 15 fichiers source > 400 lignes (hors tests/generated). Les plus critiques sont `client.ts` (2237L) et `trackApi.ts` (848L). +- Les fichiers de tests sont nombreux à dépasser 400L (ex: `TrackUpload.test.tsx` 783L, `playlistService.test.ts` 780L), signe de tests relativement exhaustifs. +- Le fichier `api.ts` généré (7123L) est attendu pour des types OpenAPI. --- -## 2. Stack détectée +## Stack détectée | Couche | Technologie | Version | |--------|-------------|---------| -| Framework UI | React | ^18.2.0 | -| Bundler | Vite | ^7.1.5 | -| Langage | TypeScript | ^5.3.3 (strict mode ON, noImplicitAny, noUncheckedIndexedAccess) | -| CSS | Tailwind CSS v4 | ^4.0.0 (CSS-first config via `@theme`) | -| State management | Zustand | ^4.5.0 | -| Server state | TanStack React Query | ^5.17.0 | -| Routing | React Router DOM | ^6.22.0 | -| Forms | React Hook Form + Zod | ^7.49.3 / ^3.25.76 | -| HTTP client | Axios | ^1.13.5 | -| Animations | Framer Motion | ^12.29.2 | -| Icons | Lucide React | ^0.321.0 | -| i18n | i18next + react-i18next | ^25.5.2 / ^15.7.3 | -| Mocking | MSW | ^2.11.2 | -| Tests | Vitest + Testing Library | ^3.2.4 / ^14.2.1 | -| Storybook | Storybook | ^8.6.15 | -| Linting | ESLint 9 flat config + jsx-a11y | ^9.0.0 / ^6.10.2 | -| Sanitization | DOMPurify | ^3.3.0 | -| Error monitoring | Sentry | ^10.32.1 | -| Drag & Drop | dnd-kit | ^6.3.1 | -| Virtualization | TanStack Virtual | ^3.13.12 | -| Streaming | HLS.js | ^1.6.14 | +| **Framework** | React | 18.2.x | +| **Bundler** | Vite | 7.1.x | +| **Langage** | TypeScript | 5.3.x (strict mode complet) | +| **CSS** | Tailwind CSS | v4.0 (CSS-first config) | +| **Design System** | SUMI v2.0 | Custom, CSS variables | +| **State global** | Zustand | 4.5.x | +| **Server state** | TanStack React Query | 5.17.x | +| **Routing** | React Router DOM | 6.22.x | +| **Formulaires** | React Hook Form + Zod | 7.49.x / 3.25.x | +| **Animation** | Framer Motion | 12.29.x | +| **HTTP** | Axios | 1.13.x | +| **i18n** | i18next + react-i18next | 25.5.x / 15.7.x | +| **Monitoring** | Sentry | 10.32.x | +| **Icônes** | Lucide React | 0.321.x | +| **Tests unitaires** | Vitest | 3.2.x | +| **Tests E2E** | Playwright | 1.58.x | +| **Storybook** | Storybook | 8.6.x | +| **Mocking** | MSW | 2.11.x | +| **Linting** | ESLint 9 (flat config) + Prettier | 9.x / 3.2.x | +| **Accessibility** | eslint-plugin-jsx-a11y | 6.10.x | +| **Virtualisation** | TanStack Virtual | 3.13.x | +| **DnD** | @dnd-kit | 6.3.x | +| **Toast** | react-hot-toast | 2.6.x | --- -## 3. Dépendances critiques — Observations +## Dépendances notables -| Dépendance | Observation | -|------------|-------------| -| `@types/dompurify` | Listé en `dependencies` au lieu de `devDependencies` — erreur de classification | -| `rollup-plugin-visualizer` | Listé en `dependencies` au lieu de `devDependencies` — inclus inutilement en runtime | -| `swagger-ui-dist` + `swagger-ui-react` | En production deps — potentiellement ~2MB de bundle si non tree-shaken / lazy-loaded | -| `emoji-picker-react` | Dépendance lourde (~200KB) — nécessite lazy loading | -| `framer-motion` | ^12.29.2 — dépendance majeure, impacte le bundle (~30KB min) | -| `lucide-react` | ^0.321.0 — version ancienne (2024), dernière version > 0.450+ | +### Production (critiques) +- `dompurify` 3.3.x — sanitization HTML (bon signe sécurité) +- `hls.js` 1.6.x — streaming HLS +- `immer` 10.x — immutabilité state +- `zod` 3.25.x — validation schemas +- `emoji-picker-react` 4.16.x — feature chat +- `swagger-ui-react` 5.31.x — documentation API embarquée + +### Dev (notables) +- `@storybook/addon-a11y` — audit accessibilité intégré +- `pa11y-ci` — CI accessibility testing +- `backstopjs` — visual regression testing +- `pixelmatch` / `pngjs` — visual diff +- `storybook-dark-mode` — dark mode Storybook +- `tw-animate-css` — animations Tailwind --- -## 4. Schéma d'arborescence commenté (2 niveaux) +## Arborescence commentée (2 niveaux) ``` src/ -├── __tests__/ # Tests globaux (accessibility, contrast) -├── app/ # Point d'entrée app (App.tsx probable) -├── components/ # Composants "legacy" / partagés (très large — 40+ sous-dossiers) -│ ├── admin/ # Vue admin -│ ├── analytics/ # Graphiques analytics -│ ├── auth/ # Composants auth -│ ├── base/ # Composants de base -│ ├── charts/ # Charts custom -│ ├── commerce/ # Commerce / e-commerce -│ ├── dashboard/ # Dashboard -│ ├── data/ # Composants data (tables) +├── app/ # Point d'entrée App, shell principal +├── components/ # Composants partagés (UI, layout, domain) +│ ├── admin/ # Vues administration +│ ├── analytics/ # Composants analytics +│ ├── auth/ # Composants auth (ProtectedRoute) +│ ├── charts/ # Composants graphiques +│ ├── commerce/ # Cart, wishlist +│ ├── dashboard/ # Dashboard widgets │ ├── developer/ # Swagger UI, API keys -│ ├── education/ # Cours, formations -│ ├── feedback/ # Toast, notifications -│ ├── filters/ # Filtres -│ ├── forms/ # Form builder, password strength +│ ├── education/ # Cours, learning +│ ├── feedback/ # Toast, progress +│ ├── filters/ # Filtres, tri +│ ├── forms/ # Form primitives │ ├── gamification/ # XP, achievements -│ ├── inventory/ # Gestion d'inventaire +│ ├── inventory/ # Inventaire │ ├── keyboard/ # Raccourcis clavier │ ├── layout/ # DashboardLayout, Sidebar, Header, Navbar -│ ├── library/ # Bibliothèque (playlists) -│ ├── live/ # Streaming live -│ ├── marketplace/ # Place de marché +│ ├── library/ # Playlists, watermark +│ ├── live/ # Live streaming +│ ├── marketplace/ # Marketplace cards │ ├── modals/ # Modales partagées -│ ├── monitoring/ # Dashboard monitoring +│ ├── monitoring/ # Monitoring dashboard │ ├── navigation/ # Breadcrumbs -│ ├── notifications/ # Notifications -│ ├── player/ # Audio player (ancien) +│ ├── notifications/ # Notification bell/menu +│ ├── player/ # Audio player UI │ ├── pwa/ # PWA composants -│ ├── search/ # Barre de recherche globale -│ ├── seller/ # Vue vendeur -│ ├── settings/ # Pages settings (account, security, appearance...) -│ ├── share/ # Share link manager -│ ├── social/ # Feed, groups, connections -│ ├── studio/ # Studio (projets, AI tools, go live) -│ ├── theme/ # ThemeSwitcher -│ ├── ui/ # ⭐ Composants UI primitifs (Button, Modal, Tabs, etc.) -│ ├── upload/ # Upload + metadata -│ └── views/ # Vues décomposées (analytics, cart, chat, discover, etc.) -├── config/ # Feature flags, env config -├── context/ # AuthContext -├── features/ # ⭐ Feature modules (architecture feature-first) +│ ├── search/ # Search bar, results +│ ├── seller/ # Seller dashboard +│ ├── settings/ # Settings views +│ ├── share/ # Sharing +│ ├── social/ # Social feed, groups +│ ├── studio/ # Studio projects +│ ├── theme/ # Theme provider, switcher +│ ├── ui/ # ⭐ Primitives UI (button, input, dialog, etc.) +│ ├── upload/ # Upload components +│ ├── user/ # User profile components +│ └── views/ # Feature views (analytics, cart, chat, etc.) +├── config/ # Configuration (env, features flags) +├── context/ # React Context (AuthContext) +├── features/ # ⭐ Feature modules (domain-driven) │ ├── admin/ # Admin feature -│ ├── auth/ # Auth feature (pages, components, tests) +│ ├── analytics/ # Analytics feature +│ ├── auth/ # Auth (login, register, 2FA, OAuth) │ ├── chat/ # Chat feature │ ├── dashboard/ # Dashboard feature +│ ├── error/ # Error pages +│ ├── inventory/ # Inventory feature +│ ├── library/ # Library feature +│ ├── marketplace/ # Marketplace feature │ ├── notifications/ # Notifications feature -│ ├── player/ # Audio player feature (store, hooks, services) -│ ├── playlists/ # Playlists feature (components, hooks, services, tests) +│ ├── player/ # ⭐ Player (store, hooks, services, components) +│ ├── playlists/ # Playlists feature (CRUD, collab, analytics) │ ├── profile/ # Profile feature -│ ├── roles/ # Role management feature +│ ├── roles/ # Role management │ ├── search/ # Search feature -│ ├── sessions/ # Sessions feature +│ ├── sessions/ # Sessions management │ ├── settings/ # Settings feature -│ ├── stream/ # Stream pages -│ ├── streaming/ # Streaming feature (HLS, analytics, bitrate) +│ ├── stream/ # Stream feature +│ ├── streaming/ # Streaming (HLS, playback analytics) │ ├── studio/ # Studio feature -│ ├── tracks/ # Tracks feature (le plus large) +│ ├── tracks/ # ⭐ Tracks (upload, comments, share, search) │ ├── upload/ # Upload feature -│ ├── user/ # User profile feature +│ ├── user/ # User feature │ └── webhooks/ # Webhooks feature -├── hooks/ # Hooks partagés (useOnlineStatus, useFormValidation, etc.) -├── lib/ # Utilitaires lib -├── locales/ # Fichiers i18n -├── mocks/ # MSW handlers (1 716 LOC) -├── pages/ # Pages routing (auth, marketplace) -├── providers/ # Providers React -├── router/ # Configuration routing -├── schemas/ # Schemas Zod (validation, API) -├── services/ # Services partagés (API client, websocket, cache, offline queue) -├── stores/ # Zustand stores -├── stories/ # Stories Storybook globales + assets -├── styles/ # 🔴 CSS files séparés (17 fichiers — design-system, tokens, fixes) -├── test/ # Test setup / utilities -├── types/ # Types globaux (generated API types — 7 123 LOC) -└── utils/ # Utilitaires (sanitize, error handler, optimistic updates) +├── hooks/ # Hooks partagés (useAuth, useDebounce, etc.) +├── lib/ # Librairies init (i18n, sentry) +├── locales/ # Fichiers de traduction i18n +├── mocks/ # MSW handlers +├── pages/ # Pages (auth, marketplace) — legacy? +├── providers/ # AuthProvider +├── router/ # Routing (AppRouter, config, guards) +├── schemas/ # Schémas Zod (request/response validation) +├── services/ # Services API (REST, WebSocket, storage) +├── stores/ # Zustand stores (cart, library, UI, rateLimit) +├── stories/ # Storybook decorators +├── styles/ # (si fichiers CSS additionnels) +├── test/ # Test utilities, setup +├── __tests__/ # Tests globaux (accessibility, contrast) +├── types/ # Types globaux et générés +└── utils/ # Utilitaires (sanitize, logger, toast, etc.) ``` --- -## 5. Première impression architecturale +## Patterns critiques détectés -**L'architecture présente un modèle hybride en transition** : un dossier `features/` bien structuré coexiste avec un méga-dossier `components/` hérité contenant 40+ sous-dossiers qui devraient, pour la plupart, être migrés vers les features correspondantes. Cette dualité crée une ambiguïté sur l'emplacement canonique de chaque composant (ex: `components/player/` vs `features/player/`, `components/settings/` vs `features/settings/`). - -**La stack technique est moderne et ambitieuse** : TypeScript strict, Tailwind v4 CSS-first, Zustand + React Query, Zod validation, MSW mocking, Storybook — c'est un ensemble cohérent et bien choisi pour un SaaS audio. Le design system "KODO" est formalisé via CSS variables avec ~100+ tokens, light/dark mode complet, et des utilitaires custom. L'investissement en outillage (ESLint custom rules pour les tokens, feature flags, Sentry) témoigne d'une volonté de rigueur. - -**Le volume est considérable pour un pré-MVP** : 222K LOC sur 2 092 fichiers, c'est un codebase substantiel. Les 706 `as any` casts, les ~80 fichiers avec inline styles, et la présence de nombreux `TODO` indiquent une dette technique significative sous la surface, probablement accumulée lors de phases de développement rapide. Les fichiers de plus de 500 lignes sont concentrés dans les services et tests, pas dans les composants UI — signe que la règle des 300 lignes max est globalement respectée côté UI. +| Pattern | Occurrences | Risque | +|---------|-------------|--------| +| `dangerouslySetInnerHTML` | 2 fichiers | 🟠 Moyen (chat) | +| `localStorage/sessionStorage` | ~45 fichiers | 🟡 Faible (encapsulé via `safeStorage.ts`, `tokenStorage.ts`) | +| `eval()` / `new Function()` | 0 | ✅ | +| `console.log/debug/info` | ~27 fichiers | 🟡 Faible (principalement dev/stories) | +| `: any` | ~80+ fichiers | 🟠 Moyen | +| `as any` | ~100+ fichiers (dont 145 dans `generated/api.ts`) | 🟠 Moyen | +| `@ts-ignore` / `@ts-expect-error` | 7 fichiers | 🟢 Faible | +| `style={{}}` inline | ~80 fichiers | 🟠 Moyen | +| `TODO/FIXME/HACK` | ~20 occurrences | 🟡 Normal | --- -## 6. Configuration notable +## Variables d'environnement client (VITE_*) -### TypeScript -- **Strict mode complet** activé (`strict: true`, `noImplicitAny`, `strictNullChecks`, `noUncheckedIndexedAccess`) -- Tests et mocks **exclus** de la compilation (`exclude` dans tsconfig) — bon choix -- Path aliases configurés (`@/*` → `./src/*`) +Déclarées dans `src/vite-env.d.ts` [vite-env.d.ts:4-21] : +- `VITE_API_URL`, `VITE_WS_URL`, `VITE_STREAM_URL`, `VITE_UPLOAD_URL` — endpoints API +- `VITE_APP_NAME` — nom de l'application +- `VITE_DEBUG`, `VITE_USE_MSW`, `VITE_STORYBOOK` — flags dev +- `VITE_FCM_VAPID_KEY` — push notifications +- Feature flags : `VITE_FEATURE_TWO_FACTOR_AUTH`, `VITE_FEATURE_PLAYLIST_*`, `VITE_FEATURE_HLS_STREAMING`, `VITE_FEATURE_ROLE_MANAGEMENT`, `VITE_FEATURE_NOTIFICATIONS` -### Vite -- Build avec manual chunks bien configurés (react, router, tanstack, icons, utils, vendor) -- Source maps cachées en production (`'hidden'`) -- Bundle visualizer en production -- Proxy API configuré pour le dev +Fichiers `.env` présents : +- `.env.example` (2.2KB) — template +- `.env.local` (450B) — config locale +- `.env.production` (1.8KB) — config prod +- `.env.storybook` (262B) — config Storybook +- **Attention** : `.env.local` et `.env.production` sont versionnés (visibles). `.gitignore` ne semble pas exclure les fichiers `.env.*`. -### Tailwind v4 -- Configuration CSS-first via `@theme inline` dans `index.css` -- Pas de `tailwind.config.ts` significatif (fichier quasi vide, délègue à CSS) -- 17 fichiers CSS dans `src/styles/` — fragmentation notable +--- -### ESLint -- Configuration flat config (ESLint 9) -- Plugin `jsx-a11y` actif -- **`@typescript-eslint/no-explicit-any: 'off'`** — ⚠️ `any` autorisé explicitement -- Rules custom pour enforcer les design tokens (pas de Tailwind default colors, pas d'arbitrary values) -- Rule pour forcer ` diff --git a/apps/web/src/components/layout/Layout.tsx b/apps/web/src/components/layout/Layout.tsx index 19be07260..22b2a72cd 100644 --- a/apps/web/src/components/layout/Layout.tsx +++ b/apps/web/src/components/layout/Layout.tsx @@ -35,6 +35,7 @@ export function Layout({ children }: LayoutProps) {
= ({ onNavigate, onLogout }) => { {/* Backdrop for closing menus - Lower z-index than Navbar (z-40) but higher than content */} {showUserMenu && (
setShowUserMenu(false)} /> )} diff --git a/apps/web/src/components/layout/PageTransition.tsx b/apps/web/src/components/layout/PageTransition.tsx index d2dde93ad..7ccdb7c8d 100644 --- a/apps/web/src/components/layout/PageTransition.tsx +++ b/apps/web/src/components/layout/PageTransition.tsx @@ -1,14 +1,29 @@ import { motion } from 'framer-motion'; import { useLocation } from 'react-router-dom'; -import { ReactNode } from 'react'; +import { ReactNode, useMemo } from 'react'; interface PageTransitionProps { children: ReactNode; } +/** + * S5.3: Subtile page transitions (Linear-style fade + slide-up) + * Respects prefers-reduced-motion for accessibility. + */ export function PageTransition({ children }: PageTransitionProps) { const location = useLocation(); + const prefersReducedMotion = useMemo( + () => + typeof window !== 'undefined' && + window.matchMedia('(prefers-reduced-motion: reduce)').matches, + [], + ); + + if (prefersReducedMotion) { + return
{children}
; + } + return ( }, - { id: 'studio', label: 'Cloud Files', icon: }, - { id: 'tracks', label: 'Projects', icon: }, - { id: 'gear', label: 'Gear Locker', icon: }, - { id: 'analytics', label: 'Performance', icon: }, - ], - }, - { - section: 'Veza Network', - items: [ - { id: 'social', label: 'Community Feed', icon: }, - { id: 'marketplace', label: 'Marketplace', icon: }, - { id: 'live', label: 'Live Sessions', icon: , badge: 3 }, - { id: 'chat', label: 'Channels', icon: , badge: 12 }, - { id: 'education', label: 'Academy', icon: }, - ], - }, - { - section: 'Commerce', - items: [ - { id: 'sell', label: 'Seller Dashboard', icon: }, - { id: 'wishlist', label: 'Wishlist', icon: }, - { id: 'purchases', label: 'Purchases', icon: }, - ], - }, - { - section: 'Library', - items: [ - { id: 'playlists', label: 'Playlists', icon: }, - { id: 'queue', label: 'Play Queue', icon: }, - ], - }, - { - section: 'System', - items: [ - { id: 'developer', label: 'Developer API', icon: }, - { id: 'admin', label: 'Admin Panel', icon: }, - ], - }, +// Section key mapping for i18n +const sectionKeys: Record = { + myStudio: 'nav.sections.myStudio', + vezaNetwork: 'nav.sections.vezaNetwork', + commerce: 'nav.sections.commerce', + library: 'nav.sections.library', + system: 'nav.sections.system', +}; + +// Icon map — static, does not need translation +const iconMap: Record = { + dashboard: , + studio: , + tracks: , + gear: , + analytics: , + social: , + marketplace: , + live: , + chat: , + education: , + sell: , + wishlist: , + purchases: , + playlists: , + queue: , + developer: , + admin: , +}; + +// Badge data — static +const badgeMap: Record = { live: 3, chat: 12 }; + +// Navigation structure definition (ids only, labels resolved via t()) +const navStructure: { sectionKey: string; itemIds: string[] }[] = [ + { sectionKey: 'myStudio', itemIds: ['dashboard', 'studio', 'tracks', 'gear', 'analytics'] }, + { sectionKey: 'vezaNetwork', itemIds: ['social', 'marketplace', 'live', 'chat', 'education'] }, + { sectionKey: 'commerce', itemIds: ['sell', 'wishlist', 'purchases'] }, + { sectionKey: 'library', itemIds: ['playlists', 'queue'] }, + { sectionKey: 'system', itemIds: ['developer', 'admin'] }, ]; +function buildNavItems(t: (key: string) => string): { section: string; items: NavItem[] }[] { + return navStructure.map(({ sectionKey, itemIds }) => ({ + section: t(sectionKeys[sectionKey] ?? sectionKey), + items: itemIds.map((id) => ({ + id, + label: t(`nav.items.${id}`), + icon: iconMap[id], + ...(badgeMap[id] != null ? { badge: badgeMap[id] } : {}), + })), + })); +} + const routeMap: Record = { dashboard: '/dashboard', studio: '/library', tracks: '/library', gear: '/gear', analytics: '/analytics', social: '/social', marketplace: '/marketplace', live: '/live', @@ -81,9 +91,11 @@ const navItemInactiveClasses = const navItemActiveClasses = 'bg-primary/10 text-primary sidebar-active-indicator'; export const Sidebar: React.FC = ({ currentView }) => { + const { t } = useTranslation(); const location = useLocation(); const { sidebarOpen, setSidebarOpen } = useUIStore(); const { handleMobileNav, handleLogout } = useSidebarNavigation(); + const navItems = useMemo(() => buildNavItems(t), [t]); const activeView = currentView || @@ -236,7 +248,7 @@ export const Sidebar: React.FC = ({ currentView }) => { {/* Footer */}
- + = ({ currentView }) => { sidebarOpen ? 'opacity-100' : 'w-0 opacity-0 overflow-hidden' )} > - Settings + {t('nav.settings')} - + diff --git a/apps/web/src/components/library/WatermarkSettingsModal.tsx b/apps/web/src/components/library/WatermarkSettingsModal.tsx index 228a4b420..9ced3b797 100644 --- a/apps/web/src/components/library/WatermarkSettingsModal.tsx +++ b/apps/web/src/components/library/WatermarkSettingsModal.tsx @@ -32,7 +32,7 @@ export const WatermarkSettingsModal: React.FC = ({ ]; return ( -
+
= ({ }; return ( -
+
= ({ if (showDeleteConfirm) { return ( -
+
setShowDeleteConfirm(false)} @@ -70,7 +70,7 @@ export const EditPlaylistModal: React.FC = ({ } return ( -
+
+
= ({ }; return ( -
+
= ({ onAddToCart, }) => { return ( -
+
onActiveImageChange(img)} className={`w-20 h-20 rounded-xl overflow-hidden cursor-pointer border-2 transition-all duration-[var(--sumi-duration-normal)] flex-shrink-0 ${activeImage === img ? 'border-primary' : 'border-transparent opacity-60 hover:opacity-100'}`} > - + ))}
diff --git a/apps/web/src/components/modals/CreatorModal.tsx b/apps/web/src/components/modals/CreatorModal.tsx index 507fbd79b..eb219312a 100644 --- a/apps/web/src/components/modals/CreatorModal.tsx +++ b/apps/web/src/components/modals/CreatorModal.tsx @@ -30,7 +30,7 @@ export const CreatorModal: React.FC = ({ if (!isOpen) return null; return ( -
+
); } + +export const NotificationMenuItem = React.memo(NotificationMenuItemInner); +NotificationMenuItem.displayName = 'NotificationMenuItem'; diff --git a/apps/web/src/components/player/FullPlayer.tsx b/apps/web/src/components/player/FullPlayer.tsx index 242135bab..aadfce93e 100644 --- a/apps/web/src/components/player/FullPlayer.tsx +++ b/apps/web/src/components/player/FullPlayer.tsx @@ -37,7 +37,7 @@ export const FullPlayer: React.FC = ({ onClose }) => { }; return ( -
+
{/* Ambient Backdrop */}
diff --git a/apps/web/src/components/seller/create-product-view/CreateProductViewDetailsCard.tsx b/apps/web/src/components/seller/create-product-view/CreateProductViewDetailsCard.tsx index ebf194093..683780e72 100644 --- a/apps/web/src/components/seller/create-product-view/CreateProductViewDetailsCard.tsx +++ b/apps/web/src/components/seller/create-product-view/CreateProductViewDetailsCard.tsx @@ -49,7 +49,7 @@ export function CreateProductViewDetailsCard({ Description