Gated behind NEXT_PUBLIC_SUBMITTER_VIEW_ALL_ENABLED (opt-in, "true"). When on, submitter roles (TECHNICAL/MANNING) get read-only access to every PO: the History page + report export, any other user's PO detail page, and the per-PO Export PDF/XLSX buttons. No approval/payment/edit rights are added. - lib/feature-flags.ts: SUBMITTER_VIEW_ALL_ENABLED flag - lib/permissions.ts: isSubmitterRole / submitterCanViewAll / canViewAllPos - po/[id] page + export route: gate via canViewAllPos - history page + reports/export route: OR submitterCanViewAll into export_reports - sidebar: show History to submitters when flag on - tests: permission helpers, both flag states - docs: .env.example, CLAUDE.md (wiki updated separately) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
123 lines
3.5 KiB
TypeScript
123 lines
3.5 KiB
TypeScript
import type { Role } from "@prisma/client";
|
|
import { SUBMITTER_VIEW_ALL_ENABLED } from "./feature-flags";
|
|
|
|
export type Permission =
|
|
| "create_po"
|
|
| "submit_po"
|
|
| "edit_own_draft_po"
|
|
| "view_own_pos"
|
|
| "view_all_pos"
|
|
| "approve_po"
|
|
| "reject_po"
|
|
| "cancel_po"
|
|
| "request_edits"
|
|
| "request_vendor_id"
|
|
| "process_payment"
|
|
| "confirm_receipt"
|
|
| "view_analytics"
|
|
| "export_reports"
|
|
| "manage_users"
|
|
| "manage_vendors"
|
|
| "create_vendor"
|
|
| "manage_vessels_accounts"
|
|
| "manage_products"
|
|
| "manage_sites";
|
|
|
|
const ROLE_PERMISSIONS: Record<Role, Permission[]> = {
|
|
TECHNICAL: ["create_po", "submit_po", "edit_own_draft_po", "view_own_pos", "confirm_receipt", "create_vendor"],
|
|
MANNING: ["create_po", "submit_po", "edit_own_draft_po", "view_own_pos", "confirm_receipt", "create_vendor"],
|
|
ACCOUNTS: ["view_all_pos", "process_payment", "manage_vendors", "create_vendor"],
|
|
MANAGER: [
|
|
"create_po",
|
|
"submit_po",
|
|
"edit_own_draft_po",
|
|
"view_own_pos",
|
|
"view_all_pos",
|
|
"approve_po",
|
|
"reject_po",
|
|
"cancel_po",
|
|
"request_edits",
|
|
"request_vendor_id",
|
|
"view_analytics",
|
|
"export_reports",
|
|
"manage_vendors",
|
|
"create_vendor",
|
|
"manage_vessels_accounts",
|
|
"manage_products",
|
|
"manage_sites",
|
|
"confirm_receipt",
|
|
"process_payment"
|
|
],
|
|
SUPERUSER: [
|
|
"create_po",
|
|
"submit_po",
|
|
"edit_own_draft_po",
|
|
"view_own_pos",
|
|
"view_all_pos",
|
|
"approve_po",
|
|
"reject_po",
|
|
"cancel_po",
|
|
"request_edits",
|
|
"request_vendor_id",
|
|
"process_payment",
|
|
"confirm_receipt",
|
|
"view_analytics",
|
|
"export_reports",
|
|
"create_vendor",
|
|
],
|
|
AUDITOR: ["view_own_pos", "view_all_pos", "view_analytics", "export_reports"],
|
|
ADMIN: [
|
|
"view_own_pos",
|
|
"view_all_pos",
|
|
"view_analytics",
|
|
"export_reports",
|
|
"manage_users",
|
|
"manage_vendors",
|
|
"create_vendor",
|
|
"manage_vessels_accounts",
|
|
"manage_products",
|
|
"manage_sites",
|
|
],
|
|
};
|
|
|
|
export function hasPermission(role: Role, permission: Permission): boolean {
|
|
return ROLE_PERMISSIONS[role]?.includes(permission) ?? false;
|
|
}
|
|
|
|
export function requirePermission(role: Role, permission: Permission): void {
|
|
if (!hasPermission(role, permission)) {
|
|
throw new Error(`Forbidden: role ${role} lacks permission ${permission}`);
|
|
}
|
|
}
|
|
|
|
export function getPermissions(role: Role): Permission[] {
|
|
return ROLE_PERMISSIONS[role] ?? [];
|
|
}
|
|
|
|
// ── Submitter roles & feature-flagged view-all ────────────────────────────────
|
|
// Submitters raise and track their own POs. The two "submitter" roles below hold
|
|
// `view_own_pos` but not `view_all_pos`.
|
|
|
|
export const SUBMITTER_ROLES: Role[] = ["TECHNICAL", "MANNING"];
|
|
|
|
export function isSubmitterRole(role: Role): boolean {
|
|
return SUBMITTER_ROLES.includes(role);
|
|
}
|
|
|
|
/**
|
|
* Feature-flagged: when NEXT_PUBLIC_SUBMITTER_VIEW_ALL_ENABLED=true, submitters may
|
|
* read & export every PO (not just their own) and reach the History page. This is a
|
|
* read-only widening — it does not grant approval, payment, or edit rights.
|
|
*/
|
|
export function submitterCanViewAll(role: Role): boolean {
|
|
return SUBMITTER_VIEW_ALL_ENABLED && isSubmitterRole(role);
|
|
}
|
|
|
|
/**
|
|
* Whether a role may view/export any PO, not just the ones they submitted.
|
|
* True for `view_all_pos` holders (ACCOUNTS, MANAGER, SUPERUSER, AUDITOR, ADMIN) and,
|
|
* when the feature flag is on, for submitters too.
|
|
*/
|
|
export function canViewAllPos(role: Role): boolean {
|
|
return hasPermission(role, "view_all_pos") || submitterCanViewAll(role);
|
|
}
|