// ===================================================================== // People & Permissions — Settings screen. // Shows everyone with access to the ATS plus the role-capability matrix. // ===================================================================== const { useState: useStateP } = React; function _formatLastActive(iso) { if (!iso) return ""; const d = new Date(iso); const now = new Date(); const diffMs = now - d; const diffMin = Math.floor(diffMs / 60000); if (diffMin < 1) return "Just now"; if (diffMin < 60) return diffMin + "m ago"; const diffHr = Math.floor(diffMin / 60); if (diffHr < 24) return diffHr + "h ago"; const diffDay = Math.floor(diffHr / 24); if (diffDay < 30) return diffDay + "d ago"; return d.toLocaleDateString("en-US", { month: "short", day: "numeric" }); } const ADMIN_NAMES = ["fangchang", "edward", "karren", "nicole"]; function _isAdmin(t) { if (t.raw_role === "admin" || t.role === "Admin") return true; const first = (t.name || "").split(" ")[0].toLowerCase(); return ADMIN_NAMES.includes(first); } function _buildPeople() { const internal = TEAM.map(t => ({ id: t.id, name: t.name, email: t.email || "", initials: t.initials, color: t.color, role: _isAdmin(t) ? "admin" : "interviewer", title: t.role, last_login: t.last_login || null, scopeRoles: "all", comp: true, })); const external = [{ id: "ext-jon-luzha", name: "Jon Luzha", email: "", initials: "JL", color: "#9C6B4F", role: "external", title: "Alexander Chapman", last_login: null, scopeRoles: "ownSubs", agencyName: "Alexander Chapman", comp: false, }]; return [...internal, ...external]; } const CAPABILITIES = [ { id: "see_all", name: "See all candidates", hint: "Across every role in the workspace", perms: { admin: "yes", interviewer: "yes", external: "no" }, cellHint: { external: "Own submissions only" } }, { id: "read_write", name: "Read & write candidate data", hint: "Emails, Slack, notes, scores, profiles, stage changes", perms: { admin: "yes", interviewer: "yes", external: "partial" }, cellHint: { external: "Own submissions only" } }, { id: "hired_access", name: "Read & write Hired candidates", hint: "View and manage candidates in the Hired stage", perms: { admin: "yes", interviewer: "no", external: "no" } }, { id: "manage_users", name: "Manage users & permissions", hint: "Invite, change roles, remove access", perms: { admin: "yes", interviewer: "no", external: "no" } }, ]; function PermCell({ value, hint }) { const ch = value === "yes" ? "✓" : value === "no" ? "—" : "◐"; const cls = "ats-perm-cell ats-perm-cell--" + (value === "yes" ? "yes" : value === "no" ? "no" : "partial"); return (
{ch} {hint &&
{hint}
}
); } function RolePill({ role }) { const r = ROLES[role]; return ( {r.label} ); } // Short-form role title for compact scope chips function shortRoleTitle(t) { return t .replace("Research Scientist, Multimodal", "Research Sci · Multimodal") .replace("Staff Engineer, Real-time Systems", "Staff Eng · Real-time") .replace("ML Infrastructure Engineer", "ML Infra") .replace("Perception & Computer Vision", "Perception & CV") .replace("Senior Product Designer", "Sr Product Designer") .replace("Founding Frontend Engineer", "Founding FE"); } function ScopeCell({ p }) { const jobs = p.scopeRoles; return (
{jobs === "all" ? ( All roles · {JOBS.filter(j => j.status === "open" || j.status === "active").length} open ) : jobs === "ownSubs" ? (
{p.agencyName} their submissions only
) : (
{jobs.map(j => ( {shortRoleTitle(j)} ))} {p.panelOnly && · panel only}
)}
); } function PermissionsScreen() { const allPeople = _buildPeople(); const [, showToast, ToastHost] = useAtsToast(); const [rowMenu, setRowMenu] = useState(null); // email of row with open menu // Listen for page changes to update active tab const [currentPage, setCurrentPage] = React.useState( typeof window.__atsCurrentPage === 'string' ? window.__atsCurrentPage : 'settings' ); React.useEffect(() => { const handlePageChange = (e) => setCurrentPage(e.detail); window.addEventListener('ats-page-change', handlePageChange); return () => window.removeEventListener('ats-page-change', handlePageChange); }, []); const activeTabMap = { 'pipeline': 'Pipeline', 'my-candidates': 'My candidates', 'settings': 'Permissions' }; return (
{ const job = JOBS.find(j => j.id === id); if (job) showToast(`${job.title} — open the Pipeline artboard to view candidates`); else if (id === "__all__") showToast("All roles selected"); else if (id === null) showToast("Showing open roles"); else showToast("Saved view selected"); }} />

People & permissions

Who has access to your hiring data, what they can see, and what they can do. External agents only see candidates they personally submitted.

{/* People */}

People with access

{allPeople.length} accounts
{allPeople.map(p => ( ))}
Person Role Scope Last active
{p.name}
{p.email}
{p.last_login ? _formatLastActive(p.last_login) : ""} {rowMenu === p.id && ( <>
setRowMenu(null)} style={{ position: "fixed", inset: 0, zIndex: 40 }} />
)}
{/* Matrix */}

Permissions matrix

What each role can see and do
{CAPABILITIES.map(cap => ( {["admin","interviewer","external"].map(r => ( ))} ))}
Capability Admin Interviewer External Agent
{cap.name} {cap.hint &&
{cap.hint}
}
{ToastHost}
); } Object.assign(window, { PermissionsScreen });