Bloc A - Code mort: - Suppression Studio (components, views, features) - Suppression gamification + services mock (projectService, storageService, gamificationService) - Mise à jour Sidebar, Navbar, locales Bloc B - Frontend: - Suppression modal.tsx deprecated, Modal.stories (doublon Dialog) - Feature flags: PLAYLIST_SEARCH, PLAYLIST_RECOMMENDATIONS, ROLE_MANAGEMENT = true - Suppression 19 tests orphelins, retrait exclusions vitest.config Bloc C - Backend: - Extraction routes_auth.go depuis router.go Bloc D - Rust: - Suppression security_legacy.rs (code mort, patterns déjà dans security/)
1 line
13 KiB
JavaScript
1 line
13 KiB
JavaScript
import{a as l,j as t,R as F}from"./vendor-react-yWUy5XPk.js";import{S as f,C as V,B as W,g as U,l as Y}from"./index-CYK_b1Uz.js";import{P as Q}from"./progress-C_BG9DJ7.js";import{aF as T,K as k,c as z,aG as L,d as H,U as Z,aH as X,O as K}from"./vendor-icons-DJFb1Tiw.js";import{S as _}from"./input-CGdBHtsQ.js";import{m as E}from"./vendor-motion-B3XPS3Jc.js";import"./vendor-CveO81sn.js";import"./vendor-security-DsrNJhpn.js";import"./vendor-router-BNNHboN9.js";import"./vendor-tanstack-kPY9uK0s.js";import"./vendor-http-Cz8wfb0q.js";import"./vendor-utils-DtoSyhX2.js";import"./vendor-i18n-CMcqpBLz.js";function q(e,{threshold:r=0,root:n=null,rootMargin:s="0%",freezeOnceVisible:i=!1}){const[c,a]=l.useState(),o=c?.isIntersecting&&i;return l.useEffect(()=>{const x=e?.current;if(!!!window.IntersectionObserver||o||!x)return;const u={threshold:r,root:n,rootMargin:s},m=new IntersectionObserver(([A])=>{a(A)},u);return m.observe(x),()=>m.disconnect()},[e,r,n,s,o]),c}function J({blurDataURL:e,width:r,height:n,className:s=""}){return e?t.jsx("img",{src:e,alt:"",className:`blur-sm ${s}`,style:{width:r,height:n},"aria-hidden":"true"}):t.jsx(f,{className:s,style:{width:r,height:n}})}const $=["webp","avif","jpeg","png","gif"],ee="jpeg";function te(e,r){const n=e.replace(/\.[^/.]+$/,""),s=r??"100vw";return $.map(i=>({src:`${n}.${i}`,type:`image/${i}`,sizes:s}))}function se(){const[e,r]=l.useState([]);return l.useEffect(()=>{(async()=>{const s=[],i=await new Promise(a=>{const o=new Image;o.onload=o.onerror=()=>a(o.height===2),o.src="data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA"}),c=await new Promise(a=>{const o=new Image;o.onload=o.onerror=()=>a(o.height===2),o.src="data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAACAAIABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgABogQEAwgMgkAAAAAAAAG8AAAAA=="});i&&s.push("webp"),c&&s.push("avif"),s.push("jpeg","png","gif"),r(s)})()},[]),e}function re({src:e,alt:r,width:n,height:s,className:i="",placeholder:c,blurDataURL:a,priority:o=!1,quality:x=75,sizes:g="100vw",onLoad:u,onError:m,fallback:A}){const[d,v]=l.useState(!1),[b,j]=l.useState(!1),[G,R]=l.useState(null),M=l.useRef(null),N=se(),C=l.useRef(null),S=!!q(C,{threshold:.1,rootMargin:"50px"})?.isIntersecting,w=F.useMemo(()=>te(e,g),[e,g]),I=l.useCallback(()=>{const p=N.find(y=>$.includes(y))||ee;return w.find(y=>y.type===`image/${p}`)?.src||e},[N,w,e]),B=l.useCallback(()=>{if(d||b)return;const p=I();R(p);const h=new Image;h.onload=()=>{v(!0),u?.()},h.onerror=()=>{j(!0),m?.()},h.src=p},[d,b,I,u,m]);l.useEffect(()=>{(o||S)&&B()},[o,S,B]);const D=l.useCallback(()=>{j(!0),m?.()},[m]),O=l.useCallback(()=>{v(!0),u?.()},[u]);return b?A||t.jsx("div",{className:`bg-muted flex items-center justify-center ${i}`,style:{width:n,height:s},children:t.jsx("span",{className:"text-muted-foreground text-sm",children:"Image non disponible"})}):!d&&!o?t.jsxs("div",{ref:C,className:`relative ${i}`,style:{width:n,height:s},children:[t.jsx(J,{blurDataURL:a,width:n,height:s,className:"absolute inset-0"}),c&&t.jsx("div",{className:"absolute inset-0 flex items-center justify-center",children:c})]}):t.jsxs("picture",{className:i,children:[w.map((p,h)=>t.jsx("source",{srcSet:p.src,type:p.type,sizes:p.sizes},`${p.type}-${h}`)),t.jsx("img",{ref:M,src:G||e,alt:r,width:n,height:s,className:`transition-opacity duration-[var(--sumi-duration-normal)] ${d?"opacity-100":"opacity-0"} ${i}`,onLoad:O,onError:D,loading:o?"eager":"lazy",decoding:"async",style:{width:n,height:s}})]})}function ae(e){return!e.modules||e.modules.length===0?0:e.modules.reduce((r,n)=>r+(n.lessons?.length??0),0)}const ne=({course:e,onClick:r,showProgress:n=!1})=>{const s=ae(e);return t.jsx("article",{children:t.jsxs(V,{variant:"default",className:"group p-0 overflow-hidden cursor-pointer hover:border-border/80 hover:shadow-xl transition-all duration-[var(--sumi-duration-normal)] flex flex-col h-full",onClick:()=>r(e),children:[t.jsxs("div",{className:"relative aspect-video bg-card overflow-hidden",children:[t.jsx(re,{src:e.thumbnailUrl,className:"w-full h-full object-cover opacity-90 group-hover:opacity-100 group-hover:scale-105 transition-all duration-[var(--sumi-duration-normal)]",alt:e.title}),t.jsx("div",{className:"absolute inset-0 bg-background/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center backdrop-blur-sm",children:t.jsx(T,{className:"w-12 h-12 text-foreground fill-current opacity-80"})}),e.certificateAvailable&&t.jsxs("div",{className:"absolute top-2 right-2 bg-warning/90 text-foreground text-xs font-bold px-2 py-0.5 rounded shadow-lg flex items-center gap-1",children:[t.jsx(k,{className:"w-3 h-3 fill-current"})," CERTIFIED"]}),t.jsxs("div",{className:"absolute bottom-2 left-2 bg-background/70 text-foreground text-xs px-2 py-1 rounded font-mono flex items-center gap-1 backdrop-blur-sm",children:[t.jsx(z,{className:"w-3 h-3"})," ",e.duration]}),s>0&&t.jsxs("div",{className:"absolute bottom-2 right-2 bg-background/70 text-foreground text-xs px-2 py-1 rounded font-mono flex items-center gap-1 backdrop-blur-sm",children:[t.jsx(L,{className:"w-3 h-3"})," ",s," lessons"]})]}),t.jsxs("div",{className:"p-4 flex flex-col flex-1",children:[t.jsxs("div",{className:"flex justify-between items-start mb-2",children:[t.jsx("span",{className:`text-xs px-2 py-0.5 rounded uppercase font-bold tracking-wide ${e.level==="Advanced"?"bg-destructive/20 text-destructive":e.level==="Intermediate"?"bg-warning/20 text-warning":"bg-success/20 text-success"}`,children:e.level}),e.rating&&t.jsxs("div",{className:"flex items-center gap-1 text-xs text-warning font-bold",children:[t.jsx(k,{className:"w-3 h-3 fill-current"})," ",e.rating]})]}),t.jsx("h3",{className:"font-bold text-foreground text-base mb-1 line-clamp-2 group-hover:text-foreground transition-colors",children:e.title}),t.jsxs("p",{className:"text-muted-foreground text-xs mb-3",children:["by ",e.instructor]}),t.jsx("div",{className:"mt-auto pt-2",children:n&&e.progress!==void 0?t.jsxs("div",{className:"space-y-2",children:[t.jsxs("div",{className:"flex justify-between text-xs text-muted-foreground",children:[t.jsx("span",{children:"Progress"}),t.jsxs("span",{className:e.progress===100?"text-success":"text-foreground",children:[e.progress,"%"]})]}),t.jsx(Q,{value:e.progress,color:e.progress===100?"lime":"cyan"}),e.progress===100&&t.jsxs("div",{className:"flex items-center gap-1 text-xs text-success mt-1 font-bold",children:[t.jsx(H,{className:"w-3 h-3"})," Completed"]})]}):t.jsxs("div",{className:"flex justify-between items-center border-t border-foreground/5 pt-3",children:[t.jsxs("div",{className:"flex items-center gap-1 text-xs text-muted-foreground",children:[t.jsx(Z,{className:"w-3 h-3"})," ",(e.studentCount||0).toLocaleString()," students"]}),t.jsx("span",{className:"font-mono font-bold text-foreground",children:e.price&&e.price>0?`$${e.price}`:"Free"})]})})]}),n&&e.progress!==void 0&&e.progress<100&&t.jsx("div",{className:"h-1 w-full bg-muted",children:t.jsx("div",{className:"h-full bg-primary transition-all duration-500",style:{width:`${e.progress}%`}})})]})})},P=F.memo(ne);P.displayName="CourseCard";function oe({onMyCoursesClick:e}){return t.jsxs("div",{className:"flex flex-col md:flex-row justify-between items-end border-b border-border pb-6 gap-4",children:[t.jsxs("div",{children:[t.jsx("h2",{className:"text-2xl font-heading font-bold text-foreground mb-2 tracking-tight",children:"ACADEMY"}),t.jsx("p",{className:"text-muted-foreground font-mono text-sm",children:"Level up your skills. Earn certificates."})]}),t.jsx(W,{variant:"glass",icon:t.jsx(X,{className:"w-4 h-4"}),onClick:e,children:"MY LEARNING"})]})}function ie({className:e}){return t.jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:e,children:[t.jsx("line",{x1:"12",x2:"12",y1:"2",y2:"22"}),t.jsx("path",{d:"M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"})]})}function le({search:e,onSearchChange:r,filterLevel:n,onFilterLevelChange:s,filterPrice:i,onFilterPriceChange:c}){return t.jsxs("div",{className:"flex flex-col md:flex-row gap-4 items-center bg-card/50 p-4 rounded-xl border border-border",children:[t.jsx("div",{className:"w-full md:w-96",children:t.jsx(_,{placeholder:"Search for courses, skills, or teachers...",value:e,onChange:a=>r(a.target.value)})}),t.jsxs("div",{className:"flex flex-wrap gap-2 w-full md:w-auto",children:[t.jsxs("div",{className:"flex items-center gap-1.5 bg-muted rounded-lg p-1 border border-border",children:[t.jsx(K,{className:"w-4 h-4 text-muted-foreground ml-2"}),["All","Beginner","Intermediate","Advanced"].map(a=>t.jsx("button",{type:"button",onClick:()=>s(a),className:`px-3 py-1.5 rounded-md text-xs font-bold uppercase transition-all ${n===a?a==="Advanced"?"bg-destructive/20 text-destructive shadow-sm":a==="Intermediate"?"bg-warning/20 text-warning shadow-sm":a==="Beginner"?"bg-success/20 text-success shadow-sm":"bg-primary/20 text-primary shadow-sm":"text-muted-foreground hover:text-foreground hover:bg-foreground/5"}`,children:a==="All"?"All Levels":a},a))]}),t.jsxs("div",{className:"flex items-center gap-1.5 bg-muted rounded-lg p-1 border border-border",children:[t.jsx(ie,{className:"w-4 h-4 text-muted-foreground ml-2"}),["All","Free","Paid"].map(a=>t.jsx("button",{type:"button",onClick:()=>c(a),className:`px-3 py-1.5 rounded-md text-xs font-bold uppercase transition-all ${i===a?"bg-primary/20 text-primary shadow-sm":"text-muted-foreground hover:text-foreground hover:bg-foreground/5"}`,children:a==="All"?"All Prices":a},a))]})]})]})}function ce(){return t.jsxs("div",{className:"col-span-full text-center py-24 text-muted-foreground",children:[t.jsx(L,{className:"w-12 h-12 mx-auto mb-4 opacity-50"}),t.jsx("p",{children:"No courses found matching your criteria."})]})}function de(){return t.jsxs("div",{className:"space-y-8 animate-fadeIn pb-20 min-h-layout-page",children:[t.jsxs("div",{className:"flex flex-col md:flex-row justify-between items-end border-b border-border pb-6 gap-4",children:[t.jsxs("div",{children:[t.jsx(f,{className:"h-8 w-32 mb-2"}),t.jsx(f,{className:"h-4 w-64"})]}),t.jsx(f,{className:"h-11 w-40 rounded-lg"})]}),t.jsxs("div",{className:"flex flex-col md:flex-row gap-4 items-center bg-card/50 p-4 rounded-xl border border-border",children:[t.jsx(f,{className:"h-10 w-full md:w-96 rounded-lg"}),t.jsxs("div",{className:"flex gap-2 w-full md:w-auto",children:[t.jsx(f,{className:"h-9 w-28 rounded-lg"}),t.jsx(f,{className:"h-9 w-24 rounded-lg"})]})]}),t.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8",children:[1,2,3,4,5,6].map(e=>t.jsx(f,{className:"aspect-video w-full rounded-xl"},e))})]})}function ue(e){if(e===void 0)return"0m";const r=typeof e=="string"?parseInt(e,10):e;if(Number.isNaN(r))return"0m";const n=Math.floor(r/1e9),s=Math.floor(n/60),i=Math.floor(s/60);return i>0?`${i}h ${s%60}m`:`${s}m`}function me(e){const r=(e||"").toLowerCase();return r==="intermediate"?"Intermediate":r==="advanced"||r==="expert"?"Advanced":"Beginner"}function Ae(e){const r=e.ID??e.id??"",n=e.Title??e.title??"",s=e.Description??e.description,i=e.Instructor??e.instructor??"",c=me(e.Level??e.level),a=e.Price??e.price??0,o=e.Tags??e.tags??[],x=ue(e.Duration??e.duration),g=e.Thumbnail??e.thumbnail??"";return{id:r,title:n,description:s,instructor:i,thumbnailUrl:g||"https://picsum.photos/seed/edu/400/225",duration:x,level:c,price:a,tags:o}}const xe={async getCatalog(){const e=await U.get("/education/courses/list");return(Array.isArray(e.data)?e.data:[]).map(Ae)}};function ge(e){const[r,n]=l.useState(""),[s,i]=l.useState("All"),[c,a]=l.useState("All"),[o,x]=l.useState(e??[]),[g,u]=l.useState(e===void 0);l.useEffect(()=>{if(e!==void 0)return;(async()=>{u(!0);try{const d=await xe.getCatalog();x(d)}catch(d){Y.error("Failed to load courses",{error:d instanceof Error?d.message:String(d),stack:d instanceof Error?d.stack:void 0})}finally{u(!1)}})()},[e]);const m=o.filter(A=>{const d=A.title.toLowerCase().includes(r.toLowerCase())||(A.tags??[]).some(j=>j.toLowerCase().includes(r.toLowerCase())),v=s==="All"||A.level===s,b=c==="All"||(c==="Free"?A.price===0:(A.price??0)>0);return d&&v&&b});return{search:r,setSearch:n,filterLevel:s,setFilterLevel:i,filterPrice:c,setFilterPrice:a,courses:o,loading:g,filtered:m}}const pe={visible:{transition:{staggerChildren:.06,delayChildren:.04}}},fe={hidden:{opacity:0,y:16,scale:.97},visible:{opacity:1,y:0,scale:1,transition:{duration:.35,ease:[.33,1,.68,1]}}};function Le({onCourseClick:e,onMyCoursesClick:r,initialCourses:n}){const{search:s,setSearch:i,filterLevel:c,setFilterLevel:a,filterPrice:o,setFilterPrice:x,loading:g,filtered:u}=ge(n);return g?t.jsx(de,{}):t.jsxs("div",{className:"space-y-8 animate-fadeIn pb-20 min-h-layout-page",children:[t.jsx(oe,{onMyCoursesClick:r}),t.jsx(le,{search:s,onSearchChange:i,filterLevel:c,onFilterLevelChange:a,filterPrice:o,onFilterPriceChange:x}),u.length===0?t.jsx(ce,{}):t.jsx(E.div,{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8",variants:pe,initial:"hidden",animate:"visible",children:u.map(m=>t.jsx(E.div,{variants:fe,children:t.jsx(P,{course:m,onClick:A=>e?.(A)})},m.id))})]})}export{Le as EducationView};
|