Files
dyolink/backend/src/common/permissions.ts

64 lines
1.9 KiB
TypeScript

/** Tab permissions — keep in sync with prisma seed and AuthService ALL_PERMISSIONS */
export const ALL_TAB_PERMISSIONS = [
'TAB_TODAY_READ',
'TAB_TODAY_EDIT',
'TAB_STAFF_READ',
'TAB_STAFF_EDIT',
'TAB_ORGANIZATIONS_READ',
'TAB_ORGANIZATIONS_EDIT',
'TAB_PATIENTS_READ',
'TAB_PATIENTS_EDIT',
'TAB_APPOINTMENTS_READ',
'TAB_APPOINTMENTS_EDIT',
'TAB_TREATMENT_READ',
'TAB_TREATMENT_EDIT',
'TAB_CASES_READ',
'TAB_CASES_EDIT',
'TAB_BILLING_READ',
'TAB_BILLING_EDIT',
'TAB_REPORTS_READ',
'TAB_REPORTS_EDIT',
] as const;
export type TabPermission = (typeof ALL_TAB_PERMISSIONS)[number];
const ALL_TAB_SET = new Set<string>(ALL_TAB_PERMISSIONS);
const TAB_ORDER_INDEX = new Map<string, number>(
ALL_TAB_PERMISSIONS.map((p, i) => [p, i]),
);
/** Enterprise / unlimited seat plans use this sentinel in seed data */
export const SEAT_UNLIMITED_THRESHOLD = 999999;
export function isUnlimitedSeats(maxUsers: number): boolean {
return maxUsers >= SEAT_UNLIMITED_THRESHOLD;
}
/** EDIT implies READ for the same feature tab */
const EDIT_TO_READ: Record<string, string> = {
TAB_TODAY_EDIT: 'TAB_TODAY_READ',
TAB_PATIENTS_EDIT: 'TAB_PATIENTS_READ',
TAB_APPOINTMENTS_EDIT: 'TAB_APPOINTMENTS_READ',
TAB_STAFF_EDIT: 'TAB_STAFF_READ',
TAB_ORGANIZATIONS_EDIT: 'TAB_ORGANIZATIONS_READ',
TAB_TREATMENT_EDIT: 'TAB_TREATMENT_READ',
TAB_CASES_EDIT: 'TAB_CASES_READ',
TAB_BILLING_EDIT: 'TAB_BILLING_READ',
TAB_REPORTS_EDIT: 'TAB_REPORTS_READ',
};
/**
* Dedupe, drop unknown strings, and add implied READ permissions for each EDIT.
*/
export function normalizeTabPermissions(names: string[]): string[] {
const out = new Set<string>();
for (const raw of names) {
const n = typeof raw === 'string' ? raw.trim() : '';
if (!n || !ALL_TAB_SET.has(n)) continue;
out.add(n);
const read = EDIT_TO_READ[n];
if (read) out.add(read);
}
return [...out].sort((a, b) => (TAB_ORDER_INDEX.get(a) ?? 0) - (TAB_ORDER_INDEX.get(b) ?? 0));
}