feat(config): modulo Configuraciones admin + permisos por rol

- Tablas ConfiguracionSistema (7 configs) + RolConfiguracion (6 roles)
- Endpoints: config/sistema GET/PATCH, config/roles GET/PATCH,
  config/usuarios GET/PATCH, config/reset-roles POST, config/acciones GET
- modulosDeRol() async con fallback a MATRIZ estatica
- auth/me devuelve acciones[] segun rol del usuario
- saludMonday.estaDisponible() lee modo_local_forzado de DB (cache 30s)
- Landing: vista Configuraciones con 4 tabs (Modo, Roles, Usuarios, Sistema)
- Mi-dia: boton Descartar se deshabilita si !descartar_lead
- 630 API tests + 24 landing tests + 20 mi-dia tests verdes

Seed: npm run seed:config
This commit is contained in:
2026-09-06 09:46:19 +00:00
parent 50e6eca7da
commit b2634f97ab
35 changed files with 2572 additions and 33 deletions

View File

@@ -10,6 +10,8 @@ export interface UsuarioSesion {
// Módulos del ecosistema visibles para el rol (matriz del API; presente en
// /auth/me y /auth/login desde sep-2026)
modulos?: string[]
// Acciones permitidas por el rol (solo admin ve las de todos los roles)
acciones?: string[]
}
export interface Sesion {

View File

@@ -0,0 +1,119 @@
import { API_URL, conAuth, manejar401 } from './auth'
// ─── Config Sistema ──────────────────────────────────────────────
export interface ConfigSistema {
[clave: string]: string
}
/** GET /config/sistema — todas las configuraciones del sistema */
export async function getConfigSistema(): Promise<ConfigSistema> {
const resp = await fetch(`${API_URL}/config/sistema`, conAuth())
manejar401(resp)
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
return (await resp.json()) as ConfigSistema
}
/** PATCH /config/sistema — actualiza configs (parcial) */
export async function patchConfigSistema(data: Record<string, string>): Promise<void> {
const resp = await fetch(
`${API_URL}/config/sistema`,
conAuth({
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}),
)
manejar401(resp)
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
}
// ─── Roles ───────────────────────────────────────────────────────
export interface RolConfig {
rol: string
modulos: string // JSON array string
acciones: string // JSON object string
}
/** GET /config/roles — todas las configuraciones de rol */
export async function getRolesConfig(): Promise<RolConfig[]> {
const resp = await fetch(`${API_URL}/config/roles`, conAuth())
manejar401(resp)
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
return (await resp.json()) as RolConfig[]
}
/** PATCH /config/roles/:rol — actualiza módulos y acciones de un rol */
export async function patchRolConfig(
rol: string,
data: { modulos: string[]; acciones: Record<string, boolean> },
): Promise<void> {
const resp = await fetch(
`${API_URL}/config/roles/${encodeURIComponent(rol)}`,
conAuth({
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}),
)
manejar401(resp)
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
}
/** POST /config/reset-roles — restaura valores por defecto */
export async function resetRoles(): Promise<void> {
const resp = await fetch(
`${API_URL}/config/reset-roles`,
conAuth({ method: 'POST' }),
)
manejar401(resp)
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
}
// ─── Usuarios ────────────────────────────────────────────────────
export interface UsuarioConfig {
id: string
username: string
nombre: string
rol: string
asignacion: string | null
activo: boolean
mondayUserId: number | null
}
/** GET /config/usuarios — lista de usuarios con rol/asignación/activo */
export async function getUsuariosConfig(): Promise<UsuarioConfig[]> {
const resp = await fetch(`${API_URL}/config/usuarios`, conAuth())
manejar401(resp)
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
return (await resp.json()) as UsuarioConfig[]
}
/** PATCH /config/usuarios/:id — cambia rol/asignación/activo */
export async function patchUsuarioConfig(
id: string,
data: { rol?: string; asignacion?: string | null; activo?: boolean },
): Promise<void> {
const resp = await fetch(
`${API_URL}/config/usuarios/${encodeURIComponent(id)}`,
conAuth({
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}),
)
manejar401(resp)
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
}
// ─── Acciones ────────────────────────────────────────────────────
/** GET /config/acciones — acciones disponibles para el rol del usuario actual */
export async function getAcciones(): Promise<Record<string, boolean>> {
const resp = await fetch(`${API_URL}/config/acciones`, conAuth())
manejar401(resp)
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
return (await resp.json()) as Record<string, boolean>
}