fase 2.1: roles editables (modelo skeen.role) y permisos por accion (43 claves, 9 grupos) con enforcement en backend y gating en frontend; editor de roles/permisos/menus en Configuracion
This commit is contained in:
@@ -2,13 +2,14 @@ import { createContext, useContext, useEffect, useState, useCallback } from 'rea
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import { odooApi, type FrontendUser, type FrontendRole, type AgendaPerm, type ClinicBrand, BRAND_DEFAULT } from '../services/odoo';
|
||||
import { applyBrandColors } from './brand-colors';
|
||||
import { defaultPermFor } from './permissions';
|
||||
|
||||
const TOKEN_KEY = 'skeen_token';
|
||||
const USER_KEY = 'skeen_user';
|
||||
const MENU_ROLES_KEY = 'skeen_menu_roles';
|
||||
const BRAND_KEY_LS = 'skeen_clinic_brand';
|
||||
|
||||
const ROLE_RANK: Record<FrontendRole, number> = {
|
||||
const ROLE_RANK: Record<string, number> = {
|
||||
admin: 100,
|
||||
medico: 50,
|
||||
recepcion: 50,
|
||||
@@ -78,6 +79,9 @@ interface AuthContextValue {
|
||||
hasRole: (min: FrontendRole) => boolean;
|
||||
canSee: (href: string) => boolean;
|
||||
canAgenda: (perm: AgendaPerm) => boolean;
|
||||
// Permiso por acción (catálogo skeen_whatsapp/permissions.py). La UI oculta
|
||||
// o deshabilita según esto; el backend sigue siendo la autoridad.
|
||||
can: (permKey: string) => boolean;
|
||||
// Brand de la clínica (white-label). BRAND_DEFAULT hasta que /brand responde.
|
||||
brand: ClinicBrand;
|
||||
setBrand: (b: ClinicBrand) => void;
|
||||
@@ -271,8 +275,18 @@ export const AuthProvider: FC<{ children: ReactNode }> = ({ children }) => {
|
||||
return perms.includes(perm);
|
||||
};
|
||||
|
||||
// Permisos por acción. El backend manda user.perms en /auth/me y /auth/login;
|
||||
// si no viniera (sesión cacheada previa al despliegue) se usa el espejo de
|
||||
// defaults de lib/permissions.ts. El admin siempre puede.
|
||||
const can = (permKey: string): boolean => {
|
||||
if (!user) return false;
|
||||
if (user.role === 'admin') return true;
|
||||
if (user.perms && typeof user.perms[permKey] === 'boolean') return user.perms[permKey];
|
||||
return defaultPermFor(user.role, permKey);
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, token, loading, bootError, retryBoot, login, logout, hasRole, canSee, canAgenda, brand, setBrand }}>
|
||||
<AuthContext.Provider value={{ user, token, loading, bootError, retryBoot, login, logout, hasRole, canSee, canAgenda, can, brand, setBrand }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
@@ -284,7 +298,7 @@ export const useAuth = (): AuthContextValue => {
|
||||
return ctx;
|
||||
};
|
||||
|
||||
export const ROLE_LABELS: Record<FrontendRole, string> = {
|
||||
export const ROLE_LABELS: Record<string, string> = {
|
||||
admin: 'Administrador',
|
||||
medico: 'Médico',
|
||||
recepcion: 'Recepción',
|
||||
@@ -296,3 +310,7 @@ export const ROLE_LABELS: Record<FrontendRole, string> = {
|
||||
marketing: 'Marketing',
|
||||
lectura: 'Lectura',
|
||||
};
|
||||
|
||||
/** Nombre visible de un rol: el configurado (si se conoce) o el código. */
|
||||
export const roleLabel = (role: string, name?: string): string =>
|
||||
name || ROLE_LABELS[role] || role;
|
||||
|
||||
Reference in New Issue
Block a user