Files
web/index.html
2026-02-07 21:25:36 +00:00

209 lines
8.8 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IntegraRepara | Acceso</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</head>
<body class="bg-gray-100 font-sans">
<div id="app" class="min-h-screen flex items-center justify-center p-4">
</div>
<script>
// --- ESTADO ---
const API_URL = ""; // Dejar vacío si está en el mismo dominio, o poner http://localhost:3000
let state = {
token: localStorage.getItem('token') || null,
tempPhone: null // Para recordar el telf al verificar
};
// --- VISTAS ---
const renderLogin = () => `
<div class="bg-white p-8 rounded-xl shadow-lg w-full max-w-md">
<h2 class="text-2xl font-bold mb-6 text-center text-blue-600">Iniciar Sesión</h2>
<form onsubmit="handleLogin(event)" class="space-y-4">
<input type="email" id="loginEmail" placeholder="Email" class="w-full p-3 border rounded" required>
<input type="password" id="loginPass" placeholder="Contraseña" class="w-full p-3 border rounded" required>
<button type="submit" class="w-full bg-blue-600 text-white p-3 rounded hover:bg-blue-700 font-bold">Entrar</button>
</form>
<p class="mt-4 text-center text-sm">
¿No tienes cuenta? <a href="#" onclick="navigate('register')" class="text-blue-500 hover:underline">Regístrate aquí</a>
</p>
</div>
`;
const renderRegister = () => `
<div class="bg-white p-8 rounded-xl shadow-lg w-full max-w-md">
<h2 class="text-2xl font-bold mb-6 text-center text-green-600">Registro Nuevo</h2>
<form onsubmit="handleRegister(event)" class="space-y-3">
<input type="text" id="regName" placeholder="Nombre Completo" class="w-full p-3 border rounded" required>
<input type="text" id="regAddress" placeholder="Dirección" class="w-full p-3 border rounded" required>
<input type="text" id="regDni" placeholder="DNI / CIF" class="w-full p-3 border rounded" required>
<input type="tel" id="regPhone" placeholder="Móvil (WhatsApp)" class="w-full p-3 border rounded" required>
<input type="email" id="regEmail" placeholder="Email" class="w-full p-3 border rounded" required>
<input type="password" id="regPass" placeholder="Contraseña" class="w-full p-3 border rounded" required>
<button type="submit" class="w-full bg-green-600 text-white p-3 rounded hover:bg-green-700 font-bold">Crear Cuenta</button>
</form>
<p class="mt-4 text-center text-sm">
¿Ya tienes cuenta? <a href="#" onclick="navigate('login')" class="text-blue-500 hover:underline">Inicia Sesión</a>
</p>
</div>
`;
const renderVerify = () => `
<div class="bg-white p-8 rounded-xl shadow-lg w-full max-w-md text-center">
<h2 class="text-2xl font-bold mb-2 text-purple-600">Verifica tu WhatsApp</h2>
<p class="mb-6 text-gray-600">Hemos enviado un código a <b>${state.tempPhone}</b></p>
<form onsubmit="handleVerify(event)" class="space-y-4">
<input type="text" id="verifyCode" placeholder="123456" class="w-full p-4 text-center text-2xl tracking-widest border rounded" maxlength="6" required>
<button type="submit" class="w-full bg-purple-600 text-white p-3 rounded hover:bg-purple-700 font-bold">Verificar</button>
</form>
<button onclick="navigate('register')" class="mt-4 text-sm text-gray-400 underline">Volver / Cambiar número</button>
</div>
`;
const renderDashboard = async () => {
// Cargar datos
const res = await fetchWithAuth('/services');
const services = res.ok ? res.data.services : [];
return `
<div class="w-full max-w-4xl">
<div class="bg-white p-6 rounded-xl shadow-lg mb-6 flex justify-between items-center">
<h1 class="text-2xl font-bold">Panel de Control</h1>
<button onclick="logout()" class="text-red-500 border border-red-500 px-4 py-2 rounded hover:bg-red-50">Cerrar Sesión</button>
</div>
<div class="bg-white p-6 rounded-xl shadow-lg">
<h3 class="font-bold text-lg mb-4">Mis Servicios</h3>
${services.length === 0 ? '<p class="text-gray-500">No hay servicios registrados.</p>' : ''}
<ul class="space-y-2">
${services.map(s => `
<li class="p-4 border rounded hover:bg-gray-50 flex justify-between">
<span class="font-bold">${s.title}</span>
<span class="text-sm text-gray-500">${new Date(s.created_at).toLocaleDateString()}</span>
</li>
`).join('')}
</ul>
</div>
</div>
`;
};
// --- LÓGICA ---
async function navigate(view) {
const app = document.getElementById('app');
app.innerHTML = '<div class="animate-pulse">Cargando...</div>';
if (view === 'login') app.innerHTML = renderLogin();
else if (view === 'register') app.innerHTML = renderRegister();
else if (view === 'verify') app.innerHTML = renderVerify();
else if (view === 'dashboard') {
if (!state.token) return navigate('login');
app.innerHTML = await renderDashboard();
}
}
// API Helpers
async function apiCall(endpoint, method, body) {
try {
const r = await fetch(API_URL + endpoint, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
return await r.json();
} catch (e) {
return { ok: false, error: "Error de conexión" };
}
}
async function fetchWithAuth(endpoint) {
try {
const r = await fetch(API_URL + endpoint, {
headers: { 'Authorization': `Bearer ${state.token}` }
});
const data = await r.json();
return { ok: r.ok, data };
} catch(e) { return { ok: false }; }
}
// Handlers
async function handleRegister(e) {
e.preventDefault();
const data = {
fullName: document.getElementById('regName').value,
address: document.getElementById('regAddress').value,
dni: document.getElementById('regDni').value,
phone: document.getElementById('regPhone').value,
email: document.getElementById('regEmail').value,
password: document.getElementById('regPass').value,
};
Swal.fire({ title: 'Registrando...', didOpen: () => Swal.showLoading() });
const res = await apiCall('/auth/register', 'POST', data);
Swal.close();
if (res.ok) {
state.tempPhone = res.phone; // Guardamos el telf normalizado
navigate('verify');
Swal.fire('¡Código Enviado!', 'Revisa tu WhatsApp', 'success');
} else {
Swal.fire('Error', res.error, 'error');
}
}
async function handleVerify(e) {
e.preventDefault();
const code = document.getElementById('verifyCode').value;
Swal.fire({ title: 'Verificando...', didOpen: () => Swal.showLoading() });
const res = await apiCall('/auth/verify', 'POST', { phone: state.tempPhone, code });
Swal.close();
if (res.ok) {
localStorage.setItem('token', res.token);
state.token = res.token;
navigate('dashboard');
Swal.fire('¡Éxito!', 'Bienvenido a la plataforma', 'success');
} else {
Swal.fire('Error', res.error, 'error');
}
}
async function handleLogin(e) {
e.preventDefault();
const email = document.getElementById('loginEmail').value;
const password = document.getElementById('loginPass').value;
Swal.fire({ title: 'Entrando...', didOpen: () => Swal.showLoading() });
const res = await apiCall('/auth/login', 'POST', { email, password });
Swal.close();
if (res.ok) {
localStorage.setItem('token', res.token);
state.token = res.token;
navigate('dashboard');
} else {
Swal.fire('Error', res.error, 'error');
}
}
function logout() {
localStorage.removeItem('token');
state.token = null;
navigate('login');
}
// Init
if (state.token) navigate('dashboard');
else navigate('login');
</script>
</body>
</html>