/* Admin → Settings → Roles & Access (full RBAC view) */
const JL_ROLES = [
{ id:'super', name:'Super Admin', scope:'District-wide', initials:'DR', color:'gold', count:3 },
{ id:'admin', name:'School Admin', scope:'Single school', initials:'SA', color:'navy', count:12 },
{ id:'teacher', name:'Teacher', scope:'Own classes', initials:'MJ', color:'blue', count:86 },
{ id:'driver', name:'Driver', scope:'Assigned route', initials:'MT', color:'green', count:25 },
{ id:'parent', name:'Parent', scope:'Own children', initials:'JH', color:'cyan', count:1248 },
{ id:'student', name:'Student', scope:'Own profile', initials:'EJ', color:'red', count:1248 },
];
/* access level per capability per role: 'full' | 'own' | 'none' */
const JL_CAPS = [
{ cap:'View student records', icon:'users', a:{ super:'full', admin:'full', teacher:'own', driver:'own', parent:'own', student:'own' } },
{ cap:'Live bus tracking', icon:'bus', a:{ super:'full', admin:'full', teacher:'none', driver:'own', parent:'own', student:'own' } },
{ cap:'Message families', icon:'message-circle', a:{ super:'full', admin:'full', teacher:'full', driver:'none', parent:'full', student:'none' } },
{ cap:'Pickup & dismissal', icon:'car-front', a:{ super:'full', admin:'full', teacher:'own', driver:'own', parent:'own', student:'own' } },
{ cap:'Process payments', icon:'credit-card', a:{ super:'full', admin:'full', teacher:'none', driver:'none', parent:'own', student:'none' } },
{ cap:'Reports & analytics', icon:'bar-chart-3', a:{ super:'full', admin:'own', teacher:'own', driver:'none', parent:'none', student:'none' } },
{ cap:'Manage roles & settings', icon:'settings', a:{ super:'full', admin:'own', teacher:'none', driver:'none', parent:'none', student:'none' } },
];
function AccessCell({ level }) {
const map = {
full: { icon:'check', bg:'var(--success-100)', fg:'var(--success-600)', label:'Full' },
own: { icon:'user', bg:'var(--warning-100)', fg:'var(--warning-600)', label:'Own data' },
none: { icon:'minus', bg:'var(--gray-100)', fg:'var(--gray-400)', label:'None' },
};
const m = map[level];
return (
);
}
function RolesAccess() {
const [active, setActive] = React.useState('admin');
const [caps, setCaps] = React.useState(JL_CAPS);
const [dirty, setDirty] = React.useState(false);
const cycle = (capIndex, roleId) => {
const nextLevel = { full:'own', own:'none', none:'full' };
setCaps(cs => cs.map((row,i)=> i===capIndex ? { ...row, a:{ ...row.a, [roleId]: nextLevel[row.a[roleId]] } } : row));
setDirty(true);
};
const fmt = n => n >= 1000 ? (n/1000).toFixed(n%1000?1:0)+'k' : ''+n;
return (
{/* role cards */}
jlToast('New custom role', { icon:'plus' })} style={{ display:'flex', alignItems:'center', gap:6, background:'var(--navy-600)', color:'#fff', border:0, borderRadius:9, padding:'8px 13px', fontWeight:700, fontSize:12.5, cursor:'pointer', fontFamily:'var(--font-sans)' }}> Add Role}>
{JL_ROLES.map(r => {
const on = active===r.id;
return (
setActive(r.id)} style={{ textAlign:'left', display:'flex', alignItems:'center', gap:11, padding:'13px 14px', borderRadius:12, cursor:'pointer', fontFamily:'var(--font-sans)', background: on?'var(--blue-100)':'#fff', border: on?'1.5px solid var(--blue-500)':'1px solid var(--gray-200)' }}>
);
})}
{/* permissions matrix */}
{dirty && { setDirty(false); jlToast('Permissions saved · applied to all members', { icon:'shield-check' }); }} style={{ display:'flex', alignItems:'center', gap:6, background:'var(--gold-400)', color:'var(--on-gold)', border:0, borderRadius:9, padding:'8px 13px', fontWeight:700, fontSize:12.5, cursor:'pointer', fontFamily:'var(--font-sans)', boxShadow:'var(--shadow-gold)' }}> Save Changes }
} pad={false}>
{/* header */}
Capability
{JL_ROLES.map(r => (
{r.name.replace(' Admin',' Adm.')}
))}
{/* rows */}
{caps.map((row, i) => (
0?'1px solid var(--gray-100)':'0' }}>
{row.cap}
{JL_ROLES.map(r => (
cycle(i, r.id)} title="Click to change access" style={{ justifySelf:'center', padding:'2px 4px', borderRadius:8, border:0, cursor:'pointer', background: active===r.id?'rgba(42,105,188,.06)':'transparent' }}>
))}
))}
{/* data-scope note */}
Row-level security is enforced everywhere. “Own data” means a teacher sees only their class, a parent only their children, a driver only their route, and a student only their own profile — never another family's information. Tap any cell above to adjust a role's access.
);
}
function Legend({ level, label }) {
return (
{label}
);
}
Object.assign(window, { RolesAccess, JL_ROLES, JL_CAPS });