PageSourceSearch

https://etaps.ai/assets/RolesSettings-BZMAUlPc.js

js etaps.ai collected 2026-10-05 14:21:46 UTC 6,266 bytes, 1 lines download raw bytes

1import{r as m,j as e}from"./react-2yNL-0jO.js";import{h as T,u as E,s as d,a as p,X as P,Y as q,I as A,T as I,V as L,W as D}from"./index-0I6tP8u4.js";import{C as F}from"./card-Dyz-4WUa.js";import{L as C}from"./label-BEk_pbj2.js";import{S}from"./switch-BsEXM-pL.js";import{S as O,a as H,b as V,c as W}from"./sheet-3vQ-Z1K-.js";import{T as z,a as B,b as N,c as v,d as $,e as g}from"./table-BSRFXuid.js";import{S as G,a as J,b as K,c as Q,d as U}from"./select-Y_CU5rpp.js";import{F as X}from"./FeaturePermissionGrid-CXiqLaqv.js";import{O as Y,aQ as M,ae as Z,aO as ee,be as se}from"./lucide-YE_Cg0sW.js";import"./admin-vendor-CGWxZvmW.js";import"./react-router-C95oXNrA.js";import"./ui-vendor-C2kPg7wM.js";import"./date-fns-VUYwK_nv.js";import"./checkbox-pUtHehaw.js";import"./collapsible-CGFYeJJd.js";function ge(){const{user:r}=T(),{toast:n}=E(),l=r==null?void 0:r.companyId,[a,o]=m.useState([]),[t,j]=m.useState(!0),[b,f]=m.useState(null),x=m.useCallback(async()=>{if(!l)return;j(!0);const{data:s}=await d.from("roles").select("*").eq("company_id",l).order("name");o(s||[]),j(!1)},[l]);m.useEffect(()=>{x()},[x]);const y=()=>f({name:"",description:"",is_active:!0,permissions:P()}),_=async s=>{const{data:c}=await d.from("role_permissions").select("*").eq("role_id",s.id),i={};(c||[]).forEach(u=>{i[u.module_key]=u}),f({...s,permissions:q(i)})},k=async s=>{var c;if(!((c=s==null?void 0:s.name)!=null&&c.trim())){n({title:"Name is required",variant:"destructive"});return}if(l)try{let i=s.id;if(i){const{error:h}=await d.from("roles").update({name:s.name.trim(),description:s.description||null,is_active:s.is_active}).eq("id",i).eq("company_id",l);if(h)throw h}else{const{data:h,error:w}=await d.from("roles").insert({company_id:l,name:s.name.trim(),description:s.description||null,is_active:s.is_active??!0}).select("id").single();if(w)throw w;i=h.id}const{error:u}=await d.from("role_permissions").upsert(s.permissions.map(h=>({...h,role_id:i,company_id:l})),{onConflict:"role_id,module_key"});if(u)throw u;n({title:"Role saved"}),f(null),await x()}catch(i){n({title:"Save failed",description:(i==null?void 0:i.message)||"Could not save role",variant:"destructive"})}},R=async s=>{const{count:c}=await d.from("profiles").select("id",{count:"exact",head:!0}).eq("custom_role_id",s.id);if(c){n({title:"Cannot delete",description:`${c} team member(s) are assigned this role. Reassign them first.`,variant:"destructive"});return}if(!confirm(`Delete role "${s.name}"?`))return;const{error:i}=await d.from("roles").delete().eq("id",s.id);if(i){n({title:"Delete failed",description:i.message,variant:"destructive"});return}n({title:"Role deleted"}),await x()};return t?e.jsx("div",{className:"p-10 text-center",children:e.jsx(Y,{className:"h-6 w-6 animate-spin inline"})}):e.jsxs("div",{className:"p-6 space-y-4 max-w-4xl",children:[e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsxs("div",{children:[e.jsxs("h1",{className:"text-2xl font-bold flex items-center gap-2",children:[e.jsx(M,{className:"h-5 w-5"})," Roles"]}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Create custom roles with per-module access instead of relying on fixed roles. Assign a role to a team member under Settings → Team."})]}),e.jsxs(p,{onClick:y,children:[e.jsx(Z,{className:"h-4 w-4 mr-1"})," Create Role"]})]}),e.jsx(F,{children:a.length===0?e.jsx("div",{className:"p-10 text-center text-muted-foreground",children:"No custom roles yet — create one above. Team members without a custom role keep full access."}):e.jsxs(z,{children:[e.jsx(B,{children:e.jsxs(N,{children:[e.jsx(v,{children:"Name"}),e.jsx(v,{children:"Description"}),e.jsx(v,{children:"Active"}),e.jsx(v,{className:"text-right",children:"Actions"})]})}),e.jsx($,{children:a.map(s=>e.jsxs(N,{children:[e.jsx(g,{className:"font-medium",children:s.name}),e.jsx(g,{className:"text-muted-foreground text-sm",children:s.description||"—"}),e.jsx(g,{children:e.jsx(S,{checked:s.is_active,onCheckedChange:async c=>{await d.from("roles").update({is_active:c}).eq("id",s.id),x()}})}),e.jsxs(g,{className:"text-right",children:[e.jsx(p,{size:"sm",variant:"ghost",onClick:()=>_(s),children:e.jsx(ee,{className:"h-4 w-4"})}),e.jsx(p,{size:"sm",variant:"ghost",onClick:()=>R(s),children:e.jsx(se,{className:"h-4 w-4"})})]})]},s.id))})]})}),e.jsx(te,{form:b,onClose:()=>f(null),onSave:k})]})}function te({form:r,onClose:n,onSave:l}){const[a,o]=m.useState(r);return m.useEffect(()=>o(r),[r]),!r||!a?null:e.jsx(O,{open:!0,onOpenChange:t=>
1!t&&n(),children:e.jsxs(H,{className:"flex flex-col sm:max-w-xl",children:[e.jsx(V,{children:e.jsx(W,{children:r.id?"Edit Role":"Create Role"})}),e.jsxs("div",{className:"space-y-4 mt-4 flex-1 overflow-y-auto pr-1 -mr-1",children:[e.jsxs("div",{children:[e.jsx(C,{children:"Role Name *"}),e.jsx(A,{value:a.name,onChange:t=>o({...a,name:t.target.value}),placeholder:"e.g. Junior Sales Agent"})]}),e.jsxs("div",{children:[e.jsx(C,{children:"Description"}),e.jsx(I,{rows:2,value:a.description||"",onChange:t=>o({...a,description:t.target.value}),placeholder:"What is this role for?"})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx(C,{children:"Feature Access"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:"Start from"}),e.jsxs(G,{onValueChange:t=>o({...a,permissions:L(t)}),children:[e.jsx(J,{className:"h-8 w-40",children:e.jsx(K,{placeholder:"Preset…"})}),e.jsx(Q,{children:Object.entries(D).map(([t,j])=>e.jsx(U,{value:t,children:j.label},t))})]})]})]}),e.jsx("p",{className:"text-xs text-muted-foreground mb-2",children:"Read shows the feature in the menu and page. Write/Create/Delete gate actions where supported. Pick a preset then fine-tune."}),e.jsx(X,{value:a.permissions,onChange:t=>o({...a,permissions:t})})]}),e.jsxs("label",{className:"flex items-center gap-2",children:[e.jsx(S,{checked:a.is_active,onCheckedChange:t=>o({...a,is_active:t})})," Active"]})]}),e.jsxs("div",{className:"flex gap-2 pt-3 border-t shrink-0",children:[e.jsx(p,{className:"flex-1",onClick:()=>l(a),children:"Save"}),e.jsx(p,{variant:"outline",className:"flex-1",onClick:n,children:"Cancel"})]})]})})}export{ge 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.