feat: enforce role section permissions and add a leads permission

Unticking a section hides it from the menu, closes its routes and blocks its writes on the server; existing roles keep their current access via migration.
This commit is contained in:
2026-10-21 13:45:16 +03:30
parent 631a3267d1
commit 7ae6a5837a
23 changed files with 246 additions and 24 deletions
+49
View File
@@ -0,0 +1,49 @@
import type { RolePermissionKey, UserRole } from '@mento/shared';
/** مسیر صفحه ← مجوز بخش؛ هم‌تراز SectionPermissionGuard در API */
const PATH_PERMISSIONS: Array<[prefix: string, key: RolePermissionKey]> = [
['/contacts', 'customers'],
['/leads', 'leads'],
['/pipeline', 'sales'],
['/deals', 'sales'],
['/sales/', 'sales'],
['/settings/pipelines', 'sales'],
['/invoices', 'billing'],
['/quotes', 'billing'],
['/settings/products', 'products'],
['/inventory', 'products'],
['/activities', 'activities'],
['/notes', 'notes'],
['/attachments', 'notes'],
['/reports', 'reports'],
['/settings/audit-logs', 'audit'],
];
export function sectionPermissionForPath(path: string): RolePermissionKey | null {
const clean = path.split(/[?#]/)[0] ?? path;
for (const [prefix, key] of PATH_PERMISSIONS) {
if (clean === prefix || clean.startsWith(prefix.endsWith('/') ? prefix : `${prefix}/`)) {
return key;
}
}
return null;
}
/** مالک و مدیر همیشه؛ بقیه با تیک همان بخش در نقششان */
export function hasSectionPermission(
role: UserRole | string | null | undefined,
permissions: readonly string[] | null | undefined,
key: RolePermissionKey,
): boolean {
if (role === 'owner' || role === 'admin') return true;
return Boolean(permissions?.includes(key));
}
export function canOpenPath(
role: UserRole | string | null | undefined,
permissions: readonly string[] | null | undefined,
path: string,
): boolean {
const key = sectionPermissionForPath(path);
return !key || hasSectionPermission(role, permissions, key);
}