veza/apps/web/dist_verification/assets/RolesPage-CkteR03X.js
senke 04c25aa24f Phase 2 stabilisation: code mort, Modal→Dialog, feature flags, tests, router split, Rust legacy
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/)
2026-02-14 17:23:32 +01:00

1 line
17 KiB
JavaScript

import{a as n,j as e}from"./vendor-react-yWUy5XPk.js";import{g as E,B as M,D as k,n as D,p as L,l as W,i as O,C as T,k as $,c as X,S as l,w as z}from"./index-CYK_b1Uz.js";import{A as R}from"./vendor-http-Cz8wfb0q.js";import{r as P}from"./features-DItyhINc.js";import{L as j,I as b}from"./input-CGdBHtsQ.js";import{T as q}from"./textarea-Dn-lpNpV.js";import{P as H,g as Y,W as J,t as K,$ as V,i as Z,a0 as ee}from"./vendor-icons-DJFb1Tiw.js";import{S as se}from"./Select-DA2I33Xz.js";import"./vendor-CveO81sn.js";import"./vendor-security-DsrNJhpn.js";import"./vendor-router-BNNHboN9.js";import"./vendor-tanstack-kPY9uK0s.js";import"./vendor-utils-DtoSyhX2.js";import"./vendor-motion-B3XPS3Jc.js";import"./vendor-i18n-CMcqpBLz.js";import"./dropdown-CA3EXRNb.js";async function re(){try{const r=await E.get("/roles");return Array.isArray(r.data)?r.data:[]}catch(r){if(r instanceof R){if(r.response?.status===401)throw new Error("Unauthorized: Please log in to access roles");if(r.response?.status===403)throw new Error("Forbidden: You do not have permission to view roles");const s=r.response?.data?.error||r.message||"Failed to fetch roles";throw new Error(s)}throw r}}async function ae(r){try{return(await E.get(`/roles/${r}`)).data}catch(s){if(s instanceof R){if(s.response?.status===401)throw new Error("Unauthorized: Please log in to access role");if(s.response?.status===403)throw new Error("Forbidden: You do not have permission to view this role");if(s.response?.status===404)throw new Error("Role not found");const a=s.response?.data?.error||s.message||"Failed to fetch role";throw new Error(a)}throw s}}async function te(r){try{return(await E.get(`/users/${r}/roles`)).data.roles||[]}catch(s){if(s instanceof R){if(s.response?.status===401)throw new Error("Unauthorized: Please log in to access user roles");if(s.response?.status===403)throw new Error("Forbidden: You do not have permission to view user roles");if(s.response?.status===404)throw new Error("User not found");const a=s.response?.data?.error||s.message||"Failed to fetch user roles";throw new Error(a)}throw s}}async function oe(r,s){P("ROLE_MANAGEMENT");try{await E.post(`/users/${r}/roles`,s)}catch(a){if(a instanceof R){if(a.response?.status===400){const p=a.response?.data?.error||"Invalid request data";throw new Error(p)}if(a.response?.status===401)throw new Error("Unauthorized: Please log in to assign roles");if(a.response?.status===403)throw new Error("Forbidden: You do not have permission to assign roles");if(a.response?.status===404)throw new Error("User or role not found");const d=a.response?.data?.error||a.message||"Failed to assign role";throw new Error(d)}throw a}}async function ne(r){try{return(await E.post("/roles",r)).data.role}catch(s){if(s instanceof R){if(s.response?.status===400){const d=s.response?.data?.error||"Invalid role data";throw new Error(d)}if(s.response?.status===401)throw new Error("Unauthorized: Please log in to create roles");if(s.response?.status===403)throw new Error("Forbidden: You do not have permission to create roles");const a=s.response?.data?.error||s.message||"Failed to create role";throw new Error(a)}throw s}}async function B(r,s){P("ROLE_MANAGEMENT");try{await E.put(`/roles/${r}`,s)}catch(a){if(a instanceof R){if(a.response?.status===400){const p=a.response?.data?.error||"Invalid role data";throw new Error(p)}if(a.response?.status===401)throw new Error("Unauthorized: Please log in to update roles");if(a.response?.status===403)throw new Error("Forbidden: You do not have permission to update roles");if(a.response?.status===404)throw new Error("Role not found or is a system role");const d=a.response?.data?.error||a.message||"Failed to update role";throw new Error(d)}throw a}}async function ie(r){P("ROLE_MANAGEMENT");try{await E.delete(`/roles/${r}`)}catch(s){if(s instanceof R){if(s.response?.status===400){const d=s.response?.data?.error||"Cannot delete system role";throw new Error(d)}if(s.response?.status===401)throw new Error("Unauthorized: Please log in to delete roles");if(s.response?.status===403)throw new Error("Forbidden: You do not have permission to delete roles");if(s.response?.status===404)throw new Error("Role not found");const a=s.response?.data?.error||s.message||"Failed to delete role";throw new Error(a)}throw s}}function le({onRoleCreated:r}){const[s,a]=n.useState(!1),[d,p]=n.useState(!1),[m,f]=n.useState({name:"",display_name:"",description:"",is_active:!0}),{success:u,error:h}=D(),x=async()=>{p(!0);try{await ne(m),u("Role created successfully"),a(!1),f({name:"",display_name:"",description:"",is_active:!0}),r(),f({name:"",display_name:"",description:"",is_active:!0}),r()}catch(c){const g=L(c);h(g.message)}finally{p(!1)}};return e.jsxs(e.Fragment,{children:[e.jsxs(M,{onClick:()=>a(!0),children:[e.jsx(H,{className:"h-4 w-4 mr-2"}),"Create Role"]}),e.jsx(k,{open:s,onClose:()=>a(!1),title:"Create New Role",onConfirm:x,confirmLabel:d?"Creating...":"Create Role",cancelLabel:"Cancel",children:e.jsxs("form",{onSubmit:c=>{c.preventDefault(),x()},className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx(j,{htmlFor:"name",children:"Name *"}),e.jsx(b,{id:"name",value:m.name,onChange:c=>f({...m,name:c.target.value}),placeholder:"e.g., content_moderator",required:!0})]}),e.jsxs("div",{children:[e.jsx(j,{htmlFor:"display_name",children:"Display Name *"}),e.jsx(b,{id:"display_name",value:m.display_name,onChange:c=>f({...m,display_name:c.target.value}),placeholder:"e.g., Content Moderator",required:!0})]}),e.jsxs("div",{children:[e.jsx(j,{htmlFor:"description",children:"Description"}),e.jsx(q,{id:"description",value:m.description,onChange:c=>f({...m,description:c.target.value}),placeholder:"Role description...",rows:3})]}),e.jsxs("div",{className:"flex items-center space-x-2",children:[e.jsx("input",{type:"checkbox",id:"is_active",checked:m.is_active,onChange:c=>f({...m,is_active:c.target.checked}),className:"rounded"}),e.jsx(j,{htmlFor:"is_active",children:"Active"})]})]})})]})}function ce({role:r,open:s,onClose:a,onRoleUpdated:d}){const[p,m]=n.useState(!1),[f,u]=n.useState(!1),[h,x]=n.useState({name:r.name,display_name:r.display_name,description:r.description,is_active:r.is_active}),{success:c,error:g}=D();n.useEffect(()=>{s&&r.id&&(u(!0),ae(r.id).then(i=>{x({name:i.name,display_name:i.display_name,description:i.description,is_active:i.is_active})}).catch(i=>{const w=L(i);g(w.message)}).finally(()=>{u(!1)}))},[s,r.id,g]);const v=async()=>{m(!0);try{await B(r.id,h),c("Role updated successfully"),a(),d()}catch(i){const w=L(i);g(w.message)}finally{m(!1)}};return e.jsx(k,{open:s,onClose:a,title:"Edit Role",onConfirm:v,confirmLabel:p?"Updating...":"Update Role",cancelLabel:"Cancel",children:f?e.jsx("div",{className:"flex justify-center py-8",children:e.jsx(Y,{className:"h-8 w-8 animate-spin"})}):e.jsxs("form",{onSubmit:i=>{i.preventDefault(),v()},className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx(j,{htmlFor:"edit-name",children:"Name *"}),e.jsx(b,{id:"edit-name",value:h.name,onChange:i=>x({...h,name:i.target.value}),required:!0,disabled:r.is_system}),r.is_system&&e.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:"System roles cannot be renamed"})]}),e.jsxs("div",{children:[e.jsx(j,{htmlFor:"edit-display_name",children:"Display Name *"}),e.jsx(b,{id:"edit-display_name",value:h.display_name,onChange:i=>x({...h,display_name:i.target.value}),required:!0})]}),e.jsxs("div",{children:[e.jsx(j,{htmlFor:"edit-description",children:"Description"}),e.jsx(q,{id:"edit-description",value:h.description,onChange:i=>x({...h,description:i.target.value}),rows:3})]}),e.jsxs("div",{className:"flex items-center space-x-2",children:[e.jsx("input",{type:"checkbox",id:"edit-is_active",checked:h.is_active,onChange:i=>x({...h,is_active:i.target.checked}),className:"rounded",disabled:r.is_system}),e.jsx(j,{htmlFor:"edit-is_active",children:"Active"}),r.is_system&&e.jsx("p",{className:"text-xs text-muted-foreground ml-2",children:"System roles are always active"})]})]})})}function de({userId:r,userName:s,availableRoles:a,open:d,onClose:p,onRoleAssigned:m}){const[f,u]=n.useState(!1),[h,x]=n.useState(!1),[c,g]=n.useState(""),[v,i]=n.useState(""),[w,I]=n.useState([]),{success:U,error:y}=D();n.useEffect(()=>{d&&r&&(x(!0),te(r).then(o=>{I(o)}).catch(o=>{const N=L(o);W.error("Failed to load user roles",{error:N.message,stack:o instanceof Error?o.stack:void 0,userId:r})}).finally(()=>{x(!1)}))},[d,r]);const _=async()=>{if(!c){y("Please select a role");return}u(!0);try{await oe(r,{role_id:c,expires_at:v||void 0}),U("Role assigned successfully"),p(),g(""),i(""),m(),i(""),m()}catch(o){const N=L(o);y(N.message)}finally{u(!1)}},S=a.filter(o=>!w.some(N=>N.id===o.id)),C=S.map(o=>({value:o.id,label:`${o.display_name} (${o.name})`}));return e.jsx(k,{open:d,onClose:p,title:`Assign Role${s?` to ${s}`:""}`,onConfirm:_,confirmLabel:f?"Assigning...":"Assign Role",cancelLabel:"Cancel",children:h?e.jsx("div",{className:"flex justify-center py-8",children:e.jsx(Y,{className:"h-8 w-8 animate-spin"})}):e.jsxs("form",{onSubmit:_,className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx(j,{htmlFor:"role-select",children:"Role *"}),S.length===0?e.jsx("div",{className:"p-2 text-sm text-muted-foreground border rounded",children:"No available roles to assign"}):e.jsx(se,{options:C,value:c,onChange:o=>g((Array.isArray(o)?o[0]:o)??""),placeholder:"Select a role"})]}),e.jsxs("div",{children:[e.jsx(j,{htmlFor:"expires-at",children:"Expires At (optional)"}),e.jsx(b,{id:"expires-at",type:"datetime-local",value:v,onChange:o=>i(o.target.value)}),e.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:"Leave empty for permanent assignment"})]}),w.length>0&&e.jsxs("div",{children:[e.jsx(j,{children:"Current Roles"}),e.jsx("div",{className:"mt-2 space-y-1",children:w.map(o=>e.jsxs("div",{className:"text-sm text-muted-foreground",children:["• ",o.display_name]},o.id))})]})]})})}function me(){return e.jsxs("div",{className:"container mx-auto px-4 py-8 max-w-6xl pb-24",children:[e.jsxs("div",{className:"mb-8 flex items-end justify-between",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(l,{className:"h-9 w-48"}),e.jsx(l,{className:"h-4 w-56"})]}),e.jsx(l,{className:"h-10 w-32 rounded-lg"})]}),e.jsxs("div",{className:"grid gap-6",children:[e.jsxs("div",{className:"overflow-hidden rounded-xl border border-border bg-card/80",children:[e.jsxs("div",{className:"grid grid-cols-12 gap-4 p-4 border-b border-border",children:[e.jsx(l,{className:"col-span-3 h-3 w-24"}),e.jsx(l,{className:"col-span-3 h-3 w-20"}),e.jsx(l,{className:"col-span-2 h-3 w-12 mx-auto"}),e.jsx(l,{className:"col-span-2 h-3 w-14 mx-auto"}),e.jsx(l,{className:"col-span-2 h-3 w-16 ml-auto"})]}),e.jsx("div",{className:"divide-y divide-white/5",children:Array.from({length:6}).map((r,s)=>e.jsxs("div",{className:"grid grid-cols-12 gap-4 p-4 items-center",children:[e.jsxs("div",{className:"col-span-3 space-y-1.5",children:[e.jsx(l,{className:"h-4 w-28"}),e.jsx(l,{className:"h-3 w-20"})]}),e.jsx("div",{className:"col-span-3",children:e.jsx(l,{className:"h-3 w-full"})}),e.jsx("div",{className:"col-span-2 flex justify-center",children:e.jsx(l,{className:"h-6 w-16 rounded-full"})}),e.jsx("div",{className:"col-span-2 flex justify-center",children:e.jsx(l,{variant:"circular",className:"h-2 w-2"})}),e.jsxs("div",{className:"col-span-2 flex justify-end gap-2",children:[e.jsx(l,{className:"h-8 w-20 rounded-md"}),e.jsx(l,{className:"h-8 w-8 rounded-md"})]})]},s))})]}),e.jsxs("div",{className:"rounded-xl border border-border bg-card/80 p-6",children:[e.jsx(l,{className:"h-4 w-36 mb-4"}),e.jsxs("div",{className:"flex gap-4 items-end",children:[e.jsxs("div",{className:"flex-1 space-y-2",children:[e.jsx(l,{className:"h-3 w-16"}),e.jsx(l,{className:"h-10 w-full rounded-md"})]}),e.jsxs("div",{className:"flex-1 space-y-2",children:[e.jsx(l,{className:"h-3 w-32"}),e.jsx(l,{className:"h-10 w-full rounded-md"})]}),e.jsx(l,{className:"h-10 w-32 rounded-lg"})]})]})]})]})}function Ce(){const[r,s]=n.useState([]),[a,d]=n.useState(!0),[p,m]=n.useState(null),[f,u]=n.useState(null),[h,x]=n.useState(null),[c,g]=n.useState(0),v=n.useRef(null),[i,w]=n.useState(!1),[I,U]=n.useState(!1),[y,_]=n.useState(""),[S,C]=n.useState(""),o=async()=>{try{d(!0),m(null);const t=await re();s(t)}catch(t){m(new Error(t instanceof Error?t.message:"Failed to load roles"))}finally{d(!1)}};n.useEffect(()=>{o()},[]);const N=async t=>{const A=async()=>{await B(t.id,{is_active:!t.is_active}),z.success(`Role ${t.is_active?"deactivated":"activated"} successfully`),o(),u(null),g(0),v.current=null};v.current=A,u(null);try{await A()}catch(F){u(new Error(F instanceof Error?F.message:"Failed to update role"))}},G=async t=>{if(t.is_system){u(new Error("Cannot delete system roles"));return}const A=async()=>{await ie(t.id),z.success("Role deleted successfully"),o(),u(null),g(0),v.current=null};v.current=A,u(null);try{await A()}catch(F){u(new Error(F instanceof Error?F.message:"Failed to delete role"))}},Q=async()=>{if(!(!v.current||c>=3)){g(t=>t+1);try{await v.current()}catch{}}};return a?e.jsx(me,{}):p&&r.length===0?e.jsx("div",{className:"container mx-auto px-4 py-8",children:e.jsx(O,{error:p,variant:"card",severity:"error",onRetry:o})}):e.jsxs("div",{className:"container mx-auto px-4 py-8 max-w-6xl pb-24",children:[f&&e.jsx(O,{error:f,variant:"banner",severity:"error",onRetry:c<3?Q:void 0,onDismiss:()=>{u(null),g(0),v.current=null}}),e.jsxs("div",{className:"mb-8 flex items-end justify-between",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-heading-1 font-heading text-foreground mb-2",children:"Access Control"}),e.jsxs("p",{className:"text-muted-foreground font-mono text-xs flex items-center gap-2",children:[e.jsx(J,{className:"w-4 h-4 text-primary"})," ROLE & PERMISSION MATRIX"]})]}),e.jsx(le,{onRoleCreated:o})]}),e.jsxs("div",{className:"grid gap-6",children:[e.jsxs(T,{variant:"glass",className:"border-border bg-card/80 overflow-hidden",children:[e.jsxs("div",{className:"grid grid-cols-12 gap-4 p-4 border-b border-border text-xs font-bold uppercase tracking-wider text-muted-foreground",children:[e.jsx("div",{className:"col-span-3",children:"Role Identity"}),e.jsx("div",{className:"col-span-3",children:"Attributes"}),e.jsx("div",{className:"col-span-2 text-center",children:"Type"}),e.jsx("div",{className:"col-span-2 text-center",children:"Status"}),e.jsx("div",{className:"col-span-2 text-right",children:"Actions"})]}),e.jsx("div",{className:"divide-y divide-white/5",children:r.length===0?e.jsx("div",{className:"text-center py-12 text-muted-foreground",children:"No roles configured yet."}):r.map(t=>e.jsxs("div",{className:"grid grid-cols-12 gap-4 p-4 items-center hover:bg-muted/50 transition-colors group",children:[e.jsxs("div",{className:"col-span-3",children:[e.jsx("div",{className:"font-bold text-foreground group-hover:text-primary transition-colors",children:t.name}),e.jsx("div",{className:"text-xs text-muted-foreground",children:t.display_name})]}),e.jsx("div",{className:"col-span-3 text-sm text-muted-foreground truncate",title:t.description,children:t.description}),e.jsx("div",{className:"col-span-2 text-center",children:t.is_system?e.jsx($,{variant:"primary",className:"border-primary/50 text-primary bg-primary/10",children:"SYSTEM"}):e.jsx($,{variant:"secondary",className:"border-border text-muted-foreground",children:"CUSTOM"})}),e.jsx("div",{className:"col-span-2 text-center",children:e.jsx("div",{className:X("inline-flex h-2 w-2 rounded-full",t.is_active?"bg-success shadow-status-dot-lime":"bg-destructive")})}),e.jsx("div",{className:"col-span-2 flex justify-end gap-2 opacity-60 group-hover:opacity-100 transition-opacity",children:t.is_system?e.jsx(K,{className:"w-4 h-4 text-muted-foreground"}):e.jsxs(e.Fragment,{children:[e.jsx(M,{size:"sm",variant:"ghost",onClick:()=>N(t),children:t.is_active?"Deactivate":"Activate"}),e.jsx(M,{size:"sm",variant:"ghost",onClick:()=>{x(t),w(!0)},children:e.jsx(V,{className:"w-4 h-4"})}),e.jsx(M,{size:"sm",variant:"ghost",className:"text-destructive hover:bg-destructive/10",onClick:()=>G(t),children:e.jsx(Z,{className:"w-4 h-4"})})]})})]},t.id))})]}),e.jsxs(T,{variant:"glass",className:"p-6 border-border bg-card/80",children:[e.jsxs("h3",{className:"font-bold text-foreground mb-4 flex items-center gap-2 text-sm uppercase tracking-widest",children:[e.jsx(ee,{className:"w-4 h-4 text-primary"})," Role Assignment"]}),e.jsxs("div",{className:"flex gap-4 items-end",children:[e.jsxs("div",{className:"flex-1 space-y-2",children:[e.jsx("label",{className:"text-xs text-muted-foreground ml-1",children:"User ID"}),e.jsx(b,{placeholder:"UID-...",value:y,onChange:t=>_(t.target.value),className:"bg-muted/30 font-mono"})]}),e.jsxs("div",{className:"flex-1 space-y-2",children:[e.jsx("label",{className:"text-xs text-muted-foreground ml-1",children:"Username (Optional)"}),e.jsx(b,{placeholder:"@username",value:S,onChange:t=>C(t.target.value),className:"bg-muted/30"})]}),e.jsx(M,{onClick:()=>{_(y),C(S||""),U(!0)},disabled:!y,className:"shadow-glow-cyan",children:"Assign Access"})]})]})]}),h&&e.jsx(ce,{role:h,open:i,onClose:()=>{w(!1),x(null)},onRoleUpdated:o}),e.jsx(de,{userId:y,userName:S,availableRoles:r.filter(t=>t.is_active),open:I,onClose:()=>{U(!1),_(""),C("")},onRoleAssigned:()=>{}})]})}export{Ce as RolesPage};