/* ============================================================ permissions.js — the 104-tag vocabulary, mirrored from the backend. IMPORTANT — WHAT THIS DOES AND DOES NOT DO ------------------------------------------ Everything here is COSMETIC: it hides nav items and blocks routes in the UI. Real enforcement is `require_permission` on the server, and today that only guards /users/*, /roles/* and /permissions/*. The other 20 screens are seed data with no server behind them, and /inbox/fetch has no auth dependency at all. A ticked box in the RBAC matrix is not an access control. Source of truth: backend/users/permissions.py PermissionTag, which asserts at startup that the enum equals the full modules x actions cross-product. ============================================================ */ export const MODULES = [ 'dashboard', 'inbox', 'jobs', 'candidates', 'pipeline', 'interviews', 'assessments', 'offers', 'reports', 'analytics', 'job_board', 'settings', 'rbac_users', 'tasks', 'talent', 'requisitions', ] export const ACTIONS = [ 'view', 'create', 'edit', 'delete', 'approve', 'export', 'manage', 'configure', ] /** All `module.action` tags (modules x actions cross-product). */ export const ALL_TAGS = MODULES.flatMap((m) => ACTIONS.map((a) => `${m}.${a}`)) /** * Build a permission predicate. A null/undefined tag is always allowed — routes * like Tasks and Help have no backend module and are open to any signed-in user. */ export function makeCan(permissions) { const set = new Set(permissions ?? []) return (tag) => !tag || set.has(tag) } export const HIRING_MANAGER_ROLE = 'hiring_manager' /** Sidebar paths a manager-type role may see. Talent Pool / Matching / Import also sit on candidates.view/create, so they are excluded here. */ export const HIRING_MANAGER_NAV = new Set([ 'candidates', 'requisitions', 'interviews', 'calendar', 'help', 'aiassistant', 'aistudio', 'notifications', ]) export function isHiringManager(user) { const name = (user?.role_name || '').trim().toLowerCase() return name === HIRING_MANAGER_ROLE || name === 'manager' }