327 lines
12 KiB
JavaScript
327 lines
12 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { User, Lock, LogIn, Database, ShieldCheck, Loader2, AlertCircle } from 'lucide-react';
|
|
import { authService } from '../services/api';
|
|
|
|
export default function Login({ onLoginSuccess }) {
|
|
const [username, setUsername] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState(null);
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await authService.login(username, password);
|
|
|
|
// Assuming the API returns { success: true, token: '...', user: { ... } }
|
|
if (response.success) {
|
|
localStorage.setItem('auth_token', response.token);
|
|
localStorage.setItem('user_data', JSON.stringify(response.user));
|
|
onLoginSuccess(response.user);
|
|
} else {
|
|
setError(response.message || 'Credenciales inválidas');
|
|
}
|
|
} catch (err) {
|
|
console.error('Login error:', err);
|
|
setError(err.message || 'Error al conectar con el servidor');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="login-page">
|
|
<div className="login-card">
|
|
<div className="login-header">
|
|
<div className="login-logo">
|
|
<Database size={32} className="logo-icon" />
|
|
</div>
|
|
<h1>ADVICOM SQL Manager</h1>
|
|
<p>Bienvenido. Ingrese sus credenciales para continuar.</p>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="login-form">
|
|
{error && (
|
|
<div className="error-alert">
|
|
<AlertCircle size={18} />
|
|
<span>{error}</span>
|
|
</div>
|
|
)}
|
|
|
|
<div className="input-group">
|
|
<label htmlFor="username">Usuario</label>
|
|
<div className="input-wrapper">
|
|
<User className="input-icon" size={18} />
|
|
<input
|
|
id="username"
|
|
type="text"
|
|
placeholder="ej: admin"
|
|
value={username}
|
|
onChange={(e) => setUsername(e.target.value)}
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="input-group">
|
|
<label htmlFor="password">Contraseña</label>
|
|
<div className="input-wrapper">
|
|
<Lock className="input-icon" size={18} />
|
|
<input
|
|
id="password"
|
|
type="password"
|
|
placeholder="••••••••"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
className="login-btn"
|
|
disabled={loading}
|
|
>
|
|
{loading ? (
|
|
<Loader2 className="animate-spin" size={20} />
|
|
) : (
|
|
<>
|
|
<LogIn size={20} />
|
|
<span>Iniciar Sesión</span>
|
|
</>
|
|
)}
|
|
</button>
|
|
</form>
|
|
|
|
<div className="login-footer">
|
|
<div className="secure-badge">
|
|
<ShieldCheck size={14} />
|
|
<span>Conexión segura SSL</span>
|
|
</div>
|
|
<p>© 2026 Advicom Group • v1.0.0</p>
|
|
</div>
|
|
</div>
|
|
|
|
<style>{`
|
|
.login-page {
|
|
height: 100vh;
|
|
width: 100vw;
|
|
background: radial-gradient(circle at top right, #1e293b, #0f172a);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 1rem;
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
.login-page::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -10%;
|
|
right: -10%;
|
|
width: 40%;
|
|
height: 40%;
|
|
background: radial-gradient(circle, rgba(59, 130, 246, 0.1) 0%, transparent 70%);
|
|
z-index: 0;
|
|
}
|
|
|
|
.login-page::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: -10%;
|
|
left: -10%;
|
|
width: 30%;
|
|
height: 30%;
|
|
background: radial-gradient(circle, rgba(59, 130, 246, 0.05) 0%, transparent 70%);
|
|
z-index: 0;
|
|
}
|
|
|
|
.login-card {
|
|
width: 100%;
|
|
max-width: 420px;
|
|
background: rgba(30, 41, 59, 0.7);
|
|
backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
border-radius: 1.5rem;
|
|
padding: 2.5rem;
|
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
|
z-index: 1;
|
|
animation: slideUp 0.6s ease-out;
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from { transform: translateY(20px); opacity: 0; }
|
|
to { transform: translateY(0); opacity: 1; }
|
|
}
|
|
|
|
.login-header {
|
|
text-align: center;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.login-logo {
|
|
width: 64px;
|
|
height: 64px;
|
|
background: linear-gradient(135deg, var(--accent), var(--accent-hover));
|
|
border-radius: 1.25rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto 1.5rem;
|
|
box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.5);
|
|
}
|
|
|
|
.logo-icon {
|
|
color: white;
|
|
}
|
|
|
|
.login-header h1 {
|
|
font-size: 1.75rem;
|
|
font-weight: 800;
|
|
color: white;
|
|
margin: 0;
|
|
letter-spacing: -0.025em;
|
|
}
|
|
|
|
.login-header p {
|
|
color: var(--text-secondary);
|
|
font-size: 0.875rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.login-form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.25rem;
|
|
}
|
|
|
|
.error-alert {
|
|
background: rgba(239, 68, 68, 0.1);
|
|
border: 1px solid rgba(239, 68, 68, 0.2);
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 0.75rem;
|
|
color: #fca5a5;
|
|
font-size: 0.8125rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.input-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.input-group label {
|
|
font-size: 0.8125rem;
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
margin-left: 0.25rem;
|
|
}
|
|
|
|
.input-wrapper {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.input-icon {
|
|
position: absolute;
|
|
left: 1rem;
|
|
color: var(--text-muted);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.input-wrapper input {
|
|
width: 100%;
|
|
background: var(--bg-input);
|
|
border: 1px solid var(--border);
|
|
color: white;
|
|
padding: 0.875rem 1rem 0.875rem 2.875rem;
|
|
border-radius: 1rem;
|
|
font-size: 0.875rem;
|
|
transition: all 0.2s;
|
|
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.input-wrapper input:focus {
|
|
outline: none;
|
|
border-color: var(--accent);
|
|
background: rgba(2, 6, 23, 0.8);
|
|
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.15);
|
|
}
|
|
|
|
.login-btn {
|
|
margin-top: 1rem;
|
|
background: linear-gradient(135deg, var(--accent), var(--accent-hover));
|
|
color: white;
|
|
border: none;
|
|
padding: 0.875rem;
|
|
border-radius: 1rem;
|
|
font-weight: 700;
|
|
font-size: 1rem;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.75rem;
|
|
transition: all 0.3s;
|
|
box-shadow: 0 10px 15px -3px rgba(59, 130, 246, 0.3);
|
|
}
|
|
|
|
.login-btn:hover:not(:disabled) {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 20px 25px -5px rgba(59, 130, 246, 0.4);
|
|
}
|
|
|
|
.login-btn:active:not(:disabled) {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.login-btn:disabled {
|
|
opacity: 0.7;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.login-footer {
|
|
margin-top: 2rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.secure-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.375rem;
|
|
color: var(--success);
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
margin-bottom: 1rem;
|
|
padding: 0.375rem 0.75rem;
|
|
background: rgba(16, 185, 129, 0.1);
|
|
border-radius: 2rem;
|
|
}
|
|
|
|
.login-footer p {
|
|
color: var(--text-muted);
|
|
font-size: 0.75rem;
|
|
margin: 0;
|
|
}
|
|
|
|
.animate-spin {
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
@keyframes spin {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
}
|