Role treasury keys now open the first allowed finance page, and team-member ownership limits lists, dashboard, and reports instead of the whole organization. Co-authored-by: Cursor <cursoragent@cursor.com>
104 lines
3.9 KiB
TypeScript
104 lines
3.9 KiB
TypeScript
import {
|
|
treasurySectionAllowed,
|
|
type RolePermissionKey,
|
|
type TreasurySectionKey,
|
|
type 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));
|
|
}
|
|
|
|
function financeSection(path: string): TreasurySectionKey | null {
|
|
const clean = path.split(/[?#]/)[0] ?? path;
|
|
if (!clean.startsWith('/finance')) return null;
|
|
if (clean.startsWith('/finance/payroll')) return 'treasury_payroll';
|
|
if (clean.startsWith('/finance/incomes')) return 'treasury_incomes';
|
|
if (clean.startsWith('/finance/expenses')) return 'treasury_expenses';
|
|
if (clean.startsWith('/finance/receivables')) return 'treasury_receivables';
|
|
if (clean.startsWith('/finance/accounts')) return 'treasury_accounts';
|
|
if (clean.startsWith('/finance/recurring')) return 'treasury_recurring';
|
|
if (clean.startsWith('/finance/reports')) return 'treasury_reports';
|
|
if (clean.startsWith('/finance/audit')) return 'treasury_audit';
|
|
if (clean.startsWith('/finance/categories')) return 'treasury_categories';
|
|
return 'treasury_dashboard';
|
|
}
|
|
|
|
export function canOpenFinancePath(
|
|
role: UserRole | string | null | undefined,
|
|
permissions: readonly string[] | null | undefined,
|
|
path: string,
|
|
): boolean {
|
|
const section = financeSection(path);
|
|
if (!section) return true;
|
|
return treasurySectionAllowed(role, permissions, section);
|
|
}
|
|
|
|
const FINANCE_ENTRIES: Array<{ path: string; section: TreasurySectionKey }> = [
|
|
{ path: '/finance', section: 'treasury_dashboard' },
|
|
{ path: '/finance/incomes', section: 'treasury_incomes' },
|
|
{ path: '/finance/expenses', section: 'treasury_expenses' },
|
|
{ path: '/finance/receivables', section: 'treasury_receivables' },
|
|
{ path: '/finance/accounts', section: 'treasury_accounts' },
|
|
{ path: '/finance/payroll', section: 'treasury_payroll' },
|
|
{ path: '/finance/recurring', section: 'treasury_recurring' },
|
|
{ path: '/finance/reports', section: 'treasury_reports' },
|
|
{ path: '/finance/audit', section: 'treasury_audit' },
|
|
{ path: '/finance/categories', section: 'treasury_categories' },
|
|
];
|
|
|
|
/** اولین صفحهٔ مالی که این نقش واقعاً میتواند باز کند. */
|
|
export function firstAllowedFinancePath(
|
|
role: UserRole | string | null | undefined,
|
|
permissions: readonly string[] | null | undefined,
|
|
): string | null {
|
|
for (const entry of FINANCE_ENTRIES) {
|
|
if (treasurySectionAllowed(role, permissions, entry.section)) return entry.path;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
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);
|
|
}
|