209 lines
8.8 KiB
HTML
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> |