frontend phase 15

This commit is contained in:
hamid
2026-07-10 20:28:06 +03:30
parent bc51cf59b4
commit 70cf00ce4a
151 changed files with 10711 additions and 44 deletions
+71
View File
@@ -0,0 +1,71 @@
import { useMemo } from 'react';
import { useAuth } from '@/context/auth';
import { APP_ROLES, ADMIN_ROLE_CODES, isAdminRoleCode, type AdminRoleCode } from '@/constants';
/**
* The set of admin consoles the current principal may act on. **UI hint only** — the server enforces
* every command's role scope (a `support` admin who forges a payout request still 403s). We derive it so
* the shell never *shows* a control the current role can't use (phase §3 "Routing & RBAC", §5).
*
* The matrix mirrors the product's five worklists (verification / refund / payout / support-alert / RBAC)
* plus the config/holiday/audit/moderation/partner surfaces, keyed off the fine-grained role codes:
* - `super_admin` — everything (only role that may grant/revoke roles).
* - `admin` — everything except role management.
* - `finance` — refunds, payouts, config, audit.
* - `support` — verification, support-alerts, tickets.
* - `moderation` — review moderation.
*/
export interface AdminCapabilities {
/** True when the principal holds any admin role at all (drives whether the console is reachable). */
isAdmin: boolean;
canVerify: boolean;
canRefund: boolean;
canPayout: boolean;
canModerate: boolean;
canConfig: boolean;
canManageAlerts: boolean;
canManageTickets: boolean;
canManagePartners: boolean;
canViewAudit: boolean;
canManageRoles: boolean;
/** The effective fine-grained roles used for the matrix (post dev-fallback). */
roles: AdminRoleCode[];
}
const has = (roles: AdminRoleCode[], ...allowed: AdminRoleCode[]) => roles.some((r) => allowed.includes(r));
/**
* Reads the session's fine-grained `roleCodes` (hydrated from `/me` by `useSessionRoleSync`). When a
* session is coarse-admin (mock auth, or `/me` not yet fine-hydrated) but carries no admin code, we fall
* back to a plain `admin` so the console is usable in dev — never `super_admin`, so the roles screen stays
* correctly hidden. This is a display convenience; authorization is always the server's.
*/
export function useAdminCapabilities(): AdminCapabilities {
const [state] = useAuth();
return useMemo<AdminCapabilities>(() => {
const user = state.currentUser;
const coarseAdmin = !!user?.roles?.includes(APP_ROLES.ADMIN);
let roles = (user?.roleCodes ?? []).filter(isAdminRoleCode);
if (roles.length === 0 && coarseAdmin) roles = ['admin'];
const isAdmin = roles.length > 0;
return {
isAdmin,
canVerify: has(roles, 'super_admin', 'admin', 'support'),
canRefund: has(roles, 'super_admin', 'admin', 'finance'),
canPayout: has(roles, 'super_admin', 'admin', 'finance'),
canModerate: has(roles, 'super_admin', 'admin', 'moderation'),
canConfig: has(roles, 'super_admin', 'admin', 'finance'),
canManageAlerts: has(roles, 'super_admin', 'admin', 'support'),
canManageTickets: has(roles, 'super_admin', 'admin', 'support'),
canManagePartners: has(roles, 'super_admin', 'admin'),
canViewAudit: has(roles, 'super_admin', 'admin'),
canManageRoles: has(roles, 'super_admin'),
roles,
};
}, [state.currentUser]);
}
/** The full ordered admin role-code list — for the (deferred) RBAC grid + any role picker. */
export { ADMIN_ROLE_CODES };
+1
View File
@@ -1,3 +1,4 @@
export * from './auth';
export * from './capabilities';
export * from './event';
export * from './layout';