chore(dx): add .cursorrules and design system audit documentation
48
apps/web/.storybook/AppProvidersForStorybook.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
import React from 'react';
|
||||
import { ThemeProvider } from '../src/components/theme/ThemeProvider';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { ToastProvider } from '../src/components/feedback/ToastProvider';
|
||||
import { AudioProvider } from '../src/context/AudioContext';
|
||||
import { AuthProvider } from '../src/context/AuthContext';
|
||||
|
||||
// Create a singleton query client for Storybook to share cache if needed,
|
||||
// or one per story. Since decorators run per story, creating it here might
|
||||
// share it. Better to create inside if we want isolation, but for performance
|
||||
// reuse is okay.
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
staleTime: Infinity,
|
||||
// Phase 1: Silence network errors in react-query
|
||||
throwOnError: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
interface AppProvidersProps {
|
||||
children: React.ReactNode;
|
||||
isDark?: boolean;
|
||||
}
|
||||
|
||||
export const AppProvidersForStorybook: React.FC<AppProvidersProps> = ({ children, isDark = true }) => {
|
||||
return (
|
||||
<ThemeProvider defaultTheme={isDark ? 'dark' : 'light'}>
|
||||
<div className={isDark ? 'dark' : ''} style={{ minHeight: '100vh', padding: '1rem', background: isDark ? '#0a0a0a' : '#ffffff', color: isDark ? '#ffffff' : '#0a0a0a' }}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ToastProvider>
|
||||
<AudioProvider>
|
||||
<AuthProvider>
|
||||
<MemoryRouter>
|
||||
{children}
|
||||
</MemoryRouter>
|
||||
</AuthProvider>
|
||||
</AudioProvider>
|
||||
</ToastProvider>
|
||||
</QueryClientProvider>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
48
apps/web/STORYBOOK_AUDIT_REPORT.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Storybook Audit Report
|
||||
|
||||
## Summary
|
||||
- **Total Stories**: 963
|
||||
- **Failed Stories**: 310 (~32% failure rate)
|
||||
- **Total Errors**: 1,040 (Network + Console)
|
||||
|
||||
## Failure Categories
|
||||
|
||||
### 1. Unmocked Network Requests (Critical)
|
||||
**Count**: 463 Network Failures
|
||||
**Diagnosis**: Stories are attempting to hit real production endpoints (`api.veza.com`) instead of using mocks.
|
||||
- `ERR_NAME_NOT_RESOLVED`: `https://api.veza.com/api/v1/api/v1/logs/frontend`
|
||||
- `ERR_ABORTED`: `https://api.veza.com/api/v1/auth/me`
|
||||
- `ERR_ABORTED`: `https://picsum.photos/200` (External images)
|
||||
|
||||
**Impact**: Components depending on data stay in "Loading" state indefinitely or crash when data is missing.
|
||||
|
||||
### 2. Missing Context Providers
|
||||
**Count**: ~15-20 identified instances (estimated from sample)
|
||||
**Diagnosis**: Components accessing global state that isn't provided in the story decorator.
|
||||
- `Error: useAuth must be used within AuthProvider`
|
||||
- `TypeError: Cannot read properties of undefined (reading 'name')` (Likely missing user object from auth context)
|
||||
|
||||
### 3. Runtime Crashes
|
||||
- `TypeError: Cannot read properties of undefined`: Cascading failures from missing API data.
|
||||
|
||||
## Remediation Plan
|
||||
|
||||
### Step 1: Configure Mock Service Worker (MSW)
|
||||
- [ ] Initialize MSW in Storybook.
|
||||
- [ ] Create `handlers.ts` to mock core endpoints:
|
||||
- `/auth/me` (User profile)
|
||||
- `/api/v1/logs/frontend` (Prevent logging noise)
|
||||
- Domain specific endpoints for failing stories.
|
||||
|
||||
### Step 2: Global Decorators
|
||||
- [ ] Wrap all stories in `preview.tsx` with:
|
||||
- `AuthProvider` (Mocked)
|
||||
- `QueryClientProvider` (React Query)
|
||||
- `MemoryRouter` (already likely present but need verification)
|
||||
|
||||
### Step 3: Fix Specific Issues
|
||||
- [ ] Fix double-path URL bug (`/api/v1/api/v1/...`)
|
||||
- [ ] Mock external image services or provide local fallbacks.
|
||||
|
||||
## Priority
|
||||
High. 30% of the component library is broken in documentation, making it unreliable for development.
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{j as e}from"./vendor-react-BHG7lGYR.js";import{L as i}from"./vendor-router-D-s5vIeO.js";import{d as n}from"./index-BWcMVITa.js";function m({title:t,subtitle:r,children:l,footerLinks:o,className:a}){return e.jsxs("div",{className:n("min-h-screen flex items-center justify-center bg-kodo-void py-12 px-4 sm:px-6 lg:px-8 relative overflow-hidden",a),role:"main","aria-label":"Page d'authentification",children:[e.jsxs("div",{className:"absolute inset-0 overflow-hidden pointer-events-none",children:[e.jsx("div",{className:"absolute top-0 right-0 w-96 h-96 bg-kodo-cyan/5 rounded-full blur-3xl"}),e.jsx("div",{className:"absolute bottom-0 left-0 w-96 h-96 bg-kodo-magenta/5 rounded-full blur-3xl"})]}),e.jsxs("div",{className:"max-w-2xl w-full space-y-8 relative z-10 animate-fade-in",children:[e.jsxs("header",{className:"text-center",children:[e.jsxs("div",{className:"flex items-center justify-center mb-6",children:[e.jsx("div",{className:"h-12 w-12 rounded-xl bg-gradient-to-br from-kodo-cyan to-kodo-cyan-dim flex items-center justify-center shadow-glow-cyan","aria-hidden":"true",children:e.jsx("span",{className:"text-kodo-void font-bold text-2xl",children:"V"})}),e.jsx("span",{className:"ml-3 font-bold text-3xl text-white",children:"Veza"})]}),e.jsx("h1",{id:"auth-form-title",className:"text-4xl font-bold text-white mb-2",children:t}),r&&e.jsx("p",{className:"text-sm text-kodo-secondary",role:"doc-subtitle",children:r})]}),e.jsx("section",{className:"glass rounded-2xl border border-white/10 py-8 px-6 sm:px-8 shadow-2xl backdrop-blur-xl w-full","aria-labelledby":"auth-form-title",children:l}),o&&o.length>0&&e.jsx("nav",{className:"text-center space-x-4","aria-label":"Navigation d'authentification",children:o.map(s=>e.jsx(i,{to:s.to,className:"text-sm text-kodo-steel dark:text-kodo-steel hover:text-white dark:hover:text-white transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 rounded",children:s.label},s.to))})]})]})}function u({loading:t,variant:r="primary",className:l,children:o,disabled:a,...s}){return e.jsx("button",{className:n("w-full px-4 py-2 rounded-lg font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",r==="primary"?"bg-kodo-cyan text-white hover:bg-kodo-cyan focus:ring-blue-500 dark:bg-kodo-cyan dark:hover:bg-kodo-cyan":"bg-kodo-slate text-kodo-text-main hover:bg-kodo-slate focus:ring-gray-500 dark:bg-kodo-steel dark:text-kodo-text-main dark:hover:bg-kodo-steel",(a||t)&&"opacity-50 cursor-not-allowed",l),disabled:a||t,"aria-busy":t,"aria-disabled":a||t?"true":"false",...s,children:t?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"sr-only",children:"Chargement en cours"}),e.jsx("span",{"aria-hidden":"true",children:"Chargement..."})]}):o})}export{m as A,u as a};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{j as e}from"./vendor-react-BHG7lGYR.js";import{g as r,B as o}from"./index-BWcMVITa.js";import{K as i,b6 as a,b7 as l}from"./vendor-icons-DaGlTw4_.js";import"./vendor-IYr-MHu4.js";import"./vendor-router-D-s5vIeO.js";import"./vendor-tanstack-BzWBL1hV.js";import"./vendor-utils-CgOSfOkx.js";function u(){const t=r(),s=()=>{t.info("Course browsing feature coming soon")};return e.jsxs("div",{className:"min-h-screen p-8 space-y-8",children:[e.jsx("div",{className:"glass-hud rounded-2xl border-white/10 p-8 hud-corner",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("div",{className:"w-12 h-12 rounded-xl bg-kodo-lime/20 flex items-center justify-center border border-kodo-lime/30",children:e.jsx(i,{className:"w-6 h-6 text-kodo-lime"})}),e.jsxs("div",{children:[e.jsx("h1",{className:"text-3xl font-display font-bold text-white",children:"Veza Academy"}),e.jsx("p",{className:"text-sm text-white opacity-80",children:"Learn production, mixing, and more"})]})]}),e.jsx(o,{onClick:s,className:"bg-kodo-lime hover:bg-kodo-lime/80 text-black",children:"Browse Courses"})]})}),e.jsxs("div",{className:"glass-hud rounded-xl border-white/10 p-12 text-center",children:[e.jsx(a,{className:"w-16 h-16 text-kodo-secondary mx-auto mb-4 opacity-50"}),e.jsx("h3",{className:"text-lg font-bold text-white mb-2",children:"Start Learning"}),e.jsx("p",{className:"text-sm text-white opacity-90 mb-4",children:"Explore courses from industry professionals"}),e.jsxs(o,{onClick:s,variant:"outline",className:"border-kodo-lime/30 text-kodo-lime hover:bg-kodo-lime/10",children:[e.jsx(l,{className:"w-4 h-4 mr-2"}),"View Courses"]})]})]})}export{u as EducationPage};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as n,j as i}from"./vendor-react-BHG7lGYR.js";import{L as h}from"./vendor-router-D-s5vIeO.js";import{A as b,a as g}from"./AuthButton-ByJY1yM3.js";import{u as j,A as y}from"./usePasswordReset-CY5zVo2R.js";import"./vendor-IYr-MHu4.js";import"./index-BWcMVITa.js";import"./vendor-tanstack-BzWBL1hV.js";import"./vendor-utils-CgOSfOkx.js";import"./vendor-icons-DaGlTw4_.js";function B(){const{handleRequestReset:m,loading:d,error:l,success:u}=j(),[t,c]=n.useState({email:""}),[r,o]=n.useState({}),p=()=>{const e={};return t.email?/\S+@\S+\.\S+/.test(t.email)||(e.email="Email invalide"):e.email="Email requis",o(e),Object.keys(e).length===0},x=(e,a)=>{c({...t,[e]:a}),r[e]&&o({...r,[e]:void 0})},v=e=>{const a=t[e];let s;a?/\S+@\S+\.\S+/.test(a)||(s="Email invalide"):s="Email requis",o(s?{...r,[e]:s}:{...r,[e]:void 0})},f=async e=>{e.preventDefault(),p()&&await m(t)};return i.jsx(b,{title:"Mot de passe oublié",subtitle:"Entrez votre email pour recevoir un lien de réinitialisation",footerLinks:[{label:"Retour à la connexion",to:"/login"}],children:u?i.jsxs("div",{className:"text-center space-y-4",role:"status","aria-live":"polite",children:[i.jsxs("div",{className:"bg-kodo-lime/10 border border-kodo-lime text-kodo-lime px-4 py-4 rounded",role:"alert","aria-live":"assertive",children:[i.jsx("p",{className:"font-medium",children:"Email envoyé !"}),i.jsxs("p",{className:"text-sm mt-1",children:["Un lien de réinitialisation a été envoyé à ",t.email]})]}),i.jsx("p",{className:"text-sm text-kodo-content-dim",children:"Veuillez vérifier votre boîte mail et cliquer sur le lien pour réinitialiser votre mot de passe."}),i.jsx(h,{to:"/login",className:"text-kodo-cyan hover:underline text-sm block focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 rounded",children:"Retour à la connexion"})]}):i.jsxs("form",{onSubmit:f,className:"space-y-4","aria-label":"Formulaire de réinitialisation de mot de passe",children:[l&&i.jsx("div",{className:"bg-kodo-red/10 border border-kodo-red text-kodo-red px-4 py-4 rounded",role:"alert","aria-live":"assertive",children:l.message}),i.jsx(y,{type:"email",label:"Email",value:t.email,onChange:e=>x("email",e.target.value),onBlur:()=>v("email"),error:r.email,required:!0,autoComplete:"email"}),i.jsx(g,{type:"submit",loading:d,children:"Envoyer le lien de réinitialisation"})]})})}export{B as ForgotPasswordPage,B as default};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{j as e}from"./vendor-react-BHG7lGYR.js";import{g as r,B as t}from"./index-BWcMVITa.js";import{y as a,ah as d,b5 as i}from"./vendor-icons-DaGlTw4_.js";import"./vendor-IYr-MHu4.js";import"./vendor-router-D-s5vIeO.js";import"./vendor-tanstack-BzWBL1hV.js";import"./vendor-utils-CgOSfOkx.js";function p(){const o=r(),s=()=>{o.info("Add gear feature coming soon")};return e.jsxs("div",{className:"min-h-screen p-8 space-y-8",children:[e.jsx("div",{className:"glass-hud rounded-2xl border-white/10 p-8 hud-corner",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("div",{className:"w-12 h-12 rounded-xl bg-kodo-steel/20 flex items-center justify-center border border-kodo-steel/30",children:e.jsx(a,{className:"w-6 h-6 text-kodo-steel"})}),e.jsxs("div",{children:[e.jsx("h1",{className:"text-3xl font-display font-bold text-white",children:"Gear Locker"}),e.jsx("p",{className:"text-sm text-white opacity-80",children:"Manage your equipment and instruments"})]})]}),e.jsxs(t,{onClick:s,className:"bg-kodo-cyan hover:bg-kodo-cyan/80 text-black",children:[e.jsx(d,{className:"w-4 h-4 mr-2"}),"Add Gear"]})]})}),e.jsxs("div",{className:"glass-hud rounded-xl border-white/10 p-12 text-center",children:[e.jsx(i,{className:"w-16 h-16 text-kodo-secondary mx-auto mb-4 opacity-50"}),e.jsx("h3",{className:"text-lg font-bold text-white mb-2",children:"No Gear Yet"}),e.jsx("p",{className:"text-sm text-white opacity-90 mb-4",children:"Start adding your equipment to keep track of your studio setup"}),e.jsx(t,{onClick:s,variant:"outline",className:"border-kodo-steel/50 text-kodo-steel hover:bg-white/5",children:"Add Your First Gear"})]})]})}export{p as GearPage};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{j as e}from"./vendor-react-BHG7lGYR.js";import{g as i,B as o}from"./index-BWcMVITa.js";import{R as a,b3 as n,aO as d}from"./vendor-icons-DaGlTw4_.js";import"./vendor-IYr-MHu4.js";import"./vendor-router-D-s5vIeO.js";import"./vendor-tanstack-BzWBL1hV.js";import"./vendor-utils-CgOSfOkx.js";function v(){const s=i(),t=()=>{s.info("Live streaming feature coming soon")},r=()=>{s.info("Browse live sessions feature coming soon")};return e.jsxs("div",{className:"min-h-screen p-8 space-y-8",children:[e.jsx("div",{className:"glass-hud rounded-2xl border-white/10 p-8 hud-corner",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("div",{className:"w-12 h-12 rounded-xl bg-kodo-red/20 flex items-center justify-center border border-kodo-red/30 animate-pulse",children:e.jsx(a,{className:"w-6 h-6 text-kodo-red"})}),e.jsxs("div",{children:[e.jsx("h1",{className:"text-3xl font-display font-bold text-white",children:"Live Sessions"}),e.jsx("p",{className:"text-sm text-kodo-secondary",children:"Stream and collaborate in real-time"})]})]}),e.jsxs(o,{onClick:t,className:"bg-kodo-red hover:bg-kodo-red/80 text-white",children:[e.jsx(n,{className:"w-4 h-4 mr-2"}),"Go Live"]})]})}),e.jsxs("div",{className:"glass-hud rounded-xl border-white/10 p-12 text-center",children:[e.jsx(d,{className:"w-16 h-16 text-kodo-secondary mx-auto mb-4 opacity-50"}),e.jsx("h3",{className:"text-lg font-bold text-white mb-2",children:"No Active Sessions"}),e.jsx("p",{className:"text-kodo-secondary mb-4",children:"Start a live session or join an ongoing stream"}),e.jsx(o,{onClick:r,variant:"outline",className:"border-kodo-red/30 text-kodo-red hover:bg-kodo-red/10",children:"Browse Live Sessions"})]})]})}export{v as LivePage};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as h,j as e}from"./vendor-react-BHG7lGYR.js";import{u as g,L as E}from"./vendor-router-D-s5vIeO.js";import{G as C}from"./vendor-IYr-MHu4.js";import{t as S,o as L,b as A,s as p}from"./vendor-utils-CgOSfOkx.js";import{h as _,g as F,w as u,B as M,l as D,j as R,A as T,C as V}from"./index-BWcMVITa.js";import{u as q,F as f}from"./useFormValidation-B1Tm3VtD.js";import{C as B}from"./checkbox-DUBsARHw.js";import{aC as z,aA as I}from"./vendor-icons-DaGlTw4_.js";import"./vendor-tanstack-BzWBL1hV.js";const P=L({email:p().email("Email invalide"),password:p().min(1,"Le mot de passe est requis"),remember_me:A().optional()}),G=()=>{const o=g(),{login:t,isLoading:i,error:m}=_(),{toast:b}=F(),{register:d,handleSubmit:w,formState:{errors:k},watch:l,setValue:j}=C({resolver:S(P),defaultValues:{email:"",password:"",remember_me:!1}}),a=l(),{validate:c,errors:v}=q({type:"LoginRequest",debounceMs:300});h.useEffect(()=>{if(a.email||a.password){const s={email:a.email||"",password:a.password||"",remember_me:a.remember_me||!1};c(s)}},[a.email,a.password,a.remember_me,c]);const y=async r=>{try{const s={email:r.email,password:r.password,remember_me:r.remember_me||!1};await t(s),o("/dashboard")}catch(s){const n=u(s);D.error("Login error",{error:s instanceof Error?s.message:String(s),stack:s instanceof Error?s.stack:void 0}),b({title:"Erreur de connexion",description:n,variant:"destructive"})}},x=r=>{const s=k[r]?.message,n=v.find(N=>N.field===r)?.message;return s||n};return e.jsxs("form",{onSubmit:w(y),className:"w-full space-y-8",noValidate:!0,children:[m&&e.jsxs("div",{className:"bg-kodo-red/10 border border-kodo-red/20 text-kodo-red text-sm px-4 py-3 rounded-lg flex items-center gap-2 animate-in fade-in slide-in-from-top-1",role:"alert",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-kodo-red shrink-0"}),e.jsx("span",{children:u(m)})]}),e.jsxs("div",{className:"space-y-4",children:[e.jsx(f,{label:"Email",type:"email",autoComplete:"email",icon:e.jsx(z,{className:"h-4 w-4"}),...d("email"),error:x("email")}),e.jsx(f,{label:"Mot de passe",type:"password",autoComplete:"current-password",icon:e.jsx(I,{className:"h-4 w-4"}),...d("password"),error:x("password")})]}),e.jsx("div",{className:"flex items-center justify-between",children:e.jsx(B,{id:"remember_me",checked:l("remember_me"),onCheckedChange:r=>j("remember_me",r===!0),label:"Se souvenir de moi"})}),e.jsx(M,{type:"submit",disabled:i,variant:"default",size:"lg",className:"w-full bg-gradient-to-r from-kodo-cyan to-kodo-cyan-dim hover:from-kodo-cyan/90 hover:to-kodo-cyan-dim/90 text-kodo-ink font-bold tracking-wide shadow-lg shadow-kodo-cyan/20 rounded-xl",children:i?e.jsxs("span",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"h-4 w-4 border-2 border-current border-t-transparent rounded-full animate-spin"}),"Connexion..."]}):"ACCÉDER AU TERMINAL"})]})},Y=()=>{const{isAuthenticated:o}=R(),t=g();return h.useEffect(()=>{o&&t("/")},[o,t]),e.jsxs("div",{className:"min-h-screen flex items-center justify-center bg-kodo-void relative overflow-hidden",children:[e.jsx(T,{}),e.jsxs("div",{className:"w-full max-w-md mx-auto relative z-10 px-4",children:[e.jsxs("div",{className:"text-center mb-8",children:[e.jsx("div",{className:"w-16 h-16 bg-gradient-to-br from-kodo-cyan to-kodo-magenta rounded-xl mx-auto mb-4 flex items-center justify-center shadow-lg shadow-kodo-cyan/20",children:e.jsx("span",{className:"text-3xl",children:"💠"})}),e.jsx("h1",{className:"text-4xl font-display font-bold text-white mb-2 tracking-wide text-glow",children:"VEZA"}),e.jsx("p",{className:"text-sm text-kodo-text-dim font-mono uppercase tracking-[0.2em]",children:"System v5.0 // Secure Login"})]}),e.jsx(V,{variant:"glass",className:"w-full backdrop-blur-3xl border-white/10 shadow-2xl p-8",children:e.jsx(G,{})}),e.jsx("div",{className:"mt-8 text-center space-y-4",children:e.jsxs("p",{className:"text-xs text-kodo-text-dim",children:["Pas encore de compte ?"," ",e.jsx(E,{to:"/register",className:"font-bold text-kodo-cyan hover:text-kodo-cyan-dim transition-colors uppercase tracking-wider",children:"S'inscrire"})]})})]})]})};export{Y as LoginPage};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{j as e}from"./vendor-react-BHG7lGYR.js";import{L as a}from"./vendor-router-D-s5vIeO.js";import{C as c,b as d,c as n,v as x,e as m,B as t}from"./index-BWcMVITa.js";import{n as o,H as r,aG as h,ad as u,af as p}from"./vendor-icons-DaGlTw4_.js";import"./vendor-IYr-MHu4.js";import"./vendor-tanstack-BzWBL1hV.js";import"./vendor-utils-CgOSfOkx.js";function C(){const i=[{to:"/dashboard",label:"Dashboard",icon:r},{to:"/library",label:"Ma bibliothèque",icon:u},{to:"/search",label:"Rechercher",icon:o},{to:"/marketplace",label:"Marketplace",icon:p}];return e.jsx("div",{className:"min-h-screen flex items-center justify-center bg-kodo-void dark:bg-kodo-ink p-4",children:e.jsx("div",{className:"w-full max-w-2xl",children:e.jsxs(c,{className:"text-center",children:[e.jsxs(d,{children:[e.jsx("div",{className:"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-kodo-steel/20 dark:bg-kodo-steel",children:e.jsx(o,{className:"h-8 w-8 text-kodo-steel dark:text-kodo-steel"})}),e.jsx(n,{className:"text-2xl",children:"Page non trouvée"}),e.jsx(x,{children:"La page que vous recherchez n'existe pas ou a été déplacée."})]}),e.jsxs(m,{className:"space-y-6",children:[e.jsx("div",{className:"text-6xl font-bold text-kodo-text-main dark:text-kodo-text-main",children:"404"}),e.jsx("p",{className:"text-kodo-content-dim dark:text-kodo-content-dim",children:"Il semble que vous ayez suivi un lien cassé ou tapé une URL incorrecte. Voici quelques options pour continuer :"}),e.jsxs("div",{className:"flex flex-col sm:flex-row gap-2",children:[e.jsx(t,{asChild:!0,className:"flex-1",children:e.jsxs(a,{to:"/dashboard",children:[e.jsx(r,{className:"mr-2 h-4 w-4"}),"Retour au dashboard"]})}),e.jsxs(t,{variant:"outline",onClick:()=>window.history.back(),className:"flex-1",children:[e.jsx(h,{className:"mr-2 h-4 w-4"}),"Page précédente"]})]}),e.jsxs("div",{className:"border-t pt-4",children:[e.jsx("p",{className:"text-sm font-medium text-kodo-text-main dark:text-kodo-text-main mb-3",children:"Liens rapides :"}),e.jsx("div",{className:"grid grid-cols-2 sm:grid-cols-4 gap-2",children:i.map(s=>{const l=s.icon;return e.jsx(t,{variant:"outline",size:"sm",asChild:!0,className:"flex flex-col h-auto py-2",children:e.jsxs(a,{to:s.to,children:[e.jsx(l,{className:"h-4 w-4 mb-1"}),e.jsx("span",{className:"text-xs",children:s.label})]})},s.to)})})]}),e.jsxs("div",{className:"border-t pt-4 text-left",children:[e.jsx("p",{className:"text-sm font-medium text-kodo-text-main dark:text-kodo-text-main mb-2",children:"Suggestions :"}),e.jsxs("ul",{className:"text-sm text-kodo-content-dim dark:text-kodo-content-dim space-y-1 list-disc list-inside",children:[e.jsx("li",{children:"Vérifiez l'orthographe de l'URL"}),e.jsx("li",{children:"Utilisez la recherche pour trouver ce que vous cherchez"}),e.jsx("li",{children:"Consultez votre bibliothèque ou le marketplace"}),e.jsx("li",{children:"Contactez le support si le problème persiste"})]})]})]})]})})})}export{C as default};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as K,j as n}from"./vendor-react-BHG7lGYR.js";import{d as k,B as d}from"./index-BWcMVITa.js";import{u as x,al as C,v as m}from"./vendor-icons-DaGlTw4_.js";function B({currentPage:e,totalPages:s,onPageChange:a,maxVisiblePages:t=5,showFirstLast:c=!1,className:A,totalItems:l,itemsPerPage:u,showItemsInfo:y=!1}){const M=K.useMemo(()=>{if(s<=t)return Array.from({length:s},(p,z)=>z+1);const i=[],h=Math.floor(t/2);let r=Math.max(1,e-h);const f=Math.min(s,r+t-1);f===s&&(r=Math.max(1,s-t+1)),(c&&r>1||r>1)&&(i.push(1),r>2&&i.push("ellipsis-start"));for(let p=r;p<=f;p++)i.push(p);return f<s&&(f<s-1&&i.push("ellipsis-end"),i.push(s)),i},[e,s,t,c]),v=()=>{e>1&&a(e-1)},j=()=>{e<s&&a(e+1)},N=()=>{a(1)},w=()=>{a(s)},o=(i,h,r)=>{if(i.key==="ArrowLeft"||i.key==="ArrowUp"){i.preventDefault(),v();return}if(i.key==="ArrowRight"||i.key==="ArrowDown"){i.preventDefault(),j();return}if(i.key==="Home"){i.preventDefault(),N();return}if(i.key==="End"){i.preventDefault(),w();return}},b=l&&u?(e-1)*u+1:null,D=l&&u?Math.min(e*u,l):null;return s<=1&&!y?null:n.jsxs("div",{className:k("flex flex-col gap-4",A),children:[y&&l!==void 0&&b!==null&&D!==null&&n.jsxs("div",{className:"text-sm text-muted-foreground text-center",children:["Affichage de ",b," à ",D," sur ",l," résultat",l>1?"s":""]}),s>1&&n.jsxs("nav",{"aria-label":"Navigation de pagination",role:"navigation",className:"flex items-center justify-center gap-1",children:[c&&n.jsxs(d,{type:"button",variant:"outline",size:"icon",onClick:N,disabled:e===1,"aria-label":"Première page",onKeyDown:i=>o(i),children:[n.jsx(x,{className:"h-4 w-4","aria-hidden":"true"}),n.jsx(x,{className:"h-4 w-4 -ml-2","aria-hidden":"true"}),n.jsx("span",{className:"sr-only",children:"Première page"})]}),n.jsxs(d,{type:"button",variant:"outline",size:"icon",onClick:v,disabled:e===1,"aria-label":"Page précédente",onKeyDown:i=>o(i),children:[n.jsx(x,{className:"h-4 w-4","aria-hidden":"true"}),n.jsx("span",{className:"sr-only",children:"Page précédente"})]}),M.map((i,h)=>i==="ellipsis-start"||i==="ellipsis-end"?n.jsx("div",{className:"flex h-9 w-9 items-center justify-center",children:n.jsx(C,{className:"h-4 w-4 text-muted-foreground"})},`ellipsis-${h}`):n.jsx(d,{type:"button",variant:e===i?"default":"outline",size:"icon",onClick:()=>a(i),"aria-label":`Aller à la page ${i}`,"aria-current":e===i?"page":void 0,onKeyDown:r=>o(r),className:k("h-9 w-9",e===i&&"bg-primary text-primary-foreground"),children:i},i)),n.jsxs(d,{type:"button",variant:"outline",size:"icon",onClick:j,disabled:e===s,"aria-label":"Page suivante",onKeyDown:i=>o(i),children:[n.jsx(m,{className:"h-4 w-4","aria-hidden":"true"}),n.jsx("span",{className:"sr-only",children:"Page suivante"})]}),c&&n.jsxs(d,{type:"button",variant:"outline",size:"icon",onClick:w,disabled:e===s,"aria-label":"Dernière page",onKeyDown:i=>o(i),children:[n.jsx(m,{className:"h-4 w-4","aria-hidden":"true"}),n.jsx(m,{className:"h-4 w-4 -ml-2","aria-hidden":"true"}),n.jsx("span",{className:"sr-only",children:"Dernière page"})]})]})]})}export{B as P};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as n,j as e}from"./vendor-react-BHG7lGYR.js";import{g as x,B as t,d as r}from"./index-BWcMVITa.js";import{u}from"./vendor-router-D-s5vIeO.js";import{V as b,a0 as p,a5 as f,a3 as g}from"./vendor-icons-DaGlTw4_.js";import"./vendor-IYr-MHu4.js";import"./vendor-tanstack-BzWBL1hV.js";import"./vendor-utils-CgOSfOkx.js";function S(){const l=u(),o=x(),[a,i]=n.useState(!1),[s,d]=n.useState(!1),c=()=>{i(!a),o.info(a?"Shuffle disabled":"Shuffle enabled")},m=()=>{d(!s),o.info(s?"Repeat disabled":"Repeat enabled")},h=()=>{l("/library")};return e.jsxs("div",{className:"min-h-screen p-8 space-y-8",children:[e.jsx("div",{className:"glass-hud rounded-2xl border-white/10 p-8 hud-corner",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("div",{className:"w-12 h-12 rounded-xl bg-kodo-magenta/20 flex items-center justify-center border border-kodo-magenta/30",children:e.jsx(b,{className:"w-6 h-6 text-kodo-magenta"})}),e.jsxs("div",{children:[e.jsx("h1",{className:"text-3xl font-display font-bold text-white",children:"Play Queue"}),e.jsx("p",{className:"text-sm text-white opacity-80",children:"Manage your playback queue"})]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(t,{onClick:c,variant:"outline",size:"icon",className:r("border-white/10",a&&"bg-kodo-magenta/20 border-kodo-magenta/50"),children:e.jsx(p,{className:"w-4 h-4"})}),e.jsx(t,{onClick:m,variant:"outline",size:"icon",className:r("border-white/10",s&&"bg-kodo-magenta/20 border-kodo-magenta/50"),children:e.jsx(f,{className:"w-4 h-4"})})]})]})}),e.jsxs("div",{className:"glass-hud rounded-xl border-white/10 p-12 text-center",children:[e.jsx(g,{className:"w-16 h-16 text-kodo-secondary mx-auto mb-4 opacity-50"}),e.jsx("h3",{className:"text-lg font-bold text-white mb-2",children:"Queue is Empty"}),e.jsx("p",{className:"text-base text-white opacity-90 mb-4",children:"Add tracks to your queue to start playing"}),e.jsx(t,{onClick:h,variant:"outline",className:"border-kodo-magenta/30 text-kodo-magenta hover:bg-kodo-magenta/10",children:"Browse Library"})]})]})}export{S as QueuePage};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as x,j as e}from"./vendor-react-BHG7lGYR.js";import{u as j,L as _}from"./vendor-router-D-s5vIeO.js";import{G as S}from"./vendor-IYr-MHu4.js";import{t as R,o as A,s as m}from"./vendor-utils-CgOSfOkx.js";import{h as w,g as L,w as u,B as F,l as M,j as C,A as D,C as I}from"./index-BWcMVITa.js";import{u as V,F as l}from"./useFormValidation-B1Tm3VtD.js";import{aC as q,U as B,aA as h}from"./vendor-icons-DaGlTw4_.js";import"./vendor-tanstack-BzWBL1hV.js";const T=A({email:m().email("Email invalide"),username:m().min(3,"Le nom d'utilisateur doit contenir au moins 3 caractères"),password:m().min(12,"Le mot de passe doit contenir au moins 12 caractères"),password_confirm:m().min(1,"La confirmation est requise")}).refine(o=>o.password===o.password_confirm,{message:"Les mots de passe ne correspondent pas",path:["password_confirm"]}),z=()=>{const o=j(),{register:d,isLoading:f,error:t}=w(),{toast:p}=L(),{register:c,handleSubmit:b,formState:{errors:k},watch:v}=S({resolver:R(T),defaultValues:{email:"",username:"",password:"",password_confirm:""}}),s=v(),{validate:g,errors:N}=V({type:"RegisterRequest",debounceMs:300});x.useEffect(()=>{if(s.email||s.username||s.password||s.password_confirm){const r={email:s.email||"",username:s.username||"",password:s.password||"",password_confirmation:s.password_confirm||""};g(r)}},[s.email,s.username,s.password,s.password_confirm,g]);const y=async a=>{try{const r={email:a.email,username:a.username,password:a.password,password_confirm:a.password_confirm};await d(r);const{isAuthenticated:n}=w.getState();o(n?"/dashboard":"/login")}catch(r){const n=u(r);M.error("Register error",{error:r instanceof Error?r.message:String(r),stack:r instanceof Error?r.stack:void 0}),p({title:"Erreur d'inscription",description:n,variant:"destructive"})}};x.useEffect(()=>{if(t){const a=u(t);p({title:"Erreur d'inscription",description:a,variant:"destructive"})}},[t,p]);const i=a=>{const r=k[a]?.message,n=N.find(E=>E.field===a)?.message;return r||n};return e.jsxs("form",{onSubmit:b(y),className:"space-y-6",noValidate:!0,children:[t&&e.jsxs("div",{className:"bg-kodo-red/10 border border-kodo-red/20 text-kodo-red text-sm px-4 py-3 rounded-lg flex items-center gap-2 animate-in fade-in slide-in-from-top-1",role:"alert",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-kodo-red shrink-0"}),e.jsx("span",{children:u(t)})]}),e.jsxs("div",{className:"space-y-4",children:[e.jsx(l,{label:"Email",type:"email",icon:e.jsx(q,{className:"h-4 w-4"}),...c("email"),error:i("email")}),e.jsx(l,{label:"Nom d'utilisateur",type:"text",icon:e.jsx(B,{className:"h-4 w-4"}),...c("username"),error:i("username")}),e.jsx(l,{label:"Mot de passe",type:"password",icon:e.jsx(h,{className:"h-4 w-4"}),...c("password"),error:i("password")}),e.jsx(l,{label:"Confirmer le mot de passe",type:"password",icon:e.jsx(h,{className:"h-4 w-4"}),...c("password_confirm"),error:i("password_confirm")||i("password_confirmation")})]}),e.jsx(F,{type:"submit",disabled:f,variant:"default",size:"lg",className:"w-full bg-gradient-to-r from-kodo-magenta to-kodo-magenta-dim hover:from-kodo-magenta/90 hover:to-kodo-magenta-dim/90 text-white font-bold tracking-wide shadow-lg shadow-kodo-magenta/20 rounded-xl",children:f?e.jsxs("span",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"h-4 w-4 border-2 border-current border-t-transparent rounded-full animate-spin"}),"Initialisation..."]}):"S'INSCRIRE"})]})},Q=()=>{const{isAuthenticated:o}=C(),d=j();return x.useEffect(()=>{o&&d("/")},[o,d]),e.jsxs("div",{className:"min-h-screen flex items-center justify-center bg-kodo-void relative overflow-hidden py-12",children:[e.jsx(D,{}),e.jsxs("div",{className:"w-full max-w-md mx-auto relative z-10 px-4",children:[e.jsxs("div",{className:"text-center mb-8",children:[e.jsx("div",{className:"w-16 h-16 bg-gradient-to-br from-kodo-cyan to-kodo-magenta rounded-xl mx-auto mb-4 flex items-center justify-center shadow-lg shadow-kodo-magenta/20",children:e.jsx("span",{className:"text-3xl",children:"💠"})}),e.jsx("h1",{className:"text-4xl font-display font-bold text-white mb-2 tracking-wide text-glow",children:"VEZA"}),e.jsx("p",{className:"text-sm text-kodo-text-dim font-mono uppercase tracking-[0.2em]",children:"Join the Network"})]}),e.jsx(I,{variant:"glass",className:"w-full backdrop-blur-3xl border-white/10 shadow-2xl p-8",children:e.jsx(z,{})}),e.jsx("div",{className:"mt-8 text-center space-y-4",children:e.jsxs("p",{className:"text-xs text-kodo-text-dim",children:["Déjà un compte ?"," ",e.jsx(_,{to:"/login",className:"font-bold text-kodo-cyan hover:text-kodo-cyan-dim transition-colors uppercase tracking-wider",children:"Se connecter"})]})})]})]})};export{Q as RegisterPage};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as i,j as e}from"./vendor-react-BHG7lGYR.js";import{C as n,b as l,c,v as x,e as m,B as s}from"./index-BWcMVITa.js";import{u as k}from"./vendor-router-D-s5vIeO.js";import{a as u,g as h,a9 as p,H as j,f,aC as N}from"./vendor-icons-DaGlTw4_.js";import"./vendor-IYr-MHu4.js";import"./vendor-tanstack-BzWBL1hV.js";import"./vendor-utils-CgOSfOkx.js";function R(){const a=k(),[t,r]=i.useState(!1),o=async()=>{r(!0),setTimeout(()=>{window.location.reload()},500)},d=()=>{a("/dashboard")};return e.jsx("div",{className:"min-h-screen flex items-center justify-center bg-kodo-void dark:bg-kodo-ink p-4",children:e.jsx("div",{className:"w-full max-w-2xl",children:e.jsxs(n,{className:"text-center",children:[e.jsxs(l,{children:[e.jsx("div",{className:"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-kodo-red/10 dark:bg-kodo-red/20",children:e.jsx(u,{className:"h-8 w-8 text-kodo-red dark:text-kodo-red"})}),e.jsx(c,{className:"text-2xl",children:"Erreur serveur"}),e.jsx(x,{children:"Une erreur interne s'est produite. Notre équipe a été notifiée."})]}),e.jsxs(m,{className:"space-y-6",children:[e.jsx("div",{className:"text-6xl font-bold text-kodo-text-main dark:text-kodo-text-main",children:"500"}),e.jsx("p",{className:"text-kodo-content-dim dark:text-kodo-content-dim",children:"Nous nous excusons pour la gêne occasionnée. Notre équipe technique a été automatiquement notifiée et travaille à résoudre le problème."}),e.jsx("div",{className:"bg-kodo-steel/10 dark:bg-kodo-steel/20 border border-kodo-steel dark:border-kodo-steel rounded-lg p-4",children:e.jsxs("div",{className:"flex items-start gap-4 text-left",children:[e.jsx(h,{className:"h-5 w-5 text-kodo-steel dark:text-kodo-steel mt-0.5"}),e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-kodo-steel dark:text-kodo-steel",children:"Que faire maintenant ?"}),e.jsxs("ul",{className:"text-sm text-kodo-steel dark:text-kodo-steel mt-2 space-y-1 list-disc list-inside",children:[e.jsx("li",{children:"Attendez quelques instants et réessayez"}),e.jsx("li",{children:"Vérifiez votre connexion internet"}),e.jsx("li",{children:"Si le problème persiste, contactez le support"})]})]})]})}),e.jsxs("div",{className:"flex flex-col sm:flex-row gap-2",children:[e.jsxs(s,{onClick:o,disabled:t,className:"flex-1",children:[e.jsx(p,{className:`mr-2 h-4 w-4 ${t?"animate-spin":""}`}),t?"Réessai...":"Réessayer"]}),e.jsxs(s,{onClick:d,variant:"outline",className:"flex-1",children:[e.jsx(j,{className:"mr-2 h-4 w-4"}),"Retour au dashboard"]})]}),e.jsxs("div",{className:"border-t pt-4 text-left",children:[e.jsx("p",{className:"text-sm font-medium text-kodo-text-main dark:text-kodo-text-main mb-3",children:"Besoin d'aide ?"}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center gap-2 text-sm text-kodo-content-dim dark:text-kodo-content-dim",children:[e.jsx(f,{className:"h-4 w-4 text-kodo-lime"}),e.jsx("span",{children:"L'erreur a été automatiquement signalée à notre équipe"})]}),e.jsxs("div",{className:"flex items-center gap-2 text-sm text-kodo-content-dim dark:text-kodo-content-dim",children:[e.jsx(N,{className:"h-4 w-4 text-kodo-steel"}),e.jsx("span",{children:"Contactez le support si le problème persiste après plusieurs tentatives"})]})]})]}),e.jsxs("details",{className:"border-t pt-4 text-left",children:[e.jsx("summary",{className:"text-sm font-medium text-kodo-text-main dark:text-kodo-text-main cursor-pointer hover:text-kodo-text-main dark:hover:text-kodo-text-main",children:"Détails techniques"}),e.jsxs("div",{className:"mt-2 p-4 bg-kodo-void dark:bg-kodo-graphite rounded text-xs font-mono text-kodo-content-dim dark:text-kodo-content-dim",children:[e.jsx("p",{children:"Code d'erreur: 500 Internal Server Error"}),e.jsxs("p",{children:["Timestamp: ",new Date().toISOString()]}),e.jsxs("p",{children:["User Agent: ",navigator.userAgent]})]})]})]})]})})})}export{R as default};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{j as e}from"./vendor-react-BHG7lGYR.js";import{d as o}from"./index-BWcMVITa.js";import{i as m}from"./vendor-icons-DaGlTw4_.js";function u({size:t="md",variant:r="default",className:s,"aria-label":a="Chargement en cours"}){const n={sm:"h-4 w-4",md:"h-5 w-5",lg:"h-6 w-6"},i={default:"text-kodo-cyan",muted:"text-muted-foreground",white:"text-white",current:"text-current"};return e.jsxs(e.Fragment,{children:[e.jsx(m,{className:o("animate-spin",n[t],i[r],s),"aria-hidden":"true"}),e.jsx("span",{className:"sr-only",children:a})]})}export{u as S};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as t,j as e}from"./vendor-react-BHG7lGYR.js";import{c as k,u as N}from"./vendor-router-D-s5vIeO.js";import{A as c,a as g}from"./AuthButton-ByJY1yM3.js";import{x as V}from"./index-BWcMVITa.js";import"./vendor-IYr-MHu4.js";import"./vendor-tanstack-BzWBL1hV.js";import"./vendor-utils-CgOSfOkx.js";import"./vendor-icons-DaGlTw4_.js";function T(){const[d]=k(),u=N(),[n,o]=t.useState("verifying"),[l,s]=t.useState("Vérification de votre email en cours..."),[j,m]=t.useState(!1),[f,v]=t.useState(!1),[a,x]=t.useState(0),i=t.useRef(null),[p,b]=t.useState(null);t.useEffect(()=>{const r=d.get("token");r?(b(r),h(r)):(o("error"),s("Lien de vérification invalide ou manquant"))},[d]),t.useEffect(()=>()=>{i.current&&clearInterval(i.current)},[]),t.useEffect(()=>(a>0?i.current=setInterval(()=>{x(r=>r<=1?(i.current&&clearInterval(i.current),0):r-1)},1e3):i.current&&(clearInterval(i.current),i.current=null),()=>{i.current&&clearInterval(i.current)}),[a]),t.useEffect(()=>{if(n==="success"){const r=setTimeout(()=>{u("/login",{replace:!0})},3e3);return()=>clearTimeout(r)}},[n,u]);const h=async r=>{try{m(!0),o("verifying"),s("Vérification de votre email en cours..."),await verifyEmail(r),o("success"),s("Votre email a été vérifié avec succès !")}catch(y){o("error"),s(y.message||"La vérification a échoué")}finally{m(!1)}},E=async()=>{if(!(a>0||f))try{v(!0);const r=localStorage.getItem("pendingVerificationEmail");if(!r){s("Email non trouvé. Veuillez vous réinscrire ou contacter le support.");return}await V.resendVerification({email:r}),x(60),s("Email de vérification envoyé ! Veuillez vérifier votre boîte mail.")}catch(r){s(r.message||"Échec de l'envoi de l'email")}finally{v(!1)}};return n==="verifying"?e.jsx(c,{title:"Vérification de l'email",subtitle:"Vérification en cours...",footerLinks:[{label:"Retour à la connexion",to:"/login"}],children:e.jsxs("div",{className:"text-center space-y-4",role:"status","aria-live":"polite","aria-busy":"true",children:[e.jsx("div",{className:"flex justify-center","aria-hidden":"true",children:e.jsx("div",{className:"animate-spin rounded-full h-12 w-12 border-b-2 border-kodo-steel"})}),e.jsx("p",{className:"text-kodo-content-dim",children:l}),e.jsx("span",{className:"sr-only",children:"Vérification de votre email en cours, veuillez patienter"})]})}):n==="success"?e.jsx(c,{title:"Email vérifié",subtitle:"Votre email a été vérifié avec succès",footerLinks:[{label:"Retour à la connexion",to:"/login"}],children:e.jsx("div",{className:"text-center space-y-4",role:"status","aria-live":"polite",children:e.jsxs("div",{className:"bg-kodo-lime/10 border border-kodo-lime text-kodo-lime px-4 py-4 rounded",role:"alert",children:[e.jsx("p",{className:"font-medium",children:"Succès !"}),e.jsx("p",{className:"text-sm mt-1",children:l}),e.jsx("p",{className:"text-xs mt-2 text-kodo-content-dim",children:"Vous allez être redirigé vers la page de connexion..."})]})})}):e.jsx(c,{title:"Vérification de l'email",subtitle:"Une erreur s'est produite",footerLinks:[{label:"Retour à la connexion",to:"/login"}],children:e.jsxs("div",{className:"text-center space-y-4",children:[e.jsxs("div",{className:"bg-kodo-red/10 border border-kodo-red text-kodo-red px-4 py-4 rounded",role:"alert","aria-live":"assertive",children:[e.jsx("p",{className:"font-medium",children:"Erreur"}),e.jsx("p",{className:"text-sm mt-1",children:l})]}),e.jsxs("div",{className:"space-y-2",children:[p&&e.jsx(g,{onClick:()=>h(p),loading:j,type:"button",children:"Réessayer"}),e.jsx(g,{onClick:E,loading:f,disabled:a>0,type:"button",variant:"secondary","aria-label":a>0?`Renvoyer l'email de vérification dans ${a} secondes`:"Renvoyer l'email de vérification",children:a>0?e.jsxs(e.Fragment,{children:[e.jsxs("span",{className:"sr-only",children:["Renvoyer dans ",a," secondes"]}),e.jsxs("span",{"aria-hidden":"true",children:["Renvoyer dans ",a,"s"]})]}):"Renvoyer l'email de vérification"})]})]})})}export{T as VerifyEmailPage,T as default};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a,j as e}from"./vendor-react-BHG7lGYR.js";import{d as t}from"./index-BWcMVITa.js";import{X as p,A as h,a as k,f as g,I as N}from"./vendor-icons-DaGlTw4_.js";const b=a.forwardRef(({variant:s="default",title:o,onClose:r,className:n,children:c,...x},m)=>{const f={destructive:"error",default:"info"}[s]||s,l={info:"bg-kodo-steel/10 border-kodo-steel/30 text-kodo-steel",success:"bg-kodo-lime/10 border-kodo-lime/30 text-kodo-lime",warning:"bg-kodo-gold/10 border-kodo-gold/30 text-kodo-gold",error:"bg-kodo-red/10 border-kodo-red/30 text-kodo-red"},i={info:e.jsx(N,{className:"w-5 h-5 flex-shrink-0"}),success:e.jsx(g,{className:"w-5 h-5 flex-shrink-0"}),warning:e.jsx(k,{className:"w-5 h-5 flex-shrink-0"}),error:e.jsx(h,{className:"w-5 h-5 flex-shrink-0"})},d=f;return e.jsxs("div",{ref:m,role:"alert",className:t("p-4 rounded-lg border flex gap-4",l[d]||l.info,n),...x,children:[i[d]||i.info,e.jsxs("div",{className:"flex-1",children:[o&&e.jsx("h5",{className:"font-bold mb-1",children:o}),e.jsx("div",{className:"text-sm opacity-90",children:c})]}),r&&e.jsx("button",{onClick:r,className:"opacity-70 hover:opacity-100 transition-opacity",children:e.jsx(p,{className:"w-4 h-4"})})]})});b.displayName="Alert";const j=a.forwardRef(({className:s,...o},r)=>e.jsx("h5",{ref:r,className:t("mb-1 font-bold leading-none tracking-tight text-white",s),...o}));j.displayName="AlertTitle";const w=a.forwardRef(({className:s,...o},r)=>e.jsx("div",{ref:r,className:t("text-sm opacity-90 [&_p]:leading-relaxed",s),...o}));w.displayName="AlertDescription";export{b as A,w as a};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{R as w,j as e}from"./vendor-react-BHG7lGYR.js";import{d as r}from"./index-BWcMVITa.js";const u=w.forwardRef(({src:s,alt:l="Avatar",fallback:n,size:d="md",status:a,className:x="",onClick:i},b)=>{const h={xs:"w-6 h-6 text-[10px]",sm:"w-8 h-8 text-xs",md:"w-10 h-10 text-sm",lg:"w-12 h-12 text-base",xl:"w-16 h-16 text-lg","2xl":"w-24 h-24 text-xl","3xl":"w-32 h-32 text-2xl"},c={online:"bg-kodo-lime",offline:"bg-kodo-steel",away:"bg-kodo-gold",idle:"bg-kodo-gold",busy:"bg-kodo-red",dnd:"bg-kodo-red"},m={xs:"w-1.5 h-1.5 border",sm:"w-2 h-2 border",md:"w-2.5 h-2.5 border-2",lg:"w-3 h-3 border-2",xl:"w-4 h-4 border-2","2xl":"w-5 h-5 border-4","3xl":"w-6 h-6 border-4"},g=(o=>{if(!o)return"?";const t=o.trim().split(" ");return t.length>=2?(t[0][0]+t[t.length-1][0]).toUpperCase():o.substring(0,2).toUpperCase()})(n||l);return e.jsxs("div",{ref:b,className:r("relative inline-block",x,i?"cursor-pointer":""),onClick:i,children:[e.jsx("div",{className:r(`${h[d]} rounded-full overflow-hidden bg-kodo-graphite border border-kodo-steel flex items-center justify-center relative`),children:s?e.jsx("img",{src:s,alt:l,className:"w-full h-full object-cover"}):e.jsx("span",{className:"font-bold text-kodo-content-dim",children:g})}),a&&e.jsx("span",{className:r("absolute bottom-0 right-0 rounded-full border-kodo-void",c[a],m[d])})]})});u.displayName="Avatar";export{u as A};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as b,j as e}from"./vendor-react-BHG7lGYR.js";import{d as y}from"./index-BWcMVITa.js";const u=b.forwardRef(({label:d,variant:a="cyan",icon:t,size:s="md",dot:n,count:o,children:l,className:m,...i},p)=>{const c={default:"cyan",primary:"cyan",success:"lime",warning:"gold",error:"magenta",secondary:"magenta"}[a]||a,r={cyan:"bg-kodo-steel/10 text-kodo-steel border-kodo-steel/30",magenta:"bg-kodo-magenta/10 text-kodo-magenta border-kodo-magenta/30",lime:"bg-kodo-lime/10 text-kodo-lime border-kodo-lime/30",gold:"bg-kodo-gold/10 text-kodo-gold border-kodo-gold/30",terminal:"bg-kodo-terminal/10 text-kodo-terminal border-kodo-terminal/30 font-mono"},x={sm:"px-2 py-0.5 text-[10px]",md:"px-2.5 py-0.5 text-[10px]",lg:"px-4 py-1 text-xs"},g=d||l,k=c;return e.jsxs("span",{ref:p,className:y("inline-flex items-center gap-1.5 rounded-full font-bold uppercase tracking-widest border",r[k]||r.cyan,x[s],m),...i,children:[n&&e.jsx("span",{className:"w-3 h-3 rounded-full bg-current"}),t&&e.jsx("span",{className:"w-3 h-3",children:t}),g,o!==void 0&&o>0&&e.jsx("span",{className:"ml-1 px-1.5 py-0.5 rounded-full bg-current/20 text-[10px]",children:o})]})});u.displayName="Badge";export{u as B};
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import{a as n,j as e}from"./vendor-react-BHG7lGYR.js";import{d as p}from"./index-BWcMVITa.js";import{l as b}from"./vendor-icons-DaGlTw4_.js";const m=n.forwardRef(({label:o,className:c="",onCheckedChange:r,id:i,...t},l)=>{const d=n.useId(),a=i||d,s=`${a}-label`,x=h=>{r&&r(h.target.checked)};return e.jsxs("label",{htmlFor:a,id:s,className:p("inline-flex items-center gap-4 cursor-pointer group",t.disabled?"opacity-50 cursor-not-allowed":"",c),children:[e.jsxs("div",{className:"relative",children:[e.jsx("input",{ref:l,id:a,type:"checkbox",className:"peer sr-only",onChange:x,"aria-label":!o&&!t["aria-label"]&&!t["aria-labelledby"]?"Checkbox":void 0,"aria-labelledby":o?s:void 0,...t}),e.jsx("div",{className:`
|
||||
w-5 h-5 rounded border border-kodo-steel bg-kodo-graphite
|
||||
peer-checked:bg-kodo-cyan peer-checked:border-kodo-steel
|
||||
peer-focus:ring-2 peer-focus:ring-kodo-steel/30
|
||||
transition-all duration-200
|
||||
`}),e.jsx(b,{className:"w-3.5 h-3.5 text-black absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-0 peer-checked:opacity-100 transition-opacity pointer-events-none",strokeWidth:3})]}),o&&e.jsx("span",{className:"text-sm text-kodo-text-main group-hover:text-white transition-colors select-none",children:o})]})});m.displayName="Checkbox";export{m as C};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{j as e}from"./vendor-react-BHG7lGYR.js";import{D as d}from"./index-BWcMVITa.js";import{a as m}from"./vendor-icons-DaGlTw4_.js";function u({open:a,onClose:r,onConfirm:l,title:i,description:t,confirmLabel:n="Confirm",cancelLabel:x="Cancel",variant:c="destructive",isLoading:s=!1}){const o=()=>{s||l()};return e.jsx(d,{open:a,onClose:r,title:i,onConfirm:o,confirmLabel:s?"Processing...":n,onCancel:r,showCancel:!0,children:e.jsx("div",{className:"space-y-4 py-4",children:e.jsxs("div",{className:"flex items-start gap-4",children:[c==="destructive"&&e.jsx("div",{className:"flex-shrink-0",children:e.jsx("div",{className:"flex h-10 w-10 items-center justify-center rounded-full bg-kodo-red/10 dark:bg-kodo-red/20",children:e.jsx(m,{className:"h-5 w-5 text-kodo-red dark:text-kodo-red"})})}),e.jsx("div",{className:"flex-1",children:e.jsx("p",{className:"text-sm text-muted-foreground",children:t})})]})})})}export{u as C};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as o,j as c}from"./vendor-react-BHG7lGYR.js";import{d}from"./index-BWcMVITa.js";function w({trigger:m,children:p,align:b="left",className:k,onOpenChange:i}){const[r,h]=o.useState(!1),l=o.useRef(null),a=o.useRef(null),f=o.useRef(null),t=o.useRef(-1),u=o.useCallback(e=>{h(e),i?.(e),e||(t.current=-1)},[i]);o.useEffect(()=>{if(!r)return;const e=s=>{l.current&&!l.current.contains(s.target)&&u(!1)};return document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[r,u]),o.useEffect(()=>{if(!r)return;const e=s=>{if(!a.current)return;const v=a.current.querySelectorAll('button, [href], input, select, textarea, [role="menuitem"], [tabindex]:not([tabindex="-1"])'),n=Array.from(v);switch(s.key){case"Escape":s.preventDefault(),u(!1),f.current?.focus();break;case"ArrowDown":s.preventDefault(),t.current=t.current<n.length-1?t.current+1:0,n[t.current]?.focus();break;case"ArrowUp":s.preventDefault(),t.current=t.current>0?t.current-1:n.length-1,n[t.current]?.focus();break;case"Enter":case" ":s.preventDefault(),t.current>=0&&n[t.current]&&n[t.current].click();break;case"Home":s.preventDefault(),t.current=0,n[0]?.focus();break;case"End":s.preventDefault(),t.current=n.length-1,n[n.length-1]?.focus();break}};return document.addEventListener("keydown",e),()=>{document.removeEventListener("keydown",e)}},[r,u]),o.useEffect(()=>{if(r&&a.current){const e=a.current.querySelectorAll('button, [href], input, select, textarea, [role="menuitem"], [tabindex]:not([tabindex="-1"])');e.length>0&&(t.current=0,setTimeout(()=>{e[0]?.focus()},0))}},[r]);const x={left:"left-0",right:"right-0",center:"left-1/2 -translate-x-1/2"};return c.jsxs("div",{ref:l,className:d("relative",k),children:[c.jsx("div",{ref:f,onClick:()=>u(!r),role:"button","aria-haspopup":"true","aria-expanded":r,tabIndex:0,onKeyDown:e=>{e.key==="Enter"||e.key===" "?(e.preventDefault(),u(!r)):e.key==="ArrowDown"&&(e.preventDefault(),u(!0))},style:{display:"inline-block"},children:m}),r&&c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"fixed inset-0 z-40",onClick:()=>u(!1),"aria-hidden":"true"}),c.jsx("div",{ref:a,className:d("absolute z-50 mt-2 min-w-[8rem] bg-kodo-ink border border-kodo-steel rounded-md shadow-lg","overflow-hidden",x[b]),role:"menu","aria-orientation":"vertical",children:p})]})]})}export{w as D};
|
||||
|
|
@ -1 +0,0 @@
|
|||
const a={TWO_FACTOR_AUTH:!0,PLAYLIST_COLLABORATION:!0,PLAYLIST_SEARCH:!1,PLAYLIST_SHARE:!1,PLAYLIST_RECOMMENDATIONS:!1,HLS_STREAMING:!1,ROLE_MANAGEMENT:!1,NOTIFICATIONS:!1};function A(e){return!!a[e]}function r(e){if(!A(e))throw new Error(`Feature "${e}" is not enabled. This feature is not available in the MVP.`)}export{A as i,r};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a,j as t}from"./vendor-react-BHG7lGYR.js";import{d as l}from"./index-BWcMVITa.js";const d=a.forwardRef(({className:o,type:r,icon:e,...i},s)=>t.jsxs("div",{className:"relative group",children:[e&&t.jsx("div",{className:"absolute left-3 top-1/2 -translate-y-1/2 text-kodo-text-dim group-focus-within:text-kodo-cyan transition-colors pointer-events-none",children:e}),t.jsx("input",{type:r,className:l("flex h-11 w-full rounded-xl border border-white/10 bg-kodo-graphite/40 px-3 py-2 text-sm text-white placeholder:text-kodo-text-dim/50 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:cursor-not-allowed disabled:opacity-50","backdrop-blur-sm transition-all duration-200","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kodo-cyan/30 focus-visible:border-kodo-cyan/50","hover:bg-white/5 hover:border-white/20",e&&"pl-10",o),ref:s,...i})]}));d.displayName="Input";export{d as I};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as t,j as s}from"./vendor-react-BHG7lGYR.js";import{d as r}from"./index-BWcMVITa.js";const d=t.forwardRef(({className:e,...a},o)=>s.jsx("label",{ref:o,className:r("text-sm font-medium leading-none text-kodo-content-dim peer-disabled:cursor-not-allowed peer-disabled:opacity-70",e),...a}));d.displayName="Label";export{d as L};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{m as a,k as l,j as s,i as t,h as e,e as o,n as r,b as i,a as d,f as c,r as y,o as P,c as m,s as n,l as g,d as k,u as p,g as u}from"./PlaylistCard-z0bLl0kY.js";const f={create:a,get:u,update:p,delete:k,list:g,search:n,addTrack:m,removeTrack:P,reorderTracks:y,addCollaborator:c,removeCollaborator:d,updateCollaboratorPermission:i,getCollaborators:r,createShareLink:o,follow:e,unfollow:t,getFollowStatus:s,getRecommendations:l};export{f as p};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as h,j as o}from"./vendor-react-BHG7lGYR.js";import{d as e}from"./index-BWcMVITa.js";const f=h.forwardRef(({value:i,max:l=100,variant:c="default",color:x="cyan",labelLeft:s,labelRight:d,className:n,...r},t)=>{const a=Math.min(100,Math.max(0,i/l*100)),m={cyan:"bg-kodo-cyan",magenta:"bg-kodo-magenta",lime:"bg-kodo-lime",gold:"bg-kodo-gold"},g={gold:"from-kodo-gold to-orange-500"};return c==="gaming"?o.jsxs("div",{className:e("relative",n),ref:t,...r,children:[o.jsx("div",{className:"h-4 bg-kodo-void rounded-full overflow-hidden border border-kodo-gold/30",children:o.jsx("div",{className:e("h-full bg-gradient-to-r shadow-[0_0_10px_rgba(255,215,0,0.5)] transition-all duration-500",g.gold),style:{width:`${a}%`}})}),(s||d)&&o.jsxs("div",{className:"flex justify-between text-[10px] font-mono font-bold text-kodo-gold mt-1 uppercase tracking-wider",children:[o.jsx("span",{children:s}),o.jsx("span",{children:d})]})]}):o.jsxs("div",{className:e("w-full",n),ref:t,...r,children:[o.jsx("div",{className:"h-2 bg-kodo-steel rounded-full overflow-hidden",children:o.jsx("div",{className:e("h-full transition-all duration-300 shadow-[0_0_10px_currentColor]",m[x]),style:{width:`${a}%`}})}),(s||d)&&o.jsxs("div",{className:"flex justify-between text-xs text-kodo-content-dim mt-1 font-mono",children:[o.jsx("span",{children:s}),o.jsx("span",{children:d})]})]})});f.displayName="Progress";export{f as P};
|
||||
|
|
@ -1 +0,0 @@
|
|||
.touch-manipulation{touch-action:manipulation;-webkit-tap-highlight-color:transparent}@media(max-width:640px){button,a[role=button],[role=button]{min-height:44px;min-width:44px}.playlist-card{-webkit-tap-highlight-color:rgba(0,0,0,.1)}.playlist-card,.playlist-track-item{-webkit-user-select:none;-moz-user-select:none;user-select:none}.playlist-list-container{-webkit-overflow-scrolling:touch;overflow-scrolling:touch}}@media(min-width:640px){button,a[role=button],[role=button]{min-height:auto;min-width:auto}}@supports (padding: max(0px)){.playlist-container{padding-left:max(1rem,env(safe-area-inset-left));padding-right:max(1rem,env(safe-area-inset-right))}}
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as i,j as r}from"./vendor-react-BHG7lGYR.js";import{D as P}from"./dropdown-BYn_8IId.js";import{I as F}from"./input-BGBI2Wze.js";import{B as G,d as j}from"./index-BWcMVITa.js";import{X as U,c as X,l as S}from"./vendor-icons-DaGlTw4_.js";function T({options:a,value:s,onChange:d,multiple:o=!1,searchable:l=!1,placeholder:c="Select an option...",disabled:O=!1,className:E,name:m,"aria-label":w,"aria-labelledby":D}){const[u,k]=i.useState(!1),[h,g]=i.useState(""),y=i.useRef(null),f=i.useMemo(()=>{const e={},n=[];return a.forEach(t=>{t.group?(e[t.group]||(e[t.group]=[]),e[t.group].push(t)):n.push(t)}),{groups:e,ungrouped:n}},[a]),p=i.useMemo(()=>{if(!l||!h)return{groups:f.groups,ungrouped:f.ungrouped};const e=h.toLowerCase(),n={},t=[];return Object.entries(f.groups).forEach(([x,K])=>{const v=K.filter(M=>M.label.toLowerCase().includes(e));v.length>0&&(n[x]=v)}),t.push(...f.ungrouped.filter(x=>x.label.toLowerCase().includes(e))),{groups:n,ungrouped:t}},[l,h,f]),b=s?(Array.isArray(s)?s:[s]).map(n=>a.find(t=>t.value===n)?.label).filter(Boolean):[],I=o?b.length>0?`${b.length} selected`:c:b[0]||c,N=e=>s?o?Array.isArray(s)&&s.includes(e):s===e:!1,A=e=>{if(o){const n=Array.isArray(s)?s:[],t=n.includes(e)?n.filter(x=>x!==e):[...n,e];d(t)}else d(e),k(!1),g("")},L=e=>{e.stopPropagation(),d(o?[]:"")};i.useEffect(()=>{u&&l&&y.current&&y.current.focus()},[u,l]),i.useEffect(()=>{u||g("")},[u]);const B=r.jsxs(G,{variant:"outline",disabled:O,className:j("w-full justify-between",!s||Array.isArray(s)&&s.length===0?"text-muted-foreground":"",E),type:"button","aria-label":w,"aria-labelledby":D,"aria-haspopup":"listbox","aria-expanded":u,children:[r.jsx("span",{className:"truncate",children:I}),r.jsxs("div",{className:"flex items-center gap-1 ml-2",children:[s&&(Array.isArray(s)&&s.length>0||!Array.isArray(s))&&r.jsx(U,{className:"h-4 w-4 shrink-0 opacity-50 hover:opacity-100",onClick:L}),r.jsx(X,{className:"h-4 w-4 shrink-0 opacity-50"})]})]}),R=r.jsxs("div",{className:"w-full min-w-[200px] max-h-[300px] overflow-y-auto",role:"listbox","aria-label":w||m||c,children:[l&&r.jsx("div",{className:"p-2 border-b",children:r.jsx(F,{ref:y,type:"text",placeholder:"Search...",value:h,onChange:e=>g(e.target.value),onClick:e=>e.stopPropagation(),className:"w-full"})}),p.ungrouped.length>0&&r.jsx("div",{className:"py-1",children:p.ungrouped.map(e=>r.jsx(C,{option:e,isSelected:N(e.value),multiple:o,onSelect:A},e.value))}),Object.entries(p.groups).map(([e,n])=>r.jsxs("div",{className:"py-1",children:[r.jsx("div",{className:"px-4 py-1.5 text-xs font-semibold text-kodo-content-dim uppercase",children:e}),n.map(t=>r.jsx(C,{option:t,isSelected:N(t.value),multiple:o,onSelect:A},t.value))]},e)),p.ungrouped.length===0&&Object.keys(p.groups).length===0&&r.jsx("div",{className:"px-4 py-2 text-sm text-kodo-content-dim text-center",children:"No options found"})]});return r.jsxs(r.Fragment,{children:[r.jsx(P,{trigger:B,align:"left",onOpenChange:k,className:"w-full",children:R}),m&&r.jsx("input",{type:"hidden",name:m,value:Array.isArray(s)?s.join(","):s||""})]})}function C({option:a,isSelected:s,multiple:d,onSelect:o}){const l=c=>{a.disabled||(c.key==="Enter"||c.key===" ")&&(c.preventDefault(),o(a.value))};return r.jsxs("div",{role:"option","aria-selected":s,className:j("relative flex items-center px-4 py-2 text-sm cursor-pointer","hover:bg-white/5 hover:text-white","focus:bg-white/5 focus:text-white","transition-colors text-kodo-text-main",s&&"bg-kodo-steel/10 text-kodo-steel",a.disabled&&"opacity-50 cursor-not-allowed pointer-events-none"),onClick:()=>!a.disabled&&o(a.value),onKeyDown:l,tabIndex:a.disabled?-1:0,children:[d&&r.jsx("div",{className:j("mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-kodo-steel",s&&"bg-kodo-cyan border-kodo-steel text-kodo-void"),children:s&&r.jsx(S,{className:"h-3 w-3"})}),r.jsx("span",{className:"flex-1",children:a.label}),!d&&s&&r.jsx(S,{className:"h-4 w-4 text-kodo-steel"})]})}export{T as S};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as e}from"./index-BWcMVITa.js";const m={getFeed:async t=>({posts:(await e.get("/social/feed",{params:t})).data.map(n=>({id:n.id,author:{name:"User",handle:"@user",avatar:""},content:n.content,timestamp:n.created_at,likes:0,comments:0,shares:0,type:"text"}))}),getPostsByUser:async(t,a=1,s)=>{const n=await e.get(`/social/posts/user/${t}`,{params:{page:a,limit:10}}),r=s?{name:s.first_name||s.last_name?`${s.first_name||""} ${s.last_name||""}`.trim():s.username,handle:`@${s.username}`,avatar:s.avatar_url||"",isVerified:!1}:{name:"User",handle:"@user",avatar:""};return{posts:n.data.map(o=>({id:o.id,author:r,content:o.content,timestamp:o.created_at,likes:o.like_count,comments:o.comment_count,shares:0,type:"text"})),total:100}},createPost:async t=>{const s=(await e.post("/social/posts",t)).data;return{post:{id:s.id,author:{name:"Me",handle:"@me",avatar:""},content:s.content,timestamp:s.created_at,likes:0,comments:0,shares:0,type:"text"}}},toggleLike:async(t,a)=>(await e.post("/social/like",{target_id:t,target_type:a})).data,getChatToken:async()=>(await e.post("/chat/token")).data,getChatStats:async()=>(await e.get("/chat/stats")).data,getComments:async t=>({comments:(await e.get(`/tracks/${t}/comments`)).data}),postComment:async(t,a)=>(await e.post(`/tracks/${t}/comments`,{content:a})).data,deleteComment:async t=>{await e.delete(`/comments/${t}`)},getNotifications:async()=>({notifications:(await e.get("/notifications")).data}),markRead:async t=>{await e.post(`/notifications/${t}/read`)},markAllRead:async()=>{await e.post("/notifications/read-all")},getWebhooks:async()=>(await e.get("/webhooks")).data,registerWebhook:async t=>{await e.post("/webhooks",t)},deleteWebhook:async t=>{await e.delete(`/webhooks/${t}`)},testWebhook:async t=>{await e.post(`/webhooks/${t}/test`)},getWebhookStats:async()=>(await e.get("/webhooks/stats")).data,regenerateWebhookKey:async t=>(await e.post(`/webhooks/${t}/regenerate-key`)).data};export{m as s};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as e,j as d}from"./vendor-react-BHG7lGYR.js";import{d as c}from"./index-BWcMVITa.js";const g=e.forwardRef(({className:s,value:t,defaultValue:i,onValueChange:r,children:a,...l},o)=>{const[f,v]=e.useState(i||""),u=t!==void 0?t:f,x=n=>{t===void 0&&v(n),r?.(n)};return d.jsx("div",{ref:o,className:s,...l,children:e.Children.map(a,n=>{if(e.isValidElement(n)){if(n.type===m)return e.cloneElement(n,{activeValue:u,onValueChange:x});if(n.type===p)return e.cloneElement(n,{activeValue:u})}return n})})});g.displayName="Tabs";const m=e.forwardRef(({className:s,children:t,activeValue:i,onValueChange:r,...a},l)=>d.jsx("div",{ref:l,className:c("inline-flex h-10 items-center justify-center rounded-md bg-kodo-graphite p-1 text-kodo-content-dim border border-kodo-steel/30",s),...a,children:e.Children.map(t,o=>e.isValidElement(o)&&o.type===b?e.cloneElement(o,{activeValue:i,onValueChange:r}):o)}));m.displayName="TabsList";const b=e.forwardRef(({className:s,value:t,activeValue:i,onValueChange:r,children:a,...l},o)=>{const f=i===t;return d.jsx("button",{ref:o,onClick:()=>r?.(t),className:c("inline-flex items-center justify-center whitespace-nowrap rounded-sm px-4 py-1.5 text-sm font-bold uppercase tracking-wider","ring-offset-kodo-void transition-all duration-200","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kodo-steel focus-visible:ring-offset-2","disabled:pointer-events-none disabled:opacity-50",f?"bg-kodo-cyan text-kodo-void":"text-kodo-content-dim hover:text-kodo-text-main",s),...l,children:a})});b.displayName="TabsTrigger";const p=e.forwardRef(({className:s,value:t,activeValue:i,children:r,...a},l)=>i!==t?null:d.jsx("div",{ref:l,className:c("mt-2 ring-offset-kodo-void focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kodo-steel focus-visible:ring-offset-2",s),...a,children:r}));p.displayName="TabsContent";export{g as T,m as a,b,p as c};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as d,j as e}from"./vendor-react-BHG7lGYR.js";import{d as l}from"./index-BWcMVITa.js";const n=d.forwardRef(({label:t,error:o,className:s,...r},a)=>e.jsxs("div",{className:"w-full",children:[t&&e.jsx("label",{className:"block text-sm font-medium text-kodo-content-dim mb-2 font-body",children:t}),e.jsx("textarea",{ref:a,className:l("w-full px-4 py-4","bg-kodo-graphite border",o?"border-kodo-red":"border-kodo-steel","text-white placeholder-gray-500","font-body text-base","rounded-lg","focus:outline-none focus:border-kodo-steel focus:ring-1 focus:ring-kodo-steel","transition-all duration-200","min-h-[100px] resize-y",s),...r}),o&&e.jsx("p",{className:"mt-1 text-xs text-kodo-red",children:o})]}));n.displayName="Textarea";export{n as T};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as o}from"./vendor-react-BHG7lGYR.js";function c(e,t){const[u,r]=o.useState(e);return o.useEffect(()=>{const n=setTimeout(()=>{r(e)},t);return()=>{clearTimeout(n)}},[e,t]),u}export{c as u};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as e,j as a}from"./vendor-react-BHG7lGYR.js";import{d as y,l as g,p as j}from"./index-BWcMVITa.js";const I=e.forwardRef(({className:m,label:o,error:t,icon:s,type:n,id:x,...l},h)=>{const i=e.useId(),d=x||i;return a.jsxs("div",{className:"relative group w-full mb-5",children:[a.jsxs("div",{className:"relative",children:[a.jsx("input",{type:n,id:d,className:y("block px-4 pb-2.5 pt-5 w-full text-sm text-white bg-kodo-graphite/40 rounded-xl border appearance-none focus:outline-none focus:ring-0 peer transition-all duration-200",t?"border-kodo-red focus:border-kodo-red":"border-white/10 hover:border-white/20 focus:border-kodo-cyan","backdrop-blur-sm",s?"pl-11":"",m),placeholder:" ",ref:h,...l}),s&&a.jsx("div",{className:"absolute left-3.5 top-1/2 -translate-y-1/2 text-kodo-text-dim peer-focus:text-kodo-cyan transition-colors pointer-events-none",children:s}),a.jsx("label",{htmlFor:d,className:y("absolute text-sm duration-200 transform -translate-y-3 scale-75 top-4 z-10 origin-[0] peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-3 pointer-events-none",s?"left-11 peer-focus:left-11 peer-placeholder-shown:left-11":"left-4 peer-focus:left-4 peer-placeholder-shown:left-4",t?"text-kodo-red":"text-kodo-text-dim peer-focus:text-kodo-cyan group-hover:text-white/70"),children:o})]}),t&&a.jsx("p",{className:"mt-1 text-xs text-kodo-red animate-slideDown",children:t})]})});I.displayName="FloatingInput";function N(m){const{type:o,debounceMs:t=300}=m,[s,n]=e.useState(!1),[x,l]=e.useState([]),[h,i]=e.useState(null),[d,b]=e.useState(null),u=e.useRef(null),r=e.useRef(0),k=e.useCallback(async(w,c)=>{if(!o)return g.warn("[useFormValidation] Validation type is required"),!1;n(!0),b(null);try{return l([]),i(!0),!0}catch(f){if(c!==r.current)return!1;const p=j(f);return b(p),l([]),i(!1),g.error("[useFormValidation] Validation request failed",{error:p.message,type:o}),!1}finally{c===r.current&&n(!1)}},[o]),V=e.useCallback(async w=>{if(t<=0)return r.current+=1,k(w,r.current);u.current&&clearTimeout(u.current),r.current+=1;const c=r.current;return new Promise(f=>{u.current=setTimeout(async()=>{if(c===r.current){const p=await k(w,c);f(p)}else f(!1)},t)})},[t,k]);e.useEffect(()=>()=>{u.current&&clearTimeout(u.current)},[]);const v=e.useCallback(()=>{l([]),i(null),b(null),n(!1)},[]);return{isValidating:s,errors:x,isValid:h,error:d,validate:V,clear:v}}export{I as F,N as u};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as s,j as r}from"./vendor-react-BHG7lGYR.js";import{d,z as x}from"./index-BWcMVITa.js";import{D as w}from"./dropdown-BYn_8IId.js";import{l as h,au as b}from"./vendor-icons-DaGlTw4_.js";const C=({open:o,onOpenChange:e,children:t})=>{const[n,a]=s.useState(!1),m=o!==void 0,p=l=>{m||a(l),e?.(l)},i=s.Children.toArray(t).find(l=>s.isValidElement(l)&&l.type===c),u=s.Children.toArray(t).find(l=>s.isValidElement(l)&&l.type===f);return!i||!u?r.jsx(r.Fragment,{children:t}):r.jsx(w,{trigger:i,onOpenChange:p,children:s.isValidElement(u)?u.props.children:u})},c=s.forwardRef(({className:o,children:e,asChild:t,...n},a)=>t&&s.isValidElement(e)?s.cloneElement(e,{ref:a,className:d(o,e.props.className),...n}):r.jsx("button",{ref:a,className:d("outline-none",o),...n,children:e}));c.displayName="DropdownMenuTrigger";const f=s.forwardRef(({className:o,align:e="start",sideOffset:t=4,children:n,...a},m)=>r.jsx("div",{ref:m,className:d("z-50 min-w-[8rem] overflow-hidden rounded-md border border-kodo-steel bg-kodo-ink p-1 text-white shadow-lg","animate-fadeIn",o),style:{marginTop:`${t}px`},...a,children:n}));f.displayName="DropdownMenuContent";const y=s.forwardRef(({className:o,inset:e,onKeyDown:t,onClick:n,...a},m)=>{const p=i=>{(i.key==="Enter"||i.key===" ")&&(i.preventDefault(),n&&!a.disabled&&n(i)),t&&t(i)};return r.jsx("button",{ref:m,role:"menuitem",className:d("relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none","transition-colors focus:bg-white/5 focus:text-white disabled:pointer-events-none disabled:opacity-50","text-kodo-text-main hover:text-white w-full text-left",e&&"pl-8",o),onKeyDown:p,onClick:n,...a})});y.displayName="DropdownMenuItem";const D=s.forwardRef(({className:o,children:e,checked:t,onCheckedChange:n,...a},m)=>r.jsxs("button",{ref:m,onClick:()=>n?.(!t),className:d("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none","transition-colors focus:bg-white/5 focus:text-white disabled:pointer-events-none disabled:opacity-50","text-kodo-text-main hover:text-white w-full text-left",o),...a,children:[r.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:t&&r.jsx(h,{className:"h-4 w-4 text-kodo-steel"})}),e]}));D.displayName="DropdownMenuCheckboxItem";const N=s.forwardRef(({className:o,children:e,checked:t,...n},a)=>r.jsxs("button",{ref:a,className:d("relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none","transition-colors focus:bg-white/5 focus:text-white disabled:pointer-events-none disabled:opacity-50","text-kodo-text-main hover:text-white w-full text-left",o),...n,children:[r.jsx("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:t&&r.jsx(b,{className:"h-2 w-2 fill-current text-kodo-steel"})}),e]}));N.displayName="DropdownMenuRadioItem";const g=s.forwardRef(({className:o,inset:e,...t},n)=>r.jsx("div",{ref:n,className:d("px-2 py-1.5 text-sm font-semibold text-kodo-content-dim",e&&"pl-8",o),...t}));g.displayName="DropdownMenuLabel";const j=s.forwardRef(({className:o,...e},t)=>r.jsx("div",{ref:t,className:d("-mx-1 my-1 h-px bg-kodo-steel",o),...e}));j.displayName="DropdownMenuSeparator";function I(){return x(e=>e.isLimited)}export{C as D,c as a,f as b,y as c,I as u};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as l,j as d}from"./vendor-react-BHG7lGYR.js";import{d as c,x as m}from"./index-BWcMVITa.js";function b({error:s,label:r,className:i,id:a,...e}){const t=l.useId(),o=a||t;return d.jsxs("div",{className:"w-full",children:[r&&d.jsx("label",{htmlFor:o,className:"block text-sm font-medium text-kodo-text-main dark:text-kodo-text-main mb-1",children:r}),d.jsx("input",{id:o,className:c("w-full px-4 py-2 border rounded-lg focus-visible:outline-none focus-visible:ring-0 transition-colors","dark:bg-kodo-graphite dark:text-white dark:border-kodo-steel",s?"border-kodo-red focus-visible:border-kodo-red/80 dark:border-kodo-red":"border-kodo-steel focus-visible:border-kodo-cyan/60 dark:border-kodo-steel",i),"aria-invalid":s?"true":"false","aria-describedby":s?`${o}-error`:void 0,"aria-required":e.required?"true":void 0,...e,autoComplete:e.autoComplete!==void 0?e.autoComplete:e.type==="email"?"email":e.type==="password"?"current-password":void 0,required:e.required}),s&&d.jsx("p",{id:`${o}-error`,className:"mt-1 text-sm text-kodo-red dark:text-kodo-red",role:"alert",children:s})]})}function h(){const[s,r]=l.useState(!1),[i,a]=l.useState(null),[e,t]=l.useState(!1);return{handleRequestReset:async n=>{try{r(!0),a(null),t(!1),await requestPasswordReset(n),t(!0)}catch(u){a(u),t(!1)}finally{r(!1)}},handleReset:async n=>{try{r(!0),a(null),t(!1),await m.resetPassword(n),t(!0)}catch(u){a(u),t(!1)}finally{r(!1)}},loading:s,error:i,success:e}}export{b as A,h as u};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{a as t}from"./index-BWcMVITa.js";import{q as u}from"./vendor-IYr-MHu4.js";async function l(r){return(await t.get(`/users/${r}`)).data.profile}async function p(r){const e=await t.get(`/users/by-username/${r}`);return e.data.profile||e.data}async function f(r,e){const s=await t.put(`/users/${r}`,e);return s.data.profile||s.data}async function d(r){return(await t.get(`/users/${r}/completion`)).data}async function w(r){return(await t.post(`/users/${r}/follow`)).data}async function g(r){return(await t.delete(`/users/${r}/follow`)).data}async function h(r,e=1,s=20){return(await t.get(`/users/${r}/followers`,{params:{page:e,limit:s}})).data}async function m(r,e=1,s=20){return(await t.get(`/users/${r}/following`,{params:{page:e,limit:s}})).data}async function E(r){try{return(await t.get(`/users/${r}/settings`)).data}catch(e){if(e instanceof u){if(e.response?.status===401)throw new Error("Unauthorized: Please log in to access settings");if(e.response?.status===403)throw new Error("Forbidden: You cannot access these settings");if(e.response?.status===404)throw new Error("Settings not found");const s=e.response?.data?.error||e.message||"Failed to fetch settings";throw new Error(s)}throw e}}async function y(r,e){try{await t.put("/users/settings",e)}catch(s){if(s instanceof u){if(s.response?.status===400){const o=s.response?.data?.error||"Invalid settings data";throw new Error(o)}if(s.response?.status===401)throw new Error("Unauthorized: Please log in to update settings");if(s.response?.status===403)throw new Error("Forbidden: You cannot update these settings");if(s.response?.status===404)throw new Error("Settings not found");const a=s.response?.data?.error||s.message||"Failed to update settings";throw new Error(a)}throw s}}class i extends Error{constructor(e,s){super(e),this.code=s,this.name="AvatarUploadError"}}async function $(r,e,s){const a=new FormData;a.append("avatar",e);try{return(await t.post(`/users/${r}/avatar`,a,{headers:{"Content-Type":"multipart/form-data"},onUploadProgress:n=>{if(n.total&&s){const c=Math.round(n.loaded*100/n.total);s(c)}}})).data}catch(o){if(o instanceof u){if(o.response){const n=o.response.status;throw n===400?new i(o.response.data?.error||"Format ou taille de fichier invalide","VALIDATION"):n===413?new i("Fichier trop volumineux (max 5MB)","VALIDATION"):n>=500?new i("Erreur serveur. Veuillez réessayer.","SERVER"):new i(o.response.data?.error||"Erreur lors de l'upload","SERVER")}else if(o.request)throw new i("Erreur de connexion. Vérifiez votre connexion internet.","NETWORK")}throw new i("Erreur inconnue","UNKNOWN")}}async function v(r){await t.delete(`/users/${r}/avatar`)}const U={getProfile:l,getProfileByUsername:p,updateProfile:f,calculateProfileCompletion:d,follow:w,unfollow:g,getFollowers:h,getFollowing:m,getSettings:E,updateSettings:y,uploadAvatar:$,deleteAvatar:v};export{g as a,w as f,l as g,U as u};
|
||||
|
Before Width: | Height: | Size: 1,002 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 638 B |
|
Before Width: | Height: | Size: 791 B |
|
|
@ -1,102 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Veza - Plateforme de streaming musical</title>
|
||||
|
||||
<!-- Google Fonts for Design System -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<!-- PERF: Preload fonts critiques uniquement, utiliser font-display: swap -->
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;900&family=Rajdhani:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600;700;900&family=Source+Serif+4:opsz,wght@8..60,400;8..60,500;8..60,600&family=JetBrains+Mono:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500&family=Noto+Sans+JP:wght@300;400;500;700;900&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
<!-- PERF: Preload stratégique des chunks vendors critiques (sera injecté par Vite en production) -->
|
||||
<!-- Les hashs seront générés automatiquement lors du build -->
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<!-- PWA Meta Tags -->
|
||||
<meta name="theme-color" content="#1a1a1a" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Veza" />
|
||||
|
||||
<!-- Apple Touch Icons -->
|
||||
<link rel="apple-touch-icon" href="/icons/icon-152x152.png" />
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/icons/icon-72x72.png" />
|
||||
<link rel="apple-touch-icon" sizes="96x96" href="/icons/icon-96x96.png" />
|
||||
<link rel="apple-touch-icon" sizes="128x128" href="/icons/icon-128x128.png" />
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/icons/icon-144x144.png" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/icons/icon-152x152.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="/icons/icon-192x192.png" />
|
||||
<link rel="apple-touch-icon" sizes="384x384" href="/icons/icon-384x384.png" />
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="/icons/icon-512x512.png" />
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-TileColor" content="#1a1a1a" />
|
||||
<meta name="msapplication-TileImage" content="/icons/icon-144x144.png" />
|
||||
|
||||
<!-- SEO and Social -->
|
||||
<meta name="description" content="Plateforme de streaming, collaboration et distribution musicale moderne" />
|
||||
<meta name="keywords" content="music, streaming, collaboration, audio, chat, veza" />
|
||||
<meta name="author" content="Veza Platform Team" />
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="Veza Platform" />
|
||||
<meta property="og:description" content="Plateforme de streaming, collaboration et distribution musicale moderne" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="/icons/icon-512x512.png" />
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="Veza Platform" />
|
||||
<meta name="twitter:description" content="Plateforme de streaming, collaboration et distribution musicale moderne" />
|
||||
<meta name="twitter:image" content="/icons/icon-512x512.png" />
|
||||
<!-- Theme Script to prevent FOUC -->
|
||||
<script>
|
||||
(function() {
|
||||
const storageKey = 'vite-ui-theme';
|
||||
const defaultTheme = 'dark';
|
||||
|
||||
try {
|
||||
const theme = localStorage.getItem(storageKey) || defaultTheme;
|
||||
const root = window.document.documentElement;
|
||||
|
||||
root.classList.remove('light', 'dark');
|
||||
|
||||
if (theme === 'system') {
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
: 'light';
|
||||
root.classList.add(systemTheme);
|
||||
} else {
|
||||
root.classList.add(theme);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to set initial theme', e);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-BWcMVITa.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-IYr-MHu4.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-react-BHG7lGYR.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-router-D-s5vIeO.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-BzWBL1hV.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-utils-CgOSfOkx.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-icons-DaGlTw4_.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/vendor-BD_zwJK7.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-COF3NoTx.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
{
|
||||
"name": "Veza Platform",
|
||||
"short_name": "Veza",
|
||||
"description": "Plateforme de streaming, collaboration et distribution musicale moderne",
|
||||
"theme_color": "#1a1a1a",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"scope": "/",
|
||||
"start_url": "/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-152x152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
}
|
||||
],
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Dashboard",
|
||||
"short_name": "Dashboard",
|
||||
"description": "Accéder au tableau de bord",
|
||||
"url": "/dashboard",
|
||||
"icons": [{ "src": "/icons/dashboard-96x96.png", "sizes": "96x96" }]
|
||||
},
|
||||
{
|
||||
"name": "Chat",
|
||||
"short_name": "Chat",
|
||||
"description": "Ouvrir le chat",
|
||||
"url": "/chat",
|
||||
"icons": [{ "src": "/icons/chat-96x96.png", "sizes": "96x96" }]
|
||||
},
|
||||
{
|
||||
"name": "Bibliothèque",
|
||||
"short_name": "Library",
|
||||
"description": "Parcourir la bibliothèque musicale",
|
||||
"url": "/library",
|
||||
"icons": [{ "src": "/icons/library-96x96.png", "sizes": "96x96" }]
|
||||
}
|
||||
],
|
||||
"categories": ["music", "entertainment", "social"],
|
||||
"screenshots": [
|
||||
{
|
||||
"src": "/screenshots/dashboard-wide.png",
|
||||
"sizes": "1280x720",
|
||||
"type": "image/png",
|
||||
"form_factor": "wide"
|
||||
},
|
||||
{
|
||||
"src": "/screenshots/dashboard-narrow.png",
|
||||
"sizes": "375x812",
|
||||
"type": "image/png",
|
||||
"form_factor": "narrow"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,349 +0,0 @@
|
|||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
|
||||
/**
|
||||
* Mock Service Worker.
|
||||
* @see https://github.com/mswjs/msw
|
||||
* - Please do NOT modify this file.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.12.3'
|
||||
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
||||
addEventListener('install', function () {
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
addEventListener('activate', function (event) {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
|
||||
addEventListener('message', async function (event) {
|
||||
const clientId = Reflect.get(event.source || {}, 'id')
|
||||
|
||||
if (!clientId || !self.clients) {
|
||||
return
|
||||
}
|
||||
|
||||
const client = await self.clients.get(clientId)
|
||||
|
||||
if (!client) {
|
||||
return
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll({
|
||||
type: 'window',
|
||||
})
|
||||
|
||||
switch (event.data) {
|
||||
case 'KEEPALIVE_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'KEEPALIVE_RESPONSE',
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'INTEGRITY_CHECK_REQUEST': {
|
||||
sendToClient(client, {
|
||||
type: 'INTEGRITY_CHECK_RESPONSE',
|
||||
payload: {
|
||||
packageVersion: PACKAGE_VERSION,
|
||||
checksum: INTEGRITY_CHECKSUM,
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'MOCK_ACTIVATE': {
|
||||
activeClientIds.add(clientId)
|
||||
|
||||
sendToClient(client, {
|
||||
type: 'MOCKING_ENABLED',
|
||||
payload: {
|
||||
client: {
|
||||
id: client.id,
|
||||
frameType: client.frameType,
|
||||
},
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case 'CLIENT_CLOSED': {
|
||||
activeClientIds.delete(clientId)
|
||||
|
||||
const remainingClients = allClients.filter((client) => {
|
||||
return client.id !== clientId
|
||||
})
|
||||
|
||||
// Unregister itself when there are no more clients
|
||||
if (remainingClients.length === 0) {
|
||||
self.registration.unregister()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
addEventListener('fetch', function (event) {
|
||||
const requestInterceptedAt = Date.now()
|
||||
|
||||
// Bypass navigation requests.
|
||||
if (event.request.mode === 'navigate') {
|
||||
return
|
||||
}
|
||||
|
||||
// Opening the DevTools triggers the "only-if-cached" request
|
||||
// that cannot be handled by the worker. Bypass such requests.
|
||||
if (
|
||||
event.request.cache === 'only-if-cached' &&
|
||||
event.request.mode !== 'same-origin'
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
// Bypass all requests when there are no active clients.
|
||||
// Prevents the self-unregistered worked from handling requests
|
||||
// after it's been terminated (still remains active until the next reload).
|
||||
if (activeClientIds.size === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const requestId = crypto.randomUUID()
|
||||
event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
|
||||
})
|
||||
|
||||
/**
|
||||
* @param {FetchEvent} event
|
||||
* @param {string} requestId
|
||||
* @param {number} requestInterceptedAt
|
||||
*/
|
||||
async function handleRequest(event, requestId, requestInterceptedAt) {
|
||||
const client = await resolveMainClient(event)
|
||||
const requestCloneForEvents = event.request.clone()
|
||||
const response = await getResponse(
|
||||
event,
|
||||
client,
|
||||
requestId,
|
||||
requestInterceptedAt,
|
||||
)
|
||||
|
||||
// Send back the response clone for the "response:*" life-cycle events.
|
||||
// Ensure MSW is active and ready to handle the message, otherwise
|
||||
// this message will pend indefinitely.
|
||||
if (client && activeClientIds.has(client.id)) {
|
||||
const serializedRequest = await serializeRequest(requestCloneForEvents)
|
||||
|
||||
// Clone the response so both the client and the library could consume it.
|
||||
const responseClone = response.clone()
|
||||
|
||||
sendToClient(
|
||||
client,
|
||||
{
|
||||
type: 'RESPONSE',
|
||||
payload: {
|
||||
isMockedResponse: IS_MOCKED_RESPONSE in response,
|
||||
request: {
|
||||
id: requestId,
|
||||
...serializedRequest,
|
||||
},
|
||||
response: {
|
||||
type: responseClone.type,
|
||||
status: responseClone.status,
|
||||
statusText: responseClone.statusText,
|
||||
headers: Object.fromEntries(responseClone.headers.entries()),
|
||||
body: responseClone.body,
|
||||
},
|
||||
},
|
||||
},
|
||||
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
|
||||
)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the main client for the given event.
|
||||
* Client that issues a request doesn't necessarily equal the client
|
||||
* that registered the worker. It's with the latter the worker should
|
||||
* communicate with during the response resolving phase.
|
||||
* @param {FetchEvent} event
|
||||
* @returns {Promise<Client | undefined>}
|
||||
*/
|
||||
async function resolveMainClient(event) {
|
||||
const client = await self.clients.get(event.clientId)
|
||||
|
||||
if (activeClientIds.has(event.clientId)) {
|
||||
return client
|
||||
}
|
||||
|
||||
if (client?.frameType === 'top-level') {
|
||||
return client
|
||||
}
|
||||
|
||||
const allClients = await self.clients.matchAll({
|
||||
type: 'window',
|
||||
})
|
||||
|
||||
return allClients
|
||||
.filter((client) => {
|
||||
// Get only those clients that are currently visible.
|
||||
return client.visibilityState === 'visible'
|
||||
})
|
||||
.find((client) => {
|
||||
// Find the client ID that's recorded in the
|
||||
// set of clients that have registered the worker.
|
||||
return activeClientIds.has(client.id)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FetchEvent} event
|
||||
* @param {Client | undefined} client
|
||||
* @param {string} requestId
|
||||
* @param {number} requestInterceptedAt
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
async function getResponse(event, client, requestId, requestInterceptedAt) {
|
||||
// Clone the request because it might've been already used
|
||||
// (i.e. its body has been read and sent to the client).
|
||||
const requestClone = event.request.clone()
|
||||
|
||||
function passthrough() {
|
||||
// Cast the request headers to a new Headers instance
|
||||
// so the headers can be manipulated with.
|
||||
const headers = new Headers(requestClone.headers)
|
||||
|
||||
// Remove the "accept" header value that marked this request as passthrough.
|
||||
// This prevents request alteration and also keeps it compliant with the
|
||||
// user-defined CORS policies.
|
||||
const acceptHeader = headers.get('accept')
|
||||
if (acceptHeader) {
|
||||
const values = acceptHeader.split(',').map((value) => value.trim())
|
||||
const filteredValues = values.filter(
|
||||
(value) => value !== 'msw/passthrough',
|
||||
)
|
||||
|
||||
if (filteredValues.length > 0) {
|
||||
headers.set('accept', filteredValues.join(', '))
|
||||
} else {
|
||||
headers.delete('accept')
|
||||
}
|
||||
}
|
||||
|
||||
return fetch(requestClone, { headers })
|
||||
}
|
||||
|
||||
// Bypass mocking when the client is not active.
|
||||
if (!client) {
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
// Bypass initial page load requests (i.e. static assets).
|
||||
// The absence of the immediate/parent client in the map of the active clients
|
||||
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
|
||||
// and is not ready to handle requests.
|
||||
if (!activeClientIds.has(client.id)) {
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
// Notify the client that a request has been intercepted.
|
||||
const serializedRequest = await serializeRequest(event.request)
|
||||
const clientMessage = await sendToClient(
|
||||
client,
|
||||
{
|
||||
type: 'REQUEST',
|
||||
payload: {
|
||||
id: requestId,
|
||||
interceptedAt: requestInterceptedAt,
|
||||
...serializedRequest,
|
||||
},
|
||||
},
|
||||
[serializedRequest.body],
|
||||
)
|
||||
|
||||
switch (clientMessage.type) {
|
||||
case 'MOCK_RESPONSE': {
|
||||
return respondWithMock(clientMessage.data)
|
||||
}
|
||||
|
||||
case 'PASSTHROUGH': {
|
||||
return passthrough()
|
||||
}
|
||||
}
|
||||
|
||||
return passthrough()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Client} client
|
||||
* @param {any} message
|
||||
* @param {Array<Transferable>} transferrables
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
function sendToClient(client, message, transferrables = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const channel = new MessageChannel()
|
||||
|
||||
channel.port1.onmessage = (event) => {
|
||||
if (event.data && event.data.error) {
|
||||
return reject(event.data.error)
|
||||
}
|
||||
|
||||
resolve(event.data)
|
||||
}
|
||||
|
||||
client.postMessage(message, [
|
||||
channel.port2,
|
||||
...transferrables.filter(Boolean),
|
||||
])
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Response} response
|
||||
* @returns {Response}
|
||||
*/
|
||||
function respondWithMock(response) {
|
||||
// Setting response status code to 0 is a no-op.
|
||||
// However, when responding with a "Response.error()", the produced Response
|
||||
// instance will have status code set to 0. Since it's not possible to create
|
||||
// a Response instance with status code 0, handle that use-case separately.
|
||||
if (response.status === 0) {
|
||||
return Response.error()
|
||||
}
|
||||
|
||||
const mockedResponse = new Response(response.body, response)
|
||||
|
||||
Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
|
||||
value: true,
|
||||
enumerable: true,
|
||||
})
|
||||
|
||||
return mockedResponse
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Request} request
|
||||
*/
|
||||
async function serializeRequest(request) {
|
||||
return {
|
||||
url: request.url,
|
||||
mode: request.mode,
|
||||
method: request.method,
|
||||
headers: Object.fromEntries(request.headers.entries()),
|
||||
cache: request.cache,
|
||||
credentials: request.credentials,
|
||||
destination: request.destination,
|
||||
integrity: request.integrity,
|
||||
redirect: request.redirect,
|
||||
referrer: request.referrer,
|
||||
referrerPolicy: request.referrerPolicy,
|
||||
body: await request.arrayBuffer(),
|
||||
keepalive: request.keepalive,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,396 +0,0 @@
|
|||
// Veza Platform Service Worker
|
||||
// Version 1.0.0
|
||||
|
||||
const CACHE_VERSION = '__BUILD_VERSION__';
|
||||
const CACHE_NAME = `veza-platform-${CACHE_VERSION}`;
|
||||
const STATIC_CACHE_NAME = `veza-static-${CACHE_VERSION}`;
|
||||
const DYNAMIC_CACHE_NAME = `veza-dynamic-${CACHE_VERSION}`;
|
||||
|
||||
// Files to cache on install
|
||||
const STATIC_ASSETS = [
|
||||
'/',
|
||||
'/dashboard',
|
||||
'/chat',
|
||||
'/library',
|
||||
'/profile',
|
||||
'/settings',
|
||||
'/manifest.json',
|
||||
'/icons/icon-192x192.png',
|
||||
'/icons/icon-512x512.png'
|
||||
];
|
||||
|
||||
// API endpoints to cache with network-first strategy
|
||||
const API_CACHE_PATTERNS = [
|
||||
/^https?:\/\/.*\/api\/v1\/user\/profile$/,
|
||||
/^https?:\/\/.*\/api\/v1\/library\/files$/,
|
||||
/^https?:\/\/.*\/api\/v1\/dashboard\/stats$/
|
||||
];
|
||||
|
||||
// Install event - cache static assets
|
||||
// CRITICAL FIX: Service Worker disabled - this code is kept for reference but should not execute
|
||||
// The Service Worker registration is disabled in pwa.ts to prevent React.Children cache issues
|
||||
self.addEventListener('install', (event) => {
|
||||
console.log('[SW] Service Worker install event - DISABLED to prevent cache conflicts');
|
||||
|
||||
// CRITICAL FIX: Skip waiting and immediately activate to avoid caching old chunks
|
||||
event.waitUntil(
|
||||
Promise.all([
|
||||
// Delete all existing caches to prevent serving old JS chunks
|
||||
caches.keys().then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames.map((cacheName) => {
|
||||
console.log('[SW] Deleting old cache:', cacheName);
|
||||
return caches.delete(cacheName);
|
||||
})
|
||||
);
|
||||
}),
|
||||
// Skip waiting immediately
|
||||
self.skipWaiting()
|
||||
]).then(() => {
|
||||
console.log('[SW] All caches cleared, skipping waiting');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Activate event - clean old caches
|
||||
// CRITICAL FIX: Aggressively clear all caches to prevent serving old JS chunks
|
||||
self.addEventListener('activate', (event) => {
|
||||
console.log('[SW] Activating service worker - clearing all caches');
|
||||
|
||||
event.waitUntil(
|
||||
Promise.all([
|
||||
// Delete ALL caches to prevent serving old JS chunks
|
||||
caches.keys().then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames.map((cacheName) => {
|
||||
console.log('[SW] Deleting cache:', cacheName);
|
||||
return caches.delete(cacheName);
|
||||
})
|
||||
);
|
||||
}),
|
||||
// Take control immediately
|
||||
self.clients.claim()
|
||||
]).then(() => {
|
||||
console.log('[SW] All caches cleared, service worker activated');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Fetch event - handle requests with appropriate caching strategy
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const { request } = event;
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Skip non-GET requests
|
||||
if (request.method !== 'GET') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip WebSocket connections
|
||||
if (request.headers.get('upgrade') === 'websocket') {
|
||||
return;
|
||||
}
|
||||
|
||||
// CRITICAL FIX: Skip service worker registration and JS chunks to avoid blocking
|
||||
// These must always come from network to ensure latest code
|
||||
if (url.pathname.includes('/sw.js') ||
|
||||
url.pathname.includes('/js/') ||
|
||||
url.pathname.includes('/assets/')) {
|
||||
return; // Let browser fetch directly from network
|
||||
}
|
||||
|
||||
// Skip external requests (except API)
|
||||
if (!url.origin.includes(self.location.origin) && !isApiRequest(request.url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.respondWith(
|
||||
handleRequest(request)
|
||||
);
|
||||
});
|
||||
|
||||
// Handle different types of requests with appropriate strategies
|
||||
async function handleRequest(request) {
|
||||
try {
|
||||
// Strategy 1: Cache First for static assets
|
||||
if (isStaticAsset(request.url)) {
|
||||
return await cacheFirst(request, STATIC_CACHE_NAME);
|
||||
}
|
||||
|
||||
// Strategy 2: Network First for API requests
|
||||
if (isApiRequest(request.url)) {
|
||||
return await networkFirst(request, DYNAMIC_CACHE_NAME);
|
||||
}
|
||||
|
||||
// Strategy 3: Stale While Revalidate for pages
|
||||
if (isPageRequest(request.url)) {
|
||||
return await staleWhileRevalidate(request, DYNAMIC_CACHE_NAME);
|
||||
}
|
||||
|
||||
// Default: Network only
|
||||
return await fetch(request);
|
||||
|
||||
} catch (error) {
|
||||
console.error('[SW] Request failed:', error);
|
||||
|
||||
// Return offline page for navigation requests
|
||||
if (isPageRequest(request.url)) {
|
||||
return await getOfflinePage();
|
||||
}
|
||||
|
||||
// Return cached version if available
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// Return generic offline response
|
||||
return new Response('Offline', {
|
||||
status: 503,
|
||||
statusText: 'Service Unavailable'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Cache First strategy
|
||||
async function cacheFirst(request, cacheName) {
|
||||
const cachedResponse = await caches.match(request);
|
||||
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
const networkResponse = await fetch(request);
|
||||
|
||||
if (networkResponse.ok) {
|
||||
const cache = await caches.open(cacheName);
|
||||
cache.put(request, networkResponse.clone());
|
||||
}
|
||||
|
||||
return networkResponse;
|
||||
}
|
||||
|
||||
// Network First strategy
|
||||
async function networkFirst(request, cacheName) {
|
||||
try {
|
||||
const networkResponse = await fetch(request);
|
||||
|
||||
if (networkResponse.ok) {
|
||||
const cache = await caches.open(cacheName);
|
||||
cache.put(request, networkResponse.clone());
|
||||
}
|
||||
|
||||
return networkResponse;
|
||||
} catch (error) {
|
||||
const cachedResponse = await caches.match(request);
|
||||
|
||||
if (cachedResponse) {
|
||||
console.log('[SW] Serving cached API response');
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Stale While Revalidate strategy
|
||||
// CORRECTION DURABLE: Clone la réponse IMMÉDIATEMENT pour éviter "Response body is already used"
|
||||
async function staleWhileRevalidate(request, cacheName) {
|
||||
const cachedResponse = await caches.match(request);
|
||||
|
||||
const networkResponsePromise = fetch(request)
|
||||
.then((networkResponse) => {
|
||||
if (networkResponse.ok) {
|
||||
// ✅ Cloner IMMÉDIATEMENT la réponse avant toute autre opération
|
||||
const responseToCache = networkResponse.clone();
|
||||
|
||||
// Mettre en cache de manière asynchrone (sans bloquer)
|
||||
caches.open(cacheName).then((cache) => {
|
||||
cache.put(request, responseToCache).catch((err) => {
|
||||
console.warn('[SW] Failed to cache response:', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
return networkResponse;
|
||||
})
|
||||
.catch(() => null);
|
||||
|
||||
return cachedResponse || await networkResponsePromise;
|
||||
}
|
||||
|
||||
// Get offline page
|
||||
async function getOfflinePage() {
|
||||
const cache = await caches.open(STATIC_CACHE_NAME);
|
||||
const offlineResponse = await cache.match('/');
|
||||
|
||||
if (offlineResponse) {
|
||||
return offlineResponse;
|
||||
}
|
||||
|
||||
return new Response(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Veza - Hors ligne</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, sans-serif;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
background: #1a1a1a;
|
||||
color: white;
|
||||
}
|
||||
.offline-container {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
background: #2a2a2a;
|
||||
}
|
||||
.offline-icon {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="offline-container">
|
||||
<div class="offline-icon">📱</div>
|
||||
<h1>Veza - Mode Hors Ligne</h1>
|
||||
<p>Vous êtes actuellement hors ligne. Certaines fonctionnalités peuvent être limitées.</p>
|
||||
<button onclick="window.location.reload()">Réessayer</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`, {
|
||||
headers: { 'Content-Type': 'text/html' }
|
||||
});
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
function isStaticAsset(url) {
|
||||
return /\.(js|css|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|ico)(\?.*)?$/.test(url);
|
||||
}
|
||||
|
||||
function isApiRequest(url) {
|
||||
return url.includes('/api/') || API_CACHE_PATTERNS.some(pattern => pattern.test(url));
|
||||
}
|
||||
|
||||
function isPageRequest(url) {
|
||||
const urlObj = new URL(url);
|
||||
return urlObj.pathname.match(/^\/[^.]*$/) && !isApiRequest(url);
|
||||
}
|
||||
|
||||
// Message handling for communication with the main thread
|
||||
self.addEventListener('message', (event) => {
|
||||
const { type } = event.data;
|
||||
|
||||
switch (type) {
|
||||
case 'SKIP_WAITING':
|
||||
self.skipWaiting();
|
||||
break;
|
||||
|
||||
case 'GET_VERSION':
|
||||
event.ports[0].postMessage({
|
||||
type: 'VERSION',
|
||||
payload: { version: CACHE_NAME }
|
||||
});
|
||||
break;
|
||||
|
||||
case 'CLEAR_CACHE':
|
||||
caches.keys().then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames.map((cacheName) => caches.delete(cacheName))
|
||||
);
|
||||
}).then(() => {
|
||||
event.ports[0].postMessage({
|
||||
type: 'CACHE_CLEARED',
|
||||
payload: { success: true }
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('[SW] Unknown message type:', type);
|
||||
}
|
||||
});
|
||||
|
||||
// Background sync for offline actions
|
||||
self.addEventListener('sync', (event) => {
|
||||
if (event.tag === 'background-sync') {
|
||||
event.waitUntil(doBackgroundSync());
|
||||
}
|
||||
});
|
||||
|
||||
async function doBackgroundSync() {
|
||||
console.log('[SW] Performing background sync...');
|
||||
// Implement background sync logic here
|
||||
// For example: sync offline messages, upload queued files, etc.
|
||||
}
|
||||
|
||||
// Push notifications
|
||||
self.addEventListener('push', (event) => {
|
||||
if (!event.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = event.data.json();
|
||||
const options = {
|
||||
body: data.body,
|
||||
icon: '/icons/icon-192x192.png',
|
||||
badge: '/icons/badge-72x72.png',
|
||||
vibrate: [100, 50, 100],
|
||||
data: {
|
||||
dateOfArrival: Date.now(),
|
||||
primaryKey: data.primaryKey || 1,
|
||||
url: data.url || '/'
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
action: 'explore',
|
||||
title: 'Ouvrir',
|
||||
icon: '/icons/checkmark.png'
|
||||
},
|
||||
{
|
||||
action: 'close',
|
||||
title: 'Fermer',
|
||||
icon: '/icons/xmark.png'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
event.waitUntil(
|
||||
self.registration.showNotification(data.title, options)
|
||||
);
|
||||
});
|
||||
|
||||
// Notification click handling
|
||||
self.addEventListener('notificationclick', (event) => {
|
||||
event.notification.close();
|
||||
|
||||
if (event.action === 'close') {
|
||||
return;
|
||||
}
|
||||
|
||||
const urlToOpen = event.notification.data?.url || '/';
|
||||
|
||||
event.waitUntil(
|
||||
self.clients.matchAll({ type: 'window' }).then((clientList) => {
|
||||
// Check if a window is already open
|
||||
for (const client of clientList) {
|
||||
if (client.url === urlToOpen && 'focus' in client) {
|
||||
return client.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Open a new window
|
||||
if (self.clients.openWindow) {
|
||||
return self.clients.openWindow(urlToOpen);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
console.log('[SW] Veza Platform Service Worker loaded');
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
* - Please do NOT modify this file.
|
||||
*/
|
||||
|
||||
const PACKAGE_VERSION = '2.12.3'
|
||||
const PACKAGE_VERSION = '2.12.7'
|
||||
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
|
||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
||||
const activeClientIds = new Set()
|
||||
|
|
|
|||
53
apps/web/scripts/analyze-errors.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
// Fix type: "module" issue by using readFileSync if running as module or just basic node
|
||||
// but easier to just use standard require if not module, or import fs if module.
|
||||
// Package.json says type: module, so imports are fine.
|
||||
|
||||
try {
|
||||
const errorData = JSON.parse(fs.readFileSync('storybook_errors.json', 'utf8'));
|
||||
const totalFailed = Object.keys(errorData).length;
|
||||
|
||||
const stats = {
|
||||
totalFailed,
|
||||
byType: {},
|
||||
commonMessages: {}
|
||||
};
|
||||
|
||||
for (const [storyId, errors] of Object.entries(errorData)) {
|
||||
for (const error of errors) {
|
||||
let type = 'Unknown';
|
||||
if (error.startsWith('Console:')) type = 'Console';
|
||||
if (error.startsWith('PageError:')) type = 'PageError';
|
||||
if (error.startsWith('NetworkFail:')) type = 'NetworkFail';
|
||||
if (error.startsWith('Navigation:')) type = 'Navigation';
|
||||
|
||||
stats.byType[type] = (stats.byType[type] || 0) + 1;
|
||||
|
||||
// Extract core message for grouping
|
||||
const message = error.substring(error.indexOf(':') + 1).trim();
|
||||
// Truncate simple variable parts
|
||||
const cleanMessage = message.split(' at ')[0].substring(0, 100);
|
||||
stats.commonMessages[cleanMessage] = (stats.commonMessages[cleanMessage] || 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('--- Error Summary ---');
|
||||
console.log(`Total Stories with Errors: ${stats.totalFailed}`);
|
||||
console.log('\nErrors by Type:');
|
||||
console.table(stats.byType);
|
||||
|
||||
console.log('\nTop 10 Common Error Messages:');
|
||||
const sortedMessages = Object.entries(stats.commonMessages)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, 10);
|
||||
|
||||
sortedMessages.forEach(([msg, count]) => {
|
||||
console.log(`${count}x: ${msg}`);
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.error('Failed to parse errors:', e);
|
||||
}
|
||||
33
apps/web/scripts/debug-story.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
async function debugStory(storyId) {
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage();
|
||||
const storyUrl = `http://localhost:6007/iframe.html?id=${storyId}&viewMode=story`;
|
||||
|
||||
console.log(`Navigating to ${storyUrl}`);
|
||||
|
||||
const errors = [];
|
||||
page.on('pageerror', err => errors.push(`PageError: ${err.message}`));
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') errors.push(`ConsoleError: ${msg.text()}`);
|
||||
});
|
||||
page.on('requestfailed', request => {
|
||||
errors.push(`NetworkFail: ${request.failure().errorText} ${request.url()}`);
|
||||
});
|
||||
|
||||
try {
|
||||
await page.goto(storyUrl, { waitUntil: 'networkidle' });
|
||||
// Wait a bit for async errors
|
||||
await page.waitForTimeout(2000);
|
||||
} catch (e) {
|
||||
errors.push(`NavigationError: ${e.message}`);
|
||||
}
|
||||
|
||||
console.log('Errors found:', JSON.stringify(errors, null, 2));
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
const storyId = process.argv[2] || 'components-admin-adminauditlogsview--default';
|
||||
debugStory(storyId);
|
||||
106
apps/web/scripts/wrap_msw_handlers.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to wrap MSW handler responses in the correct API format.
|
||||
Converts: HttpResponse.json({ data })
|
||||
To: HttpResponse.json({ success: true, data: { data } })
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
def wrap_handler_response(content):
|
||||
"""
|
||||
Find and wrap MSW handler responses that aren't already wrapped.
|
||||
"""
|
||||
# Pattern to match http.get/post/etc handlers
|
||||
# This matches: http.METHOD('pattern', (...) => { return HttpResponse.json({ ... }); })
|
||||
|
||||
lines = content.split('\n')
|
||||
result = []
|
||||
i = 0
|
||||
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
|
||||
# Check if this is a handler definition
|
||||
if re.match(r'\s*http\.(get|post|put|delete|patch|all)\(', line):
|
||||
# Find the return HttpResponse.json statement
|
||||
handler_start = i
|
||||
brace_count = 0
|
||||
in_handler = False
|
||||
|
||||
for j in range(i, min(i + 100, len(lines))): # Look ahead up to 100 lines
|
||||
current_line = lines[j]
|
||||
|
||||
# Track braces to know when we're inside the handler
|
||||
brace_count += current_line.count('{') - current_line.count('}')
|
||||
|
||||
# Look for HttpResponse.json(
|
||||
if 'HttpResponse.json(' in current_line:
|
||||
# Check if already wrapped (has 'success: true')
|
||||
# Look ahead a few lines to see if success: true exists
|
||||
is_wrapped = False
|
||||
for k in range(j, min(j + 5, len(lines))):
|
||||
if 'success:' in lines[k] or 'success :' in lines[k]:
|
||||
is_wrapped = True
|
||||
break
|
||||
|
||||
if not is_wrapped and 'message:' not in current_line:
|
||||
# This needs wrapping
|
||||
# Find the opening brace after HttpResponse.json(
|
||||
indent = len(current_line) - len(current_line.lstrip())
|
||||
|
||||
# Add the wrapper
|
||||
result.append(current_line.replace(
|
||||
'HttpResponse.json({',
|
||||
'HttpResponse.json({\n' + ' ' * (indent + 2) + 'success: true,\n' + ' ' * (indent + 2) + 'data: {'
|
||||
))
|
||||
|
||||
# Now we need to find the closing brace and add an extra }
|
||||
json_brace_count = 1
|
||||
for k in range(j + 1, len(lines)):
|
||||
check_line = lines[k]
|
||||
json_brace_count += check_line.count('{') - check_line.count('}')
|
||||
|
||||
if json_brace_count == 0:
|
||||
# This is the closing brace
|
||||
# Add extra closing brace before this one
|
||||
result.append(' ' * (indent + 2) + '}')
|
||||
result.append(check_line)
|
||||
i = k + 1
|
||||
break
|
||||
else:
|
||||
result.append(check_line)
|
||||
break
|
||||
else:
|
||||
# Already wrapped or special case, just copy
|
||||
result.append(current_line)
|
||||
i = j + 1
|
||||
break
|
||||
elif j == i + 99 or (j > i and brace_count == 0):
|
||||
# Didn't find HttpResponse.json, just copy the line
|
||||
result.append(line)
|
||||
i += 1
|
||||
break
|
||||
else:
|
||||
result.append(line)
|
||||
i += 1
|
||||
|
||||
return '\n'.join(result)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python wrap_msw_handlers.py <handlers_file>")
|
||||
sys.exit(1)
|
||||
|
||||
filepath = sys.argv[1]
|
||||
|
||||
with open(filepath, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
wrapped_content = wrap_handler_response(content)
|
||||
|
||||
with open(filepath, 'w') as f:
|
||||
f.write(wrapped_content)
|
||||
|
||||
print(f"✅ Wrapped MSW handlers in {filepath}")
|
||||
|
|
@ -8,7 +8,7 @@ import { AdminAuditLogsView } from './AdminAuditLogsView';
|
|||
* filtrage et détails contextuels.
|
||||
*/
|
||||
const meta: Meta<typeof AdminAuditLogsView> = {
|
||||
title: 'Components/Admin/AdminAuditLogsView',
|
||||
title: 'Components/Features/Admin/AdminAuditLogsView',
|
||||
component: AdminAuditLogsView,
|
||||
parameters: {
|
||||
layout: 'fullscreen',
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { ToastProvider } from '../../components/feedback/ToastProvider';
|
|||
* visualisation du trafic, queue de modération et logs système.
|
||||
*/
|
||||
const meta: Meta<typeof AdminDashboardView> = {
|
||||
title: 'Components/Admin/AdminDashboardView',
|
||||
title: 'Components/Features/Admin/AdminDashboardView',
|
||||
component: AdminDashboardView,
|
||||
parameters: {
|
||||
layout: 'fullscreen',
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { ToastProvider } from '../../components/feedback/ToastProvider';
|
|||
* pour pending/reviewed/resolved et actions de modération.
|
||||
*/
|
||||
const meta: Meta<typeof AdminModerationView> = {
|
||||
title: 'Components/Admin/AdminModerationView',
|
||||
title: 'Components/Features/Admin/AdminModerationView',
|
||||
component: AdminModerationView,
|
||||
parameters: {
|
||||
layout: 'fullscreen',
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { AdminSettingsView } from './AdminSettingsView';
|
|||
* mode maintenance et annonces globales.
|
||||
*/
|
||||
const meta: Meta<typeof AdminSettingsView> = {
|
||||
title: 'Components/Admin/AdminSettingsView',
|
||||
title: 'Components/Features/Admin/AdminSettingsView',
|
||||
component: AdminSettingsView,
|
||||
parameters: {
|
||||
layout: 'padded',
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { AdminUsersView } from './AdminUsersView';
|
|||
* et actions de modération (ban, suppression, rôles).
|
||||
*/
|
||||
const meta: Meta<typeof AdminUsersView> = {
|
||||
title: 'Components/Admin/AdminUsersView',
|
||||
title: 'Components/Features/Admin/AdminUsersView',
|
||||
component: AdminUsersView,
|
||||
parameters: {
|
||||
layout: 'fullscreen',
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const createMockUser = (overrides: Partial<User> = {}): User => ({
|
|||
* avec menu d'actions contextuel.
|
||||
*/
|
||||
const meta: Meta<typeof UserTableRow> = {
|
||||
title: 'Components/Admin/UserTableRow',
|
||||
title: 'Components/Features/Admin/UserTableRow',
|
||||
component: UserTableRow,
|
||||
parameters: {
|
||||
docs: {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { BanUserModal } from './BanUserModal';
|
|||
* d'un utilisateur avec raison, durée et notes internes.
|
||||
*/
|
||||
const meta: Meta<typeof BanUserModal> = {
|
||||
title: 'Components/Admin/Modals/BanUserModal',
|
||||
title: 'Components/Features/Admin/Modals/BanUserModal',
|
||||
component: BanUserModal,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||
import { TrackAnalyticsView } from './TrackAnalyticsView';
|
||||
|
||||
const meta: Meta<typeof TrackAnalyticsView> = {
|
||||
title: 'Components/Analytics/TrackAnalyticsView',
|
||||
title: 'Components/Features/Analytics/TrackAnalyticsView',
|
||||
component: TrackAnalyticsView,
|
||||
parameters: { layout: 'padded' },
|
||||
tags: ['autodocs'],
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const MOCK_ITEM = {
|
|||
};
|
||||
|
||||
const meta: Meta<typeof CartItem> = {
|
||||
title: 'Components/Commerce/CartItem',
|
||||
title: 'Components/Features/Commerce/CartItem',
|
||||
component: CartItem,
|
||||
parameters: { layout: 'padded' },
|
||||
tags: ['autodocs'],
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||
import { OrderSummary } from './OrderSummary';
|
||||
|
||||
const meta: Meta<typeof OrderSummary> = {
|
||||
title: 'Components/Commerce/OrderSummary',
|
||||
title: 'Components/Features/Commerce/OrderSummary',
|
||||
component: OrderSummary,
|
||||
parameters: { layout: 'padded' },
|
||||
tags: ['autodocs'],
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { WishlistView } from './WishlistView';
|
|||
import { ToastProvider } from '../../components/feedback/ToastProvider';
|
||||
|
||||
const meta: Meta<typeof WishlistView> = {
|
||||
title: 'Components/Commerce/WishlistView',
|
||||
title: 'Components/Features/Commerce/WishlistView',
|
||||
component: WishlistView,
|
||||
parameters: { layout: 'fullscreen' },
|
||||
tags: ['autodocs'],
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||
import { ActivityGraph } from './ActivityGraph';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Dashboard/ActivityGraph',
|
||||
title: 'Components/Features/Dashboard/ActivityGraph',
|
||||
component: ActivityGraph,
|
||||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { StatCard } from './StatCard';
|
|||
import { Activity, Music, Users, DollarSign } from 'lucide-react';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Dashboard/StatCard',
|
||||
title: 'Components/Features/Dashboard/StatCard',
|
||||
component: StatCard,
|
||||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||
import { TrackList } from './TrackList';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Dashboard/TrackList',
|
||||
title: 'Components/Features/Dashboard/TrackList',
|
||||
component: TrackList,
|
||||
tags: ['autodocs'],
|
||||
} satisfies Meta<typeof TrackList>;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||
import { APIPlaygroundView } from './APIPlaygroundView';
|
||||
|
||||
const meta: Meta<typeof APIPlaygroundView> = {
|
||||
title: 'Components/Developer/APIPlaygroundView',
|
||||
title: 'Components/Features/Developer/APIPlaygroundView',
|
||||
component: APIPlaygroundView,
|
||||
parameters: { layout: 'fullscreen' },
|
||||
tags: ['autodocs'],
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { DeveloperDashboardView } from './DeveloperDashboardView';
|
|||
import { ToastProvider } from '../../components/feedback/ToastProvider';
|
||||
|
||||
const meta: Meta<typeof DeveloperDashboardView> = {
|
||||
title: 'Components/Developer/DeveloperDashboardView',
|
||||
title: 'Components/Features/Developer/DeveloperDashboardView',
|
||||
component: DeveloperDashboardView,
|
||||
parameters: { layout: 'fullscreen' },
|
||||
tags: ['autodocs'],
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||
import { WebhooksView } from './WebhooksView';
|
||||
|
||||
const meta: Meta<typeof WebhooksView> = {
|
||||
title: 'Components/Developer/WebhooksView',
|
||||
title: 'Components/Features/Developer/WebhooksView',
|
||||
component: WebhooksView,
|
||||
parameters: { layout: 'fullscreen' },
|
||||
tags: ['autodocs'],
|
||||
|
|
|
|||