/** * Admin Panel: Roles & Permissions. Shows the permission catalog grouped * by module (GET /admin/permissions, auth.PERMISSIONS) and every role's * grants (GET /admin/roles). System roles (Admin, Recruiter, * Interviewer, Candidate) are READ-ONLY here -- see * azure-functions/api_blueprint.py's update_role_endpoint docstring for * why: they're the deliberately-designed least-privilege baseline, and * letting them drift silently would break guarantees other features * depend on (e.g. Recruiter never holding screening.approve). Only a * custom role created here can be edited. */ function RoleManagement(props) { var api = props.api; var snapshot = CDPI_REPORT_SNAPSHOT.useReportSnapshot("role_management", function () { return Promise.all([api.listRoles(), api.listPermissions()]) .then(function (results) { return { roles: results[0].roles, permissions: results[1].permissions }; }); }); var data = { loading: snapshot.state.loading && !snapshot.state.data, error: snapshot.state.error, roles: (snapshot.state.data && snapshot.state.data.roles) || [], permissions: (snapshot.state.data && snapshot.state.data.permissions) || {}, }; var load = snapshot.refresh; var expandedState = React.useState(null); var expanded = expandedState[0]; var setExpanded = expandedState[1]; var createOpenState = React.useState(false); var createOpen = createOpenState[0]; var setCreateOpen = createOpenState[1]; var errorMsg = React.useState(null); var error = errorMsg[0]; var setError = errorMsg[1]; var modules = {}; Object.keys(data.permissions).forEach(function (pid) { var mod = data.permissions[pid].module; (modules[mod] = modules[mod] || []).push(pid); }); return (

Roles & Permissions

Admin is the top-level role (no separate Super Admin tier) and holds every permission. System roles below are read-only; create a custom role to grant a different combination of permissions.

Snapshot from {snapshot.state.lastUpdated ? new Date(snapshot.state.lastUpdated).toLocaleString() : "just now"}
{error &&
{error}
} {data.error &&
Couldn't load roles: {data.error}
} {data.loading &&

Loading…

} {!data.loading && !data.error && (
{data.roles.map(function (r) { var isOpen = expanded === r.role_key; return ( {isOpen && ( )} ); })}
RoleDescriptionPermissionsType
{r.display_name} {r.description} {r.permission_ids.length} permission(s) {r.is_system ? "System" : "Custom"}
)} {createOpen && ( )}
); } function RolePermissionEditor(props) { var role = props.role; var modules = props.modules; var permissions = props.permissions; var onSave = props.onSave; // null for system roles -- read-only var selectedState = React.useState(role.permission_ids.slice()); var selected = selectedState[0]; var setSelected = selectedState[1]; var savingState = React.useState(false); var saving = savingState[0]; var setSaving = savingState[1]; function toggle(pid) { if (!onSave) return; setSelected(function (s) { return s.indexOf(pid) === -1 ? s.concat([pid]) : s.filter(function (p) { return p !== pid; }); }); } return (
{Object.keys(modules).sort().map(function (mod) { return (
{mod}
{modules[mod].map(function (pid) { return ( ); })}
); })} {onSave && (
)} {!onSave &&

System role — read-only. Create a custom role to use a different combination.

}
); } function CreateRoleModal(props) { var modules = props.modules; var permissions = props.permissions; var onClose = props.onClose; var onSave = props.onSave; var fieldsState = React.useState({ role_key: "", display_name: "", description: "", permission_ids: [] }); var fields = fieldsState[0]; var setFields = fieldsState[1]; var saveState = React.useState({ saving: false, error: null }); var save = saveState[0]; var setSave = saveState[1]; function setField(name, value) { setFields(function (f) { return Object.assign({}, f, { [name]: value }); }); } function toggle(pid) { setFields(function (f) { var next = f.permission_ids.indexOf(pid) === -1 ? f.permission_ids.concat([pid]) : f.permission_ids.filter(function (p) { return p !== pid; }); return Object.assign({}, f, { permission_ids: next }); }); } function handleSubmit(e) { e.preventDefault(); setSave({ saving: true, error: null }); onSave(fields).catch(function (err) { setSave({ saving: false, error: err.message }); }); } return (

New Custom Role

{Object.keys(modules).sort().map(function (mod) { return (
{mod}
{modules[mod].map(function (pid) { return ( ); })}
); })}
{save.error &&
{save.error}
}
); }