PageSourceSearch

https://app.fynedesk.ai/assets/RolesTab-DgZL1E_Z.js

js fynedesk.ai collected 2026-10-05 14:54:59 UTC 27,134 bytes, 1 lines download raw bytes

1import{e as l,j as e}from"./react-vendor-DvYSljET.js";import{dp as Ee,dk as De,di as Pe,de as Le,ah as Me,c_ as x,a4 as xe,p as Ie,a_ as F,m as Oe,aw as V,a2 as H,at as Fe,al as ue,x as pe,aN as fe}from"./index-By5z6z4E.js";import{B as He}from"./BusinessBadge-B5is1i1O.js";import{u as Ke}from"./useRedirectToPlan-DTNRvREv.js";import{C as qe}from"./circle-check-big-BPBFHmoC.js";import{T as X}from"./trash-2-fwf24e91.js";import{S as be}from"./save-DUofqVBh.js";import{E as Ue}from"./eye-DRjU8rUL.js";import{P as We}from"./pencil-D_5Bkqq1.js";import"./common-vendor-B-x8eRJF.js";import"./router-vendor-DWVw0nsd.js";import"./supabase-vendor-BZ9LqEgu.js";function he({level:t,t:a}){return t==="critical"?e.jsx("span",{className:"inline-flex items-center gap-1 rounded-full bg-red-50 px-1.5 py-0.5 text-[11px] font-semibold text-red-600",children:a("rolesTab.riskCritical","Critical")}):t==="high"?e.jsx("span",{className:"inline-flex items-center gap-1 rounded-full bg-amber-50 px-1.5 py-0.5 text-[11px] font-semibold text-amber-700",children:a("rolesTab.riskHigh","High")}):t==="medium"?e.jsx("span",{className:"inline-flex items-center gap-1 rounded-full fd-surface-2 px-1.5 py-0.5 text-[11px] font-medium fd-text-muted-2",children:a("rolesTab.riskMedium","Medium")}):null}function ls(){const{t}=Ee(),{theme:a}=De(),{can:ge}=Pe(),{businessLocked:J}=Le(),je=Ke(),ye=ge(Me.ROLES_MANAGE),[B,Q]=l.useState([]),[z,Ne]=l.useState([]),[T,Z]=l.useState({}),[v,ve]=l.useState({}),[ee,ke]=l.useState({}),[we,se]=l.useState(!0),[te,p]=l.useState(null),[$,A]=l.useState(null),[n,re]=l.useState(null),[h,ae]=l.useState(new Set),[u,E]=l.useState({name:"",description:"",rank:45}),[K,le]=l.useState(""),[ne,ie]=l.useState(!1),[g,k]=l.useState(!1),[Te,C]=l.useState(!1),[c,N]=l.useState({templateId:"",name:"",description:"",rank:45}),[Ce,_]=l.useState(!1),[q,oe]=l.useState(""),[j,D]=l.useState(null),[P,U]=l.useState("");l.useEffect(()=>{if(!$)return;const s=setTimeout(()=>A(null),4e3);return()=>clearTimeout(s)},[$]);const R=l.useCallback(async()=>{se(!0),p(null);try{const[s,r,i,o,d]=await Promise.all([x.rpc("admin_rbac_role_scope_metadata"),x.from("permissions").select("*").order("grp").order("key"),x.from("role_permissions").select("role_id, permission_key"),x.from("role_assignments").select("role_id, status").eq("status","active"),x.from("roles").select("id, updated_at")]);if(s.error)throw s.error;if(r.error)throw r.error;Q(s.data||[]),Ne((r.data||[]).filter(m=>!m.deprecated_at));const b={};(i.data||[]).forEach(m=>{(b[m.role_id]=b[m.role_id]||[]).push(m.permission_key)}),Z(b);const y={};(o.data||[]).forEach(m=>{y[m.role_id]=(y[m.role_id]||0)+1}),ve(y);const me={};(d.data||[]).forEach(m=>{me[m.id]=m.updated_at}),ke(me)}catch(s){console.error("[Roles] load failed:",s),p(s.message||t("rolesTab.loadFailed","Could not load roles."))}finally{se(!1)}},[t]);l.useEffect(()=>{R()},[R]);const f=l.useMemo(()=>{const s={};return z.forEach(r=>{s[r.key]=r}),s},[z]),_e=l.useMemo(()=>{const s=new Map;return z.forEach(r=>{s.has(r.grp)||s.set(r.grp,[]),s.get(r.grp).push(r)}),Array.from(s.entries())},[z]),de=l.useCallback(s=>(T[s]||[]).filter(i=>["high","critical"].includes(f[i]?.risk_level)).length,[T,f]),W=s=>{re(s),ae(new Set(T[s.id]||[])),E({name:s.name,description:s.description||"",rank:s.rank??45}),le(""),ie(!1)},Y=()=>{re(null),_(!1),oe("")},Re=l.useCallback(s=>{const r=new Set(s);let i=!0;for(;i;)i=!1,r.forEach(o=>{(f[o]?.dependencies||[]).forEach(d=>{r.has(d)||(r.add(d),i=!0)})});return r},[f]),ce=l.useCallback((s,r)=>Array.from(r).filter(i=>i!==s&&(f[i]?.dependencies||[]).includes(s)),[f]),Se=s=>{ae(r=>{const i=new Set(r);if(i.has(s)){if(ce(s,i).length>0)return r;i.delete(s)}else return i.add(s),Re(i);return i})},L=l.useMemo(()=>new Set(n?T[n.id]||[]:[]),[n,T]),S=l.useMemo(()=>Array.from(h).filter(s=>!L.has(s)).sort(),[h,L]),G=l.useMemo(()=>Array.from(L).filter(s=>!h.has(s)).sort(),[h,L]),M=l.useMemo(()=>S.filter(s=>["roles.manage","security.manage","billing.manage","api_tokens.manage"].includes(s)),[S]),I=n&&!n.is_system&&(u.name!==n.name||(u.description||"")!==(n.description||"")||Number(u.rank)!==Number(n.rank)),O=S.length>0||G.length>0,Be=async()=>
1{if(n){k(!0),p(null);try{if(I){const{error:s}=await x.rpc("update_role_meta",{p_role_id:n.id,p_name:u.name,p_description:u.description,p_rank:Number(u.rank)});if(s)throw s}if(O){const{error:s}=await x.rpc("update_role_permissions",{p_role_id:n.id,p_keys:Array.from(h),p_reason:q.trim()||null});if(s)throw s}A(t("rolesTab.saved","Role saved.")),Y(),await R()}catch(s){console.error("[Roles] save failed:",s),p(s.message||t("rolesTab.saveFailed","Could not save the role.")),_(!1)}finally{k(!1)}}},ze=async()=>{k(!0),p(null);try{const{data:s,error:r}=await x.rpc("create_role_from_template",{p_template_role_id:c.templateId?Number(c.templateId):null,p_name:c.name.trim(),p_description:c.description.trim()||null,p_rank:Number(c.rank)||45});if(r)throw r;A(t("rolesTab.created","Role created.")),C(!1),N({templateId:"",name:"",description:"",rank:45}),await R();const{data:i}=await x.rpc("admin_rbac_role_scope_metadata"),o=(i||[]).find(d=>d.id===s);if(o){const{data:d}=await x.from("role_permissions").select("permission_key").eq("role_id",o.id);Z(b=>({...b,[o.id]:(d||[]).map(y=>y.permission_key)})),Q(i||[]),W(o)}}catch(s){console.error("[Roles] duplicate failed:",s),p(s.message||t("rolesTab.createFailed","Could not create the role."))}finally{k(!1)}},$e=async()=>{if(j){k(!0),p(null);try{const{error:s}=await x.rpc("delete_role",{p_role_id:j.id,p_remap_role_id:P?Number(P):null});if(s)throw s;A(t("rolesTab.deleted","Role deleted.")),D(null),U(""),Y(),await R()}catch(s){console.error("[Roles] delete failed:",s),p(s.message||t("rolesTab.deleteFailed","Could not delete the role."))}finally{k(!1)}}};if(!ye)return e.jsx("div",{className:"p-8 text-center text-sm fd-text-muted-2",children:t("rolesTab.noAccess","You need role management access to view this tab.")});const w=!J,Ae=s=>{if(ne&&!["high","critical"].includes(s.risk_level))return!1;if(!K.trim())return!0;const r=K.trim().toLowerCase();return s.key.toLowerCase().includes(r)||(s.label||"").toLowerCase().includes(r)||(s.description||"").toLowerCase().includes(r)};return e.jsxs("div",{className:"w-full",children:[J&&e.jsxs("div",{className:"mb-4 flex flex-wrap items-center gap-2 rounded-lg border fd-border fd-surface px-3 py-2.5",children:[e.jsx(xe,{size:15,className:"fd-text-subtle"}),e.jsx(He,{}),e.jsx("span",{className:"text-sm fd-text-muted-2",children:t("rolesTab.businessLocked","Custom roles are available on the Business plan. You can review the built-in roles below.")}),e.jsx("button",{onClick:je,className:"text-sm font-semibold text-indigo-600 hover:text-indigo-700 underline cursor-pointer",children:t("rolesTab.viewPlans","View plans")})]}),te&&e.jsxs("div",{className:"mb-4 flex items-center gap-2 rounded-lg border border-red-100 bg-red-50 px-3 py-2 text-sm text-red-700",children:[e.jsx(Ie,{size:16}),e.jsx("span",{className:"flex-1",children:te}),e.jsx("button",{onClick:()=>p(null),className:"text-red-500 hover:text-red-700 cursor-pointer",children:e.jsx(F,{size:16})})]}),$&&e.jsxs("div",{className:"mb-4 flex items-center gap-2 rounded-lg border border-emerald-100 bg-emerald-50 px-3 py-2 text-sm text-emerald-700",children:[e.jsx(qe,{size:16}),$]}),we?e.jsxs("div",{className:"p-12 text-center",children:[e.jsx("div",{className:`mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-2 ${a.spinnerBorder} border-t-transparent`}),e.jsx("p",{className:"fd-text-muted-2",children:t("rolesTab.loading","Loading roles…")})]}):n?e.jsxs("div",{children:[e.jsxs("button",{onClick:Y,className:"mb-3 inline-flex items-center gap-1 text-sm fd-text-muted-2 hover:[color:var(--fd-text)] cursor-pointer",children:[e.jsx(Oe,{size:15})," ",t("rolesTab.backToLibrary","All roles")]}),e.jsxs("div",{className:"rounded-xl border fd-border fd-surface p-4 sm:p-5",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsx("div",{className:"min-w-0 flex-1",children:n.is_system||!w?e.jsxs(e.Fragment,{children:[e.jsxs("h2",{className:"text-lg font-bold fd-text flex items-center gap-2",children:[e.jsx(V,{size:18,className:a.accentText}),n.name,n.is_system&&e.jsx("span",{className:"rounded-full fd-surface-2 px-2 py-0.5 text-[11px] font-semibold fd-text-muted-2",children:t("rolesTab.systemRole","Built-in")})]}),n.description&&e.jsx("p",{className:"mt-0.5 text-sm fd-text-muted-2",children:n.description})]}):e.jsxs("div",{className:"space-y-2",children:[e.jsx("input",{value:u.name,onChange:s=>E(r=>({...r,name:s.target.value})),maxLength:60,className:`w-full max-w-sm rounded-lg border fd-border px-3 py-1.5 text-base font-semibold focus:outline-none focus:ring-2 ${a.focusRing}/20 ${a.focusBorder}`,"aria-label":t("rolesTab.roleName","Role name")}
1),e.jsx("input",{value:u.description,onChange:s=>E(r=>({...r,description:s.target.value})),placeholder:t("rolesTab.roleDescriptionPlaceholder","What is this role for?"),className:`w-full max-w-lg rounded-lg border fd-border px-3 py-1.5 text-sm focus:outline-none focus:ring-2 ${a.focusRing}/20 ${a.focusBorder}`,"aria-label":t("rolesTab.roleDescription","Role description")}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2 text-sm",children:[e.jsx("label",{className:"fd-text-muted-2",children:t("rolesTab.rank","Seniority rank")}),e.jsx("input",{type:"number",min:1,max:89,value:u.rank,onChange:s=>E(r=>({...r,rank:s.target.value})),className:`w-20 rounded-lg border fd-border px-2 py-1.5 focus:outline-none focus:ring-2 ${a.focusRing}/20 ${a.focusBorder}`}),Number(u.rank)>=40?e.jsx("span",{className:"inline-flex items-center gap-1 rounded-full bg-amber-50 px-2 py-0.5 text-[11px] font-semibold text-amber-700",children:t("rolesTab.paidSeat","Uses a paid seat")}):e.jsx("span",{className:"text-[11px] fd-text-subtle",children:t("rolesTab.freeSeat","Does not use a paid seat")})]})]})}),e.jsx("div",{className:"flex items-center gap-2",children:!n.is_system&&w&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{onClick:()=>{D(n),U("")},className:"inline-flex items-center gap-1.5 rounded-lg border border-red-200 px-3 py-2 text-sm font-medium text-red-600 hover:bg-red-50 cursor-pointer",children:[e.jsx(X,{size:14})," ",t("rolesTab.delete","Delete")]}),e.jsxs("button",{onClick:()=>{(M.length>0||O||I)&&_(!0)},disabled:g||!O&&!I,className:`inline-flex items-center gap-1.5 rounded-lg ${a.primaryBg} ${a.primaryBgHover} px-3 py-2 text-sm font-medium text-white disabled:opacity-50 cursor-pointer transition-colors`,children:[g?e.jsx(H,{size:14,className:"animate-spin"}):e.jsx(be,{size:14}),t("rolesTab.reviewAndSave","Review & save")]})]})})]}),e.jsxs("div",{className:"mt-4 flex flex-wrap items-center gap-2",children:[e.jsxs("div",{className:"relative min-w-[220px] flex-1 sm:max-w-xs",children:[e.jsx(Fe,{className:"absolute left-3 top-1/2 -translate-y-1/2 fd-text-subtle",size:15}),e.jsx("input",{value:K,onChange:s=>le(s.target.value),placeholder:t("rolesTab.searchPermissions","Search permissions…"),className:`w-full rounded-lg border fd-border py-2 pl-9 pr-3 text-sm focus:outline-none focus:ring-2 ${a.focusRing}/20 ${a.focusBorder}`})]}),e.jsxs("label",{className:"inline-flex cursor-pointer items-center gap-2 text-sm fd-text-muted-2",children:[e.jsx("input",{type:"checkbox",checked:ne,onChange:s=>ie(s.target.checked),className:"cursor-pointer"}),t("rolesTab.showRiskyOnly","Show risky only")]}),e.jsx("span",{className:"ml-auto text-xs fd-text-subtle",children:t("rolesTab.selectedCount","{{count}} permissions selected",{count:h.size})})]}),e.jsx("div",{className:"mt-4 space-y-5",children:_e.map(([s,r])=>{const i=r.filter(Ae);return i.length===0?null:e.jsxs("div",{children:[e.jsx("h3",{className:"mb-2 text-xs font-semibold uppercase tracking-wide fd-text-muted-2",children:t(`rolesTab.group.${s}`,s)}),e.jsx("div",{className:"grid gap-1.5 sm:grid-cols-2",children:i.map(o=>{const d=h.has(o.key),b=d&&ce(o.key,h).length>0,y=n.is_system||!w;return e.jsxs("label",{className:`flex items-start gap-2.5 rounded-lg border px-3 py-2 ${d?"fd-border-strong fd-surface-2":"fd-border fd-surface"} ${y?"":"cursor-pointer"}`,title:b?t("rolesTab.requiredByOther","Required by another selected permission"):o.description||o.key,children:[e.jsx("input",{type:"checkbox",checked:d,disabled:y||b,onChange:()=>Se(o.key),className:"mt-0.5 cursor-pointer disabled:cursor-not-allowed"}),e.jsxs("span",{className:"min-w-0",children:[e.jsxs("span",{className:"flex flex-wrap items-center gap-1.5",children:[e.jsx("span",{className:"text-sm font-medium fd-text",children:o.label||o.key}),e.jsx(he,{level:o.risk_level,t}),b&&e.jsx(xe,{size:11,className:"fd-text-subtle"})]}),e.jsx("span",{className:"block truncate text-[11px] text-slate-400 font-mono",children:o.key})]})]},o.key)})})]},s)})})]})]}):e.jsxs("div",{children:[e.jsxs("div",{className:"mb-4 flex flex-wrap items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-base font-semibold fd-text",children:t("rolesTab.libraryTitle","Role library")}),e.jsx("p",{className:"text-sm fd-text-muted-2",children:t("rolesTab.librarySubtitle","Built-in roles are read-only. Create your own by duplicating one and adjusting its permissions.")})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("button",{onClick:()=>{N({templateId:"",name:"",description:"",rank:45}),C(!0)},disabled:!w,className:"inline-flex items-center gap-1.5 rounded-lg border fd-border-strong px-3 py-2 text-sm font-medium fd-text-2 hover:[background-color:var(--fd-surface-2)] disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer",children:[e.jsx(ue,{size:15})," ",t("rolesTab.fromScratch","Start from scratch")]}),e.jsxs("button",{onClick:()=>{N({templateId:String(B.find(s=>s.name==="Agent")?.id||""),name:"",description:"",rank:45}),C(!0)},disabled:!w,className:`inline-flex items-center gap-1.5 rounded-lg ${a.primaryBg} ${a.primaryBgHover} px-3 py-2 text-sm font-medium text-white disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer transition-colors`,children:[e.jsx(pe,{size:15})," ",t("rolesTab.duplicateRole","Duplicate a role")]})]})]}),e.jsx("div",{className:"overflow-hidden rounded-xl border fd-border fd-surface shadow-sm",children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full min-w-[720px] text-sm",children:[e.jsx("thead",{className:"fd-bg border-b fd-border",children:e.jsxs("tr",{children:[e.jsx("th",{className:"px-4 py-2.5 text-left text-xs font-semibold uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.colRole","Role")}),e.jsx("th",{className:"px-4 py-2.5 text-left text-xs font-semibold uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.colRank","Rank")}),e.jsx("th",{className:"px-4 py-2.5 text-left text-xs font-semibold uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.colMembers","People")}),e.jsx("th",{className:"px-4 py-2.5 text-left text-xs font-semibold uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.colPermissions","Permissions")}),e.jsx("th",{className:"px-4 py-2.5 text-left text-xs font-semibold uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.colRisky","Risky grants")}),e.jsx("th",{className:"px-4 py-2.5 text-left text-xs font-semibold uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.colUpdated","Last edited")}),e.jsx("th",{className:"px-4 py-2.5 text-right text-xs font-semibold uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.colActions","Actions")})]})}),e.jsx("tbody",{className:"divide-y fd-border",children:B.map(s=>e.jsxs("tr",{className:"hover:[background-color:var(--fd-bg)]",children:[e.jsx("td",{className:"px-4 py-2.5",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(V,{size:15,className:s.is_system?"fd-text-subtle":a.accentText}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("p",{className:"font-medium fd-text",children:s.name}),s.description&&e.jsx("p",{className:"truncate text-xs fd-text-muted-2 max-w-[280px]",children:s.description})]}),s.is_system&&e.jsx("span",{className:"ml-1 shrink-0 rounded-full fd-surface-2 px-2 py-0.5 text-[11px] font-semibold fd-text-muted-2",children:t("rolesTab.systemRole","Built-in")})]})}),e.jsx("td",{className:"px-4 py-2.5",children:e.jsx("span",{className:"rounded-full border fd-border px-2 py-0.5 text-xs font-semibold fd-text-muted-2",children:s.rank})}),e.jsx("td",{className:"px-4 py-2.5 fd-text-muted-2",children:v[s.id]||0}),e.jsx("td",{className:"px-4 py-2.5 fd-text-muted-2",children:s.permission_count}),e.jsx("td",{className:"px-4 py-2.5",children:de(s.id)>0?e.jsxs("span",{className:"inline-flex items-center gap-1 text-amber-700",children:[e.jsx(fe,{size:13})," ",de(s.id)]}):e.jsx("span",{className:"fd-text-subtle",children:"0"})}),e.jsx("td",{className:"px-4 py-2.5 fd-text-subtle text-xs",children:s.is_system?"—":ee[s.id]?new Date(ee[s.id]).toLocaleDateString():"—"}),e.jsx("td",{className:"px-4 py-2.5 text-right",children:e.jsx("div",{className:"inline-flex items-center gap-1.5",children:s.is_system||!w?e.jsxs("button",{onClick:()=>W(s),className:"inline-flex items-center gap-1 rounded-lg border fd-border px-2.5 py-1.5 text-xs font-medium fd-text-muted hover:[background-color:var(--fd-surface-2)] cursor-pointer",children:[e.jsx(Ue,{size:13})," ",t("rolesTab.viewPermissions","View permissions")]}):e.jsxs("button",{onClick:()=>W(s),className:"inline-flex items-center gap-1 rounded-lg border fd-border px-2.5 py-1.5 text-xs font-medium fd-text-muted hover:[background-color:var(--fd-surface-2)] cursor-pointer",children:[e.jsx(We,{size:13})," ",t("rolesTab.edit","Edit")]})})})]},s.id))})]})})})]}),Te&&e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",children:e.jsxs("div",{className:"w-full max-w-md rounded-2xl fd-surface shadow-xl",children:[e.jsxs("div",{className:"flex items-center gap-3 border-b fd-border-2 px-5 py-4",children:[e.jsx(pe,{size:18,className:a.accentText}),e.jsx("h3",{className:"flex-1 text-base font-semibold fd-text",children:t("rolesTab.newRoleTitle","Create a custom role")}),e.jsx("button",{onClick:()=>C(!1),className:"fd-text-subtle hover:[color:var(--fd-text)] cursor-pointer",children:e.jsx(F,{size:18})})]}),e.jsxs("div",{className:"space-y-4 px-5 py-5",children:[e.jsxs("div",{children:[e.jsx("label",{className:"mb-1 block text-xs font-medium uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.startFrom","Start from")}),e.jsxs("select",{value:c.templateId,onChange:s=>N(r=>({...r,templateId:s.target.value})),className:`w-full rounded-lg border fd-border fd-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 ${a.focusRing}/20 ${a.focusBorder} cursor-pointer`,children:[e.jsx("option",{value:"",children:t("rolesTab.blankRole","Blank role (no permissions)")}
1),B.map(s=>e.jsxs("option",{value:s.id,children:[s.name," (",s.permission_count," ",t("rolesTab.permissionsShort","permissions"),")"]},s.id))]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"mb-1 block text-xs font-medium uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.roleName","Role name")}),e.jsx("input",{value:c.name,onChange:s=>N(r=>({...r,name:s.target.value})),maxLength:60,placeholder:t("rolesTab.roleNamePlaceholder","e.g. KB Editor, Billing Admin"),className:`w-full rounded-lg border fd-border px-3 py-2 text-sm focus:outline-none focus:ring-2 ${a.focusRing}/20 ${a.focusBorder}`})]}),e.jsxs("div",{children:[e.jsx("label",{className:"mb-1 block text-xs font-medium uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.roleDescription","Description")}),e.jsx("input",{value:c.description,onChange:s=>N(r=>({...r,description:s.target.value})),placeholder:t("rolesTab.roleDescriptionPlaceholder","What is this role for?"),className:`w-full rounded-lg border fd-border px-3 py-2 text-sm focus:outline-none focus:ring-2 ${a.focusRing}/20 ${a.focusBorder}`})]}),e.jsxs("div",{children:[e.jsx("label",{className:"mb-1 block text-xs font-medium uppercase tracking-wide fd-text-muted-2",children:t("rolesTab.rank","Seniority rank")}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"number",min:1,max:89,value:c.rank,onChange:s=>N(r=>({...r,rank:s.target.value})),className:`w-24 rounded-lg border fd-border px-3 py-2 text-sm focus:outline-none focus:ring-2 ${a.focusRing}/20 ${a.focusBorder}`}),Number(c.rank)>=40?e.jsx("span",{className:"inline-flex items-center gap-1 rounded-full bg-amber-50 px-2 py-0.5 text-[11px] font-semibold text-amber-700",children:t("rolesTab.paidSeat","Uses a paid seat")}):e.jsx("span",{className:"text-[11px] fd-text-subtle",children:t("rolesTab.freeSeat","Does not use a paid seat")})]}),e.jsx("p",{className:"mt-1 text-xs fd-text-subtle",children:t("rolesTab.rankHint","Rank decides directory-sync tie-breaks and whether the role bills a seat (40 and above). Built-in ranks: Agent 50, Restricted Agent 40, Collaborator 20.")})]})]}),e.jsxs("div",{className:"flex items-center justify-end gap-2 border-t fd-border-2 px-5 py-4",children:[e.jsx("button",{onClick:()=>C(!1),className:"rounded-lg border fd-border px-4 py-2 text-sm fd-text-muted hover:[background-color:var(--fd-bg)] cursor-pointer",children:t("rolesTab.cancel","Cancel")}),e.jsxs("button",{onClick:ze,disabled:g||c.name.trim().length<2,className:`inline-flex items-center gap-1.5 rounded-lg ${a.primaryBg} ${a.primaryBgHover} px-4 py-2 text-sm font-medium text-white disabled:opacity-50 cursor-pointer transition-colors`,children:[g?e.jsx(H,{size:14,className:"animate-spin"}):e.jsx(ue,{size:14}),t("rolesTab.createRole","Create role")]})]})]})}),Ce&&n&&e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",children:e.jsxs("div",{className:"w-full max-w-lg rounded-2xl fd-surface shadow-xl",children:[e.jsxs("div",{className:"flex items-center gap-3 border-b fd-border-2 px-5 py-4",children:[e.jsx(V,{size:18,className:a.accentText}),e.jsx("h3",{className:"flex-1 text-base font-semibold fd-text",children:t("rolesTab.confirmTitle","Review changes to {{name}}",{name:n.name})}),e.jsx("button",{onClick:()=>_(!1),className:"fd-text-subtle hover:[color:var(--fd-text)] cursor-pointer",children:e.jsx(F,{size:18})})]}),e.jsxs("div",{className:"space-y-4 px-5 py-5",children:[(v[n.id]||0)>0&&e.jsx("p",{className:"rounded-lg fd-surface-2 px-3 py-2 text-sm fd-text-muted",children:t("rolesTab.affectedUsers","This change immediately affects {{count}} person(s) holding this role.",{count:v[n.id]||0})}),S.length>0&&e.jsxs("div",{children:[e.jsx("p",{className:"mb-1 text-xs font-semibold uppercase tracking-wide text-emerald-700",children:t("rolesTab.addedPerms","Added")}),e.jsx("div",{className:"flex flex-wrap gap-1.5",children:S.map(s=>e.jsxs("span",{className:"inline-flex items-center gap-1 rounded-full bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700",children:[f[s]?.label||s,e.jsx(he,{level:f[s]?.risk_level,t})]},s))})]}),G.length>0&&e.jsxs("div",{children:[e.jsx("p",{className:"mb-1 text-xs font-semibold uppercase tracking-wide text-red-600",children:t("rolesTab.removedPerms","Removed")}),e.jsx("div",{className:"flex flex-wrap gap-1.5",children:G.map(s=>e.jsx("span",{className:"inline-flex items-center rounded-full bg-red-50 px-2 py-0.5 text-xs font-medium text-red-600",children:f[s]?.label||s},s))})]}),!O&&I&&e.jsx("p",{className:"text-sm fd-text-muted-2",children:t("rolesTab.metaOnlyChange","Only the name, description, or rank changes — permissions stay the same.")}),M.length>0&&e.jsxs("div",{children:[e.jsxs("p",{className:"mb-1 flex items-center gap-1 text-sm font-semibold text-red-600",children:[e.jsx(fe,{size:14}),t("rolesTab.criticalReasonTitle","You are granting critical permissions")]}),e.jsx("p",{className:"mb-2 text-xs fd-text-muted-2",children:t("rolesTab.criticalReasonHint","Adding {{keys}} requires a short reason for the audit log.",{keys:M.join(", ")})}),e.jsx("textarea",{value:q,onChange:s=>oe(s.target.value),rows:2,placeholder:t("rolesTab.reasonPlaceholder","Why does this role need these permissions?"),className:`w-full rounded-lg border fd-border px-3 py-2 text-sm focus:outline-none focus:ring-2 ${a.focusRing}/20 ${a.focusBorder}`})]})]}),e.jsxs("div",{className:"flex items-center justify-end gap-2 border-t fd-border-2 px-5 py-4",children:[e.jsx("button",{onClick:()=>_(!1),className:"rounded-lg border fd-border px-4 py-2 text-sm fd-text-muted hover:[background-color:var(--fd-bg)] cursor-pointer",children:t("rolesTab.cancel","Cancel")}),e.jsxs("button",{onClick:Be,disabled:g||M.length>0&&q.trim().length<5,className:`inline-flex items-center gap-1.5 rounded-lg ${a.primaryBg} ${a.primaryBgHover} px-4 py-2 text-sm font-medium text-white disabled:opacity-50 cursor-pointer transition-colors`,children:[g?e.jsx(H,{size:14,className:"animate-spin"}):e.jsx(be,{size:14}),t("rolesTab.saveChanges","Save changes")]})]})]})}),j&&e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",children:e.jsxs("div",{className:"w-full max-w-md rounded-2xl fd-surface shadow-xl",children:[e.jsxs("div",{className:"flex items-center gap-3 border-b fd-border-2 px-5 py-4",children:[e.jsx(X,{size:18,className:"text-red-500"}),e.jsx("h3",{className:"flex-1 text-base font-semibold fd-text",children:t("rolesTab.deleteTitle","Delete {{name}}?",{name:j.name})}),e.jsx("button",{onClick:()=>D(null),className:"fd-text-subtle hover:[color:var(--fd-text)] cursor-pointer",children:e.jsx(F,{size:18})})]}),e.jsx("div",{className:"space-y-3 px-5 py-5",children:(v[j.id]||0)>0?e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"text-sm fd-text-muted",children:t("rolesTab.deleteRemapHint","{{count}} person(s) currently hold this role. Choose the role they should move to.",{count:v[j.id]||0})}),e.jsxs("select",{value:P,onChange:s=>U(s.target.value),className:`w-full rounded-lg border fd-border fd-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 ${a.focusRing}/20 ${a.focusBorder} cursor-pointer`,children:[e.jsx("option",{value:"",children:t("rolesTab.pickRemapRole","Select a role…")}),B.filter(s=>s.id!==j.id&&s.name!=="Owner").map(s=>e.jsx("option",{value:s.id,children:s.name},s.id))]})]}):e.jsx("p",{className:"text-sm fd-text-muted",children:t("rolesTab.deleteNoAssignees","No one holds this role. It will be removed permanently.")})}),e.jsxs("div",{className:"flex items-center justify-end gap-2 border-t fd-border-2 px-5 py-4",children:[e.jsx("button",{onClick:()=>D(null),className:"rounded-lg border fd-border px-4 py-2 text-sm fd-text-muted hover:[background-color:var(--fd-bg)] cursor-pointer",children:t("rolesTab.cancel","Cancel")}),e.jsxs("button",{onClick:$e,disabled:g||(v[j.id]||0)>0&&!P,className:"inline-flex items-center gap-1.5 rounded-lg bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700 disabled:opacity-50 cursor-pointer transition-colors",children:[g?e.jsx(H,{size:14,className:"animate-spin"}):e.jsx(X,{size:14}),t("rolesTab.deleteConfirm","Delete role")]})]})]})})]})}export{ls as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.