/**
* Uso típico:
*
* error: function (xhr, error, thrown) {
* manejarErrorApi(xhr, thrown, {
* onAntes: () => desactivarSpinnerOverlay(),
* onTabla: (msg) => $('#cuerpoTabla').empty().append(
* `
| ${msg} |
`
* ),
* });
* }
*
* Cargado globalmente desde `a_general/base.html`. No depende de jQuery
* para definirse; sólo necesita SweetAlert2 (o `mostrarAlerta` si la
* página la define) al momento de ejecutarse — si ninguna está disponible,
* cae a `alert()`.
*/
(function (global) {
'use strict';
const MENSAJES_POR_STATUS = {
401: {
tipo: 'warning',
titulo: 'Error de autenticación',
mensaje: 'Ocurrió un error o no tienes permiso para continuar. Pulsa “Reintentar” para volver a cargar los datos.',
confirmar: 'Reintentar',
cancelar: 'Cerrar',
reintentable: true,
},
403: {
tipo: 'error',
titulo: 'Acceso denegado',
mensaje: 'No tienes permisos para acceder a este recurso. Contacta a soporte si crees que es un error.',
confirmar: 'Cerrar',
},
429: {
tipo: 'warning',
titulo: 'Demasiadas solicitudes',
confirmar: 'Entendido',
},
};
function _parsearResponseText(xhr) {
if (!xhr.responseText) return null;
try { return JSON.parse(xhr.responseText); } catch (_) { return null; }
}
function _mensajeDeBody(xhr, fallback) {
const body = xhr.responseJSON || _parsearResponseText(xhr) || {};
return body.message || fallback;
}
function _resolverConfig(xhr) {
if (xhr.status === 0) {
return {
tipo: 'error',
titulo: 'Sin conexión',
mensaje: 'No se pudo contactar al servidor. Verifica tu conexión a internet e inténtalo de nuevo.',
confirmar: 'Cerrar',
};
}
const base = MENSAJES_POR_STATUS[xhr.status];
if (base) {
return {
...base,
mensaje: base.mensaje || _mensajeDeBody(
xhr,
'Has excedido el límite de solicitudes. Intenta nuevamente en unos momentos.'
),
};
}
if (xhr.status >= 500) {
return {
tipo: 'error',
titulo: 'Error del servidor',
mensaje: _mensajeDeBody(
xhr,
'Ocurrió un error en el servidor. Si el problema persiste, contacta a soporte técnico.'
),
confirmar: 'Cerrar',
};
}
return {
tipo: 'error',
titulo: 'Error en la solicitud',
mensaje: _mensajeDeBody(
xhr,
`Ocurrió un error al obtener la información (estado: ${xhr.status || 'desconocido'}).`
),
confirmar: 'Cerrar',
};
}
function _mostrar(cfg, conReintento) {
if (!conReintento && typeof global.mostrarAlerta === 'function') {
try {
global.mostrarAlerta(cfg.tipo, cfg.titulo, cfg.mensaje);
return Promise.resolve({ isConfirmed: false });
} catch (e) { /* cae a Swal abajo */ }
}
if (typeof global.Swal !== 'undefined') {
const swalCfg = {
icon: cfg.tipo,
title: cfg.titulo,
text: cfg.mensaje,
confirmButtonText: cfg.confirmar || 'Cerrar',
confirmButtonColor: '#d33',
allowOutsideClick: false,
allowEscapeKey: false,
};
if (conReintento) {
swalCfg.showCancelButton = true;
swalCfg.cancelButtonText = cfg.cancelar || 'Cerrar';
swalCfg.confirmButtonColor = '#3085d6';
}
return global.Swal.fire(swalCfg);
}
global.alert(`${cfg.titulo}: ${cfg.mensaje}`);
return Promise.resolve({ isConfirmed: false });
}
global.manejarErrorApi = function (xhr, thrown, opciones) {
opciones = opciones || {};
const cfg = _resolverConfig(xhr);
const conReintento = cfg.reintentable === true
&& typeof opciones.onReintentar === 'function';
if (typeof opciones.onAntes === 'function') {
try { opciones.onAntes(cfg); } catch (e) { console.error(e); }
}
if (typeof opciones.onTabla === 'function') {
try { opciones.onTabla(cfg.mensaje, cfg); } catch (e) { console.error(e); }
}
const promesa = _mostrar(cfg, conReintento);
promesa.then((resultado) => {
if (conReintento && resultado && resultado.isConfirmed) {
try { opciones.onReintentar(cfg); } catch (e) { console.error(e); }
}
if (typeof opciones.onDespues === 'function') {
try { opciones.onDespues(cfg, resultado); } catch (e) { console.error(e); }
}
});
return promesa;
};
})(window);