white-label: brand de la clinica por configuracion (fase 1 de instancia sin branding SKEEN)

- backend: GET/PUT /skeen/frontend/v1/brand leyendo ir.config_parameter
  skeen.clinic.brand (JSON: nombre, logo_claro, logo_oscuro, pie) con
  defaults SKEEN; GET publico (el login lo necesita sin sesion), PUT admin
  con auditoria. frontend_users.xml: 'Administrador SKEEN' -> 'Administrador'
- frontend: ClinicBrand + BRAND_DEFAULT en odoo.ts; brand y setBrand en el
  contexto de auth (fetch al boot + cache localStorage + document.title)
- Login/layouts/mensajes/exports leen el brand en vez de strings SKEEN:
  Login.tsx, SkeenLayout, HomeNestLayout, Cumpleanos (wa + export),
  Pacientes/Ventas/Cortes/Reportes/Kpis/Alertas/AgendaPrint (titulos),
  useClinicaPrint (fallback nombre), Login hace GET publico al montar
- Configuracion > Clinica: tarjeta 'Identidad de la clinica' (solo admin)
  para editar nombre/pie/logos con preview
- no renombrado (invisible al usuario): rutas API /skeen/*, claves
  localStorage, nombres de modulos, parametros skeen.*

Verificacion API: 11/11 tests OK (defaults, PUT merge, campo invalido
ignorado, restore, admin neutral). Deploy: build + rsync + Odoo reiniciado
This commit is contained in:
2026-10-08 00:09:00 +00:00
parent 2573562218
commit 2f7cbe237e
18 changed files with 302 additions and 48 deletions

View File

@@ -898,6 +898,22 @@ export const AGENDA_PERM_ITEMS: { key: AgendaPerm; label: string; nota: string }
{ key: 'visitas', label: 'Generar visitas', nota: 'Check-in (En cabina) y todo el expediente del día.' },
];
// Brand de la clínica (white-label). El backend resuelve herencia: si la
// clínica no configuró un campo, devuelve el default de SKEEN.
export interface ClinicBrand {
nombre: string;
logo_claro: string; // fondos oscuros (login SKEEN, sidebar)
logo_oscuro: string; // fondos claros (tema HomeNest)
pie: string; // línea del pie (login / documentos)
}
export const BRAND_DEFAULT: ClinicBrand = {
nombre: 'SKEEN Derma Experts',
logo_claro: '/skeen-brand/logos/Logo%20Completo%20Blanco.png',
logo_oscuro: '/skeen-brand/logos/Logo%20Completo%20Negro.png',
pie: 'SKEEN Derma Experts · Playas de Rosarito, B.C.',
};
export interface FrontendUser {
id: number;
login: string;
@@ -1338,6 +1354,17 @@ export const odooApi = {
return data;
},
// Brand de la clínica (white-label): nombre, logos y pie desde Config.
async getBrand(): Promise<{ status: string; brand: ClinicBrand; message?: string }> {
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/brand`);
return data;
},
async putBrand(payload: { brand: Partial<ClinicBrand>; motivo?: string }): Promise<{ status: string; brand: ClinicBrand; message?: string }> {
const { data } = await axios.put(`${ODOO_BASE}/skeen/frontend/v1/brand`, payload);
return data;
},
async updateRolePermissions(matrix: Record<string, FrontendRole[]>): Promise<{ status: string; matrix: Record<string, FrontendRole[]>; message?: string }> {
const { data } = await axios.put(`${ODOO_BASE}/skeen/frontend/v1/roles/permissions`, { matrix });
return data;