/* ═══════════════════════════════════════════════════════════════ ROLES — src/lib/roles.ts The same ladder as server/src/auth.js, and it has to stay the same ladder. This copy exists to decide what to draw; the server's copy decides what's allowed. If they ever disagree the worst case is a button that 403s, which is the right way round for them to fail. Components should ask canDelete(user), not user.role === "admin". The second form is what silently locked superadmins out of saving when the third role went in: an equality check against a ladder is a bug waiting for the next role to be added, and there's now a fourth. ═══════════════════════════════════════════════════════════════ */ export const ROLES = ["viewer", "editor", "admin", "superadmin"] as const; export type Role = (typeof ROLES)[number]; export const ROLE_RANK: Record = { viewer: 1, editor: 2, admin: 3, superadmin: 4, }; export const ROLE_LABELS: Record = { viewer: "Viewer", editor: "Editor", admin: "Admin", superadmin: "Superadmin", }; /* Each line is what that role adds to the one above it in the list. Read top to bottom, they describe the whole ladder. */ export const ROLE_NOTES: Record = { viewer: "Can read everything in the CMS and change nothing.", editor: "Can create and update records. Can't delete anything.", admin: "Can delete records, including feedback.", superadmin: "Can manage accounts, roles and sessions.", }; type MaybeUser = { role?: string | null } | null | undefined; /* Minimum, not equality: a superadmin passes atLeast(user, "editor"). */ export function atLeast(user: MaybeUser, role: Role): boolean { const have = ROLE_RANK[(user?.role ?? "") as Role] ?? 0; return have >= ROLE_RANK[role]; } /* Named for the capability rather than the rank, so call sites read as intent and a future reshuffle of the ladder is one edit here rather than a search for every comparison. */ export const canWrite = (user: MaybeUser) => atLeast(user, "editor"); export const canDelete = (user: MaybeUser) => atLeast(user, "admin"); export const isSuper = (user: MaybeUser) => atLeast(user, "superadmin");