- login.html: página de acceso con diseño Quiet Wealth (sin popup del browser) - api/login.php: endpoint POST para iniciar sesión, GET ?action=logout para cerrarla - auth.php: valida sesión PHP ($_SESSION) en lugar de HTTP Basic Auth - ui.js: apiFetch() intercepta 401 y redirige a login.html automáticamente - Todos los fetch() en dashboard, add y historial migrados a apiFetch() - Eliminado docker/htpasswd y auth_basic de NGINX Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
129 lines
5.8 KiB
HTML
129 lines
5.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="utf-8"/>
|
|
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
|
<title>Quiet Wealth — Acceso</title>
|
|
<script src="js/tailwind.cdn.js"></script>
|
|
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet"/>
|
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
|
<script id="tailwind-config">
|
|
tailwind.config = {
|
|
darkMode: "class",
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
"background": "#f8faf9",
|
|
"surface-container-lowest": "#ffffff",
|
|
"surface-container-low": "#f2f4f3",
|
|
"surface-container": "#eceeed",
|
|
"on-surface": "#191c1c",
|
|
"on-surface-variant": "#3e4948",
|
|
"primary": "#005050",
|
|
"primary-container": "#006a6a",
|
|
"on-primary": "#ffffff",
|
|
"error": "#ba1a1a",
|
|
"outline": "#6e7979",
|
|
"outline-variant": "#bec9c8",
|
|
},
|
|
fontFamily: { headline: ["Manrope"], body: ["Manrope"] }
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style>
|
|
body { font-family: 'Manrope', sans-serif; }
|
|
.material-symbols-outlined { font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24; }
|
|
</style>
|
|
</head>
|
|
<body class="bg-background min-h-screen flex items-center justify-center px-6">
|
|
|
|
<div class="w-full max-w-sm space-y-8">
|
|
<!-- Logo / título -->
|
|
<div class="text-center space-y-3">
|
|
<div class="inline-flex items-center justify-center w-16 h-16 rounded-[1.5rem] bg-gradient-to-br from-primary to-primary-container shadow-lg shadow-primary/20">
|
|
<span class="material-symbols-outlined text-white text-3xl" style="font-variation-settings:'FILL' 1;">account_balance_wallet</span>
|
|
</div>
|
|
<div>
|
|
<h1 class="text-2xl font-extrabold text-primary tracking-tight">Quiet Wealth</h1>
|
|
<p class="text-sm text-on-surface-variant mt-1">Ingresá para continuar</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Formulario -->
|
|
<div class="bg-surface-container-lowest rounded-[2rem] p-7 shadow-sm space-y-5">
|
|
<div class="space-y-2">
|
|
<label class="text-[11px] uppercase tracking-widest font-bold text-on-surface-variant">Usuario</label>
|
|
<div class="flex items-center gap-3 bg-surface-container-low px-4 py-3.5 rounded-xl">
|
|
<span class="material-symbols-outlined text-outline text-[20px]">person</span>
|
|
<input id="inp-user" type="text" autocomplete="username"
|
|
class="bg-transparent text-sm font-semibold w-full focus:outline-none text-on-surface placeholder-outline"
|
|
placeholder="Usuario"/>
|
|
</div>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<label class="text-[11px] uppercase tracking-widest font-bold text-on-surface-variant">Contraseña</label>
|
|
<div class="flex items-center gap-3 bg-surface-container-low px-4 py-3.5 rounded-xl">
|
|
<span class="material-symbols-outlined text-outline text-[20px]">lock</span>
|
|
<input id="inp-pass" type="password" autocomplete="current-password"
|
|
class="bg-transparent text-sm font-semibold w-full focus:outline-none text-on-surface placeholder-outline"
|
|
placeholder="Contraseña"/>
|
|
</div>
|
|
</div>
|
|
|
|
<p id="ui-error" class="hidden text-sm text-error font-semibold text-center pt-1"></p>
|
|
|
|
<button id="btn-login"
|
|
class="w-full h-14 bg-gradient-to-br from-primary to-primary-container text-white rounded-xl font-bold text-base shadow-lg shadow-primary/20 active:scale-[0.98] transition-transform flex items-center justify-center gap-2 mt-2">
|
|
<span>Ingresar</span>
|
|
<span class="material-symbols-outlined text-[20px]">arrow_forward</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const btnLogin = document.getElementById('btn-login');
|
|
const inpUser = document.getElementById('inp-user');
|
|
const inpPass = document.getElementById('inp-pass');
|
|
const uiError = document.getElementById('ui-error');
|
|
|
|
async function login() {
|
|
uiError.classList.add('hidden');
|
|
btnLogin.disabled = true;
|
|
btnLogin.innerHTML = '<span>Verificando...</span>';
|
|
|
|
try {
|
|
const r = await fetch('api/login.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ user: inpUser.value, pass: inpPass.value })
|
|
});
|
|
const data = await r.json();
|
|
|
|
if (data.status === 'success') {
|
|
window.location.href = 'index.html';
|
|
} else {
|
|
uiError.textContent = data.message;
|
|
uiError.classList.remove('hidden');
|
|
btnLogin.disabled = false;
|
|
btnLogin.innerHTML = '<span>Ingresar</span><span class="material-symbols-outlined text-[20px]">arrow_forward</span>';
|
|
}
|
|
} catch {
|
|
uiError.textContent = 'Error de conexión con el servidor.';
|
|
uiError.classList.remove('hidden');
|
|
btnLogin.disabled = false;
|
|
btnLogin.innerHTML = '<span>Ingresar</span><span class="material-symbols-outlined text-[20px]">arrow_forward</span>';
|
|
}
|
|
}
|
|
|
|
btnLogin.addEventListener('click', login);
|
|
document.addEventListener('keydown', (e) => { if (e.key === 'Enter') login(); });
|
|
|
|
// Si ya está autenticado, redirigir directo
|
|
fetch('api/login.php').then(r => {
|
|
if (r.status === 200) window.location.href = 'index.html';
|
|
}).catch(() => {});
|
|
</script>
|
|
</body>
|
|
</html>
|