246 lines
13 KiB
HTML
246 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Nuevo Servicio Disponible - IntegraRepara</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
<style>
|
|
.btn-animate { transition: all 0.2s; }
|
|
.btn-animate:active { transform: scale(0.95); }
|
|
</style>
|
|
</head>
|
|
<body class="bg-slate-50 text-slate-800 font-sans antialiased">
|
|
|
|
<div class="max-w-md mx-auto min-h-screen flex flex-col p-4">
|
|
|
|
<div id="top-header" class="text-center py-6">
|
|
<div class="bg-blue-600 w-16 h-16 rounded-2xl flex items-center justify-center mx-auto shadow-lg shadow-blue-200 mb-4">
|
|
<i data-lucide="briefcase" class="text-white w-8 h-8"></i>
|
|
</div>
|
|
<h1 class="text-xl font-black uppercase tracking-tight text-slate-900">Servicio Disponible</h1>
|
|
<p id="timer" class="text-sm font-bold text-red-500 mt-1">Cargando datos...</p>
|
|
</div>
|
|
|
|
<div id="content" class="hidden space-y-4">
|
|
<div class="bg-white rounded-3xl p-6 shadow-sm border border-slate-100 space-y-6">
|
|
|
|
<div class="flex justify-between items-start border-b border-slate-50 pb-4">
|
|
<div>
|
|
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest">Gremio</p>
|
|
<p id="guild" class="font-black text-blue-600 uppercase text-lg leading-tight">--</p>
|
|
</div>
|
|
<div class="text-right shrink-0 ml-4">
|
|
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest">Referencia</p>
|
|
<p id="ref" class="font-bold text-slate-700 text-sm">--</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-5">
|
|
<div class="flex items-start gap-3">
|
|
<div class="bg-indigo-50 p-2.5 rounded-xl text-indigo-500 shrink-0">
|
|
<i data-lucide="map-pin" class="w-5 h-5"></i>
|
|
</div>
|
|
<div class="min-w-0">
|
|
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-0.5">Ubicación / CP</p>
|
|
<p id="location-street" class="font-bold text-slate-800 uppercase text-sm truncate">--</p>
|
|
<p id="location-city" class="text-xs text-slate-500 font-medium mt-0.5">--</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-start gap-3">
|
|
<div class="bg-rose-50 p-2.5 rounded-xl text-rose-500 shrink-0">
|
|
<i data-lucide="sparkles" class="w-5 h-5"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-0.5">Resumen Inteligente</p>
|
|
<p id="desc" class="text-sm text-slate-700 leading-snug font-medium">--</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-4 pt-4">
|
|
<button onclick="respond('reject')" class="btn-animate bg-white border-2 border-slate-200 text-slate-500 font-black py-4 rounded-2xl uppercase text-xs tracking-widest hover:bg-slate-50 shadow-sm">
|
|
Rechazar
|
|
</button>
|
|
<button onclick="respond('accept')" class="btn-animate bg-emerald-600 text-white font-black py-4 rounded-2xl uppercase text-xs tracking-widest shadow-lg shadow-emerald-200 hover:bg-emerald-700">
|
|
Aceptar Trabajo
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="error-screen" class="hidden text-center py-20 px-6">
|
|
<i data-lucide="clock-alert" class="w-20 h-20 text-slate-300 mx-auto mb-6"></i>
|
|
<h2 class="text-2xl font-black text-slate-800 uppercase">¡Lo sentimos!</h2>
|
|
<p id="error-msg" class="text-slate-500 text-base mt-3 font-medium">Este enlace ha caducado o el servicio ya ha sido asignado a otro compañero.</p>
|
|
<button onclick="window.close()" class="mt-10 bg-slate-100 px-6 py-3 rounded-full text-slate-600 font-bold uppercase text-xs tracking-widest active:scale-95 transition-transform">Cerrar ventana</button>
|
|
</div>
|
|
|
|
<div id="loading" class="flex-1 flex items-center justify-center">
|
|
<div class="text-center">
|
|
<i data-lucide="loader-2" class="w-8 h-8 animate-spin text-blue-600 mx-auto mb-3"></i>
|
|
<p class="text-xs font-bold text-slate-400 uppercase tracking-widest animate-pulse">Obteniendo datos...</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
const API_URL = "https://integrarepara-api.integrarepara.es";
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const token = urlParams.get('t');
|
|
|
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
if (!token) {
|
|
showError("Token de acceso no válido");
|
|
return;
|
|
}
|
|
await loadAssignmentData();
|
|
lucide.createIcons();
|
|
});
|
|
|
|
// ==========================================
|
|
// 🧠 LÓGICA DE RESUMEN INTELIGENTE TIPO "IA"
|
|
// ==========================================
|
|
function smartSummarize(text) {
|
|
if (!text || text.trim() === "") return "Se requiere asistencia técnica en el domicilio.";
|
|
|
|
// 1. Limpiamos basura, dobles espacios y saltos de línea
|
|
let clean = text.replace(/(\r\n|\n|\r)/gm, " ").replace(/\s+/g, " ").trim();
|
|
|
|
// 2. Cortamos el texto justo antes de que empiecen las notas administrativas (teléfonos, pólizas, asegurados)
|
|
const cutPattern = /\b(mo\b|m\.o\b|material:|asegurado:|telefono:|teléfono:|urgente:|observaciones:|franquicia:|poliza:|tramitador:)/i;
|
|
const cutIndex = clean.search(cutPattern);
|
|
if (cutIndex > 10) {
|
|
clean = clean.substring(0, cutIndex).trim();
|
|
}
|
|
|
|
// 3. Le damos formato Humano (Primera letra mayúscula, el resto normal para que sea legible)
|
|
clean = clean.charAt(0).toUpperCase() + clean.slice(1).toLowerCase();
|
|
|
|
// 4. Extracción de frases clave: Separamos por puntos y cogemos máximo 2 frases
|
|
let sentences = clean.match(/[^.!?]+[.!?]+/g);
|
|
if (!sentences) {
|
|
// Si no había puntos, cortamos elegantemente a los 130 caracteres sin partir palabras
|
|
if (clean.length > 130) {
|
|
let cutText = clean.substring(0, 130);
|
|
return cutText.substring(0, cutText.lastIndexOf(" ")) + "...";
|
|
}
|
|
return clean + (clean.endsWith('.') ? '' : '.');
|
|
}
|
|
|
|
if (sentences.length >= 2) {
|
|
return sentences.slice(0, 2).join(' ').trim() + " ...";
|
|
}
|
|
|
|
return sentences[0].trim();
|
|
}
|
|
|
|
async function loadAssignmentData() {
|
|
try {
|
|
const res = await fetch(`${API_URL}/public/assignment/${token}`);
|
|
const data = await res.json();
|
|
|
|
if (data.ok) {
|
|
const raw = data.service;
|
|
|
|
// 1. GREMIO Y REFERENCIA
|
|
document.getElementById('guild').innerText = raw["Gremio"] || "Servicio General";
|
|
document.getElementById('ref').innerText = raw["Expediente"] || "Sin Ref";
|
|
|
|
// 2. PROCESADO DE DIRECCIÓN ESTRICTO (Corta en el primer número)
|
|
const fullAddress = raw["Dirección"] || raw["DOMICILIO"] || "";
|
|
let streetOnly = fullAddress;
|
|
|
|
// Buscamos dónde empieza el número de la calle
|
|
const numberIndex = fullAddress.search(/\d/);
|
|
if (numberIndex > 3) { // Si el número está después de la 3ra letra (ej: Calle 2)
|
|
streetOnly = fullAddress.substring(0, numberIndex).replace(/[,/-]+$/, '').trim();
|
|
} else if (numberIndex === -1) {
|
|
// Si es una dirección rara sin números, la dejamos tal cual pero la limpiamos
|
|
streetOnly = fullAddress.trim();
|
|
}
|
|
|
|
// Si nos hemos cargado la calle por error, devolvemos un texto seguro
|
|
if(!streetOnly || streetOnly.length < 3) streetOnly = "Calle Oculta";
|
|
|
|
const city = raw["Población"] || "Localidad sin especificar";
|
|
const cp = raw["Código Postal"] || "---";
|
|
|
|
document.getElementById('location-street').innerText = streetOnly;
|
|
document.getElementById('location-city').innerText = `${city} (CP: ${cp})`;
|
|
|
|
// 3. RESUMEN INTELIGENTE
|
|
document.getElementById('desc').innerText = smartSummarize(raw["Descripción"]);
|
|
|
|
// TEMPORIZADOR
|
|
if (data.debug) {
|
|
const limitDate = new Date(data.debug.hora_limite_bd);
|
|
const timeStr = limitDate.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' });
|
|
|
|
document.getElementById('timer').innerHTML = `
|
|
Responde antes de que caduque el turno<br>
|
|
<span class="text-xs text-slate-500 font-medium mt-2 block">Límite hasta las: <strong class="text-slate-800">${timeStr}</strong></span>
|
|
`;
|
|
} else {
|
|
document.getElementById('timer').innerText = "Responde antes de que caduque el turno";
|
|
}
|
|
|
|
document.getElementById('loading').classList.add('hidden');
|
|
document.getElementById('content').classList.remove('hidden');
|
|
} else {
|
|
showError(data.error);
|
|
}
|
|
} catch (e) {
|
|
showError("Error al conectar con el servidor");
|
|
}
|
|
}
|
|
|
|
async function respond(action) {
|
|
if(!confirm(`¿Seguro que quieres ${action === 'accept' ? 'ACEPTAR' : 'RECHAZAR'} este servicio?`)) return;
|
|
|
|
try {
|
|
document.getElementById('top-header').classList.add('hidden');
|
|
document.getElementById('content').classList.add('hidden');
|
|
|
|
document.getElementById('loading').innerHTML = `<div class="text-center mt-20"><i data-lucide="loader-2" class="w-10 h-10 animate-spin text-blue-600 mx-auto mb-3"></i><p class="text-xs font-bold text-slate-400 uppercase tracking-widest animate-pulse">Procesando...</p></div>`;
|
|
document.getElementById('loading').classList.remove('hidden');
|
|
lucide.createIcons();
|
|
|
|
const res = await fetch(`${API_URL}/public/assignment/respond`, {
|
|
method: 'POST',
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ token, action })
|
|
});
|
|
const data = await res.json();
|
|
|
|
if (data.ok) {
|
|
document.getElementById('loading').innerHTML = `
|
|
<div class="text-center fade-in mt-20">
|
|
<i data-lucide="${action === 'accept' ? 'check-circle-2' : 'x-circle'}" class="w-24 h-24 ${action === 'accept' ? 'text-emerald-500' : 'text-slate-300'} mx-auto mb-6"></i>
|
|
<h2 class="text-3xl font-black uppercase text-slate-800 tracking-tight">${action === 'accept' ? '¡Asignado!' : 'Rechazado'}</h2>
|
|
<p class="text-slate-500 text-base mt-4">${action === 'accept' ? 'Ya puedes ver todos los datos del cliente en tu aplicación.' : 'Gracias por avisar. Turno liberado para otro compañero.'}</p>
|
|
</div>
|
|
`;
|
|
lucide.createIcons();
|
|
} else {
|
|
showError(data.error);
|
|
}
|
|
} catch (e) {
|
|
showError("Hubo un problema al procesar tu respuesta.");
|
|
}
|
|
}
|
|
|
|
function showError(msg) {
|
|
document.getElementById('top-header').classList.add('hidden');
|
|
document.getElementById('loading').classList.add('hidden');
|
|
document.getElementById('content').classList.add('hidden');
|
|
document.getElementById('error-screen').classList.remove('hidden');
|
|
if(msg) document.getElementById('error-msg').innerText = msg;
|
|
lucide.createIcons();
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |