// Role strings, the static role-to-screen map and the labels. // The server decides access on every request. This file only shapes the screens. import type { Department } from "./types"; /** Screens in the order the role switcher shows them (the old build's order). */ export const SCREENS = ["clerical", "warehouse", "supervisor", "stall", "qc", "pm", "admin"] as const; export type Screen = (typeof SCREENS)[number]; export const SCREEN_LABELS: Record = { clerical: "Clerical", warehouse: "Warehouse", supervisor: "Supervisor", stall: "Weld Stall", qc: "Q.C.", pm: "Project Mgmt", admin: "Admin", }; /** The phase in which each placeholder screen gets its real content. */ export const SCREEN_PHASE: Record, number> = { clerical: 2, warehouse: 4, supervisor: 5, stall: 5, qc: 6, pm: 8, }; export function isScreen(s: string | undefined): s is Screen { return s !== undefined && (SCREENS as readonly string[]).includes(s); } export type ShopLevel = "worker" | "lead" | "supervisor"; export type ParsedRole = | { shop: false; kind: string } | { shop: true; dept: string; level: ShopLevel }; const SHOP_LEVELS: readonly ShopLevel[] = ["worker", "lead", "supervisor"]; /** "shop:03:supervisor" -> shop role; anything else is an office role kind. */ export function parseRole(role: string): ParsedRole { const m = /^shop:([^:]+):(worker|lead|supervisor)$/.exec(role); if (m && m[1] !== undefined && m[2] !== undefined) { return { shop: true, dept: m[1], level: m[2] as ShopLevel }; } return { shop: false, kind: role }; } /** Turns a role kind and department ("shop_lead", "03") into the role string ("shop:03:lead"). */ export function roleFromKind(kind: string, dept: string | null): string { if (kind.includes(":")) return kind; const m = /^shop_(worker|lead|supervisor)$/.exec(kind); if (m && m[1] && dept) return `shop:${dept}:${m[1]}`; return kind; } /** The static map. Same rule as the server's screens_for(roles). */ export function screensFor(roles: readonly string[]): Screen[] { const out = new Set(); for (const r of roles) { const p = parseRole(r); if (p.shop) { out.add("stall"); if (p.level !== "worker") out.add("supervisor"); continue; } if (p.kind === "clerical" || p.kind === "clerical_supervisor") out.add("clerical"); else if (p.kind.startsWith("warehouse")) out.add("warehouse"); else if (p.kind.startsWith("qc")) out.add("qc"); else if (p.kind === "pm") out.add("pm"); else if (p.kind === "admin") SCREENS.forEach((s) => out.add(s)); } return SCREENS.filter((s) => out.has(s)); } /** Known screens from a server list, in switcher order. */ export function orderScreens(list: readonly string[]): Screen[] { return SCREENS.filter((s) => list.includes(s)); } /** The screen a user lands on: the first non-admin screen, else admin, else null. */ export function firstScreen(list: readonly string[]): Screen | null { const ordered = orderScreens(list); return ordered.find((s) => s !== "admin") ?? ordered[0] ?? null; } export function homePath(list: readonly string[]): string { const s = firstScreen(list); return s ? `/${s}` : "/account"; } const OFFICE_LABELS: Record = { admin: "Admin", clerical: "Clerical", clerical_supervisor: "Clerical supervisor", warehouse: "Warehouse", warehouse_supervisor: "Warehouse supervisor", qc: "Q.C.", qc_supervisor: "Q.C. supervisor", pm: "Project Management", }; const LEVEL_LABELS: Record = { worker: "Worker", lead: "Lead", supervisor: "Supervisor" }; /** "shop:03:supervisor" -> "03 Weld Parts · Supervisor". Office roles get their plain name. */ export function roleLabel(role: string, departments: readonly Department[] = []): string { const p = parseRole(role); if (!p.shop) return OFFICE_LABELS[p.kind] ?? p.kind; const dept = departments.find((d) => d.code === p.dept); const where = dept ? `${p.dept} ${dept.name}` : p.dept; return `${where} · ${LEVEL_LABELS[p.level]}`; } /** The .rchip variant for a role: supervisors pink, leads green, admin amber. */ export function roleChipClass(role: string): string { const p = parseRole(role); if (p.shop) return p.level === "worker" ? "rchip" : `rchip ${p.level}`; if (p.kind === "admin") return "rchip admin"; if (p.kind.endsWith("_supervisor")) return "rchip supervisor"; return "rchip"; } /** Office roles first in a fixed order, then shop roles by department and level. */ export function sortRoles(roles: readonly string[]): string[] { const office = Object.keys(OFFICE_LABELS); const rank = (r: string): [number, string, number] => { const p = parseRole(r); if (!p.shop) return [0, "", office.includes(p.kind) ? office.indexOf(p.kind) : office.length]; return [1, p.dept, SHOP_LEVELS.indexOf(p.level)]; }; return [...roles].sort((a, b) => { const [a0, a1, a2] = rank(a); const [b0, b1, b2] = rank(b); return a0 - b0 || a1.localeCompare(b1) || a2 - b2; }); }