/** * 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);