feat(ui): rediseñar pantalla de login con mascota bot de soporte animada y acceso rapido

This commit is contained in:
2026-10-20 11:41:42 -05:00
parent 5b54fe0982
commit 72ce6adc12
5 changed files with 561 additions and 38 deletions
+195
View File
@@ -54,3 +54,198 @@ main {
padding-top: 0;
}
}
/* ==============================================================================
Login Page Modern Styling & Support Bot Mascot
============================================================================== */
.login-container {
min-height: calc(100vh - 120px);
display: flex;
align-items: center;
justify-content: center;
padding: 2.5rem 1rem;
}
.login-card-wrapper {
background: #ffffff;
border-radius: 24px;
overflow: hidden;
box-shadow: 0 25px 50px -12px rgba(15, 23, 42, 0.18), 0 0 0 1px rgba(226, 232, 240, 0.8);
max-width: 980px;
width: 100%;
}
.login-hero-side {
background: linear-gradient(135deg, #070e20 0%, #0f2b6b 45%, #0284c7 100%);
color: #ffffff;
padding: 3rem 2.5rem;
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.login-hero-side::before {
content: '';
position: absolute;
top: -20%;
left: -20%;
width: 140%;
height: 140%;
background: radial-gradient(circle at 30% 30%, rgba(56, 189, 248, 0.16) 0%, transparent 60%);
pointer-events: none;
}
.bot-avatar-wrapper {
position: relative;
display: inline-block;
margin: 1.25rem 0;
text-align: center;
}
.bot-avatar-img {
width: 145px;
height: 145px;
filter: drop-shadow(0 16px 24px rgba(2, 6, 23, 0.5));
animation: floatBot 4s ease-in-out infinite;
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.bot-avatar-img:hover {
transform: scale(1.08) rotate(-3deg);
}
@keyframes floatBot {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-8px);
}
}
.bot-status-pill {
display: inline-flex;
align-items: center;
gap: 6px;
background: rgba(15, 23, 42, 0.65);
backdrop-filter: blur(8px);
border: 1px solid rgba(56, 189, 248, 0.3);
border-radius: 9999px;
padding: 0.35rem 0.85rem;
font-size: 0.78rem;
font-weight: 500;
color: #e2e8f0;
}
.bot-status-dot {
width: 8px;
height: 8px;
background-color: #10b981;
border-radius: 50%;
box-shadow: 0 0 10px #10b981;
}
.login-feature-item {
display: flex;
align-items: center;
gap: 12px;
font-size: 0.9rem;
color: #cbd5e1;
margin-bottom: 0.75rem;
}
.login-feature-icon {
width: 28px;
height: 28px;
border-radius: 8px;
background: rgba(56, 189, 248, 0.15);
border: 1px solid rgba(56, 189, 248, 0.3);
color: #38bdf8;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.8rem;
flex-shrink: 0;
}
.login-form-side {
padding: 3.5rem 3rem;
background: #ffffff;
}
.login-input-group .input-group-text {
background-color: #f8fafc;
border-color: #cbd5e1;
color: #64748b;
border-top-left-radius: 12px;
border-bottom-left-radius: 12px;
padding-left: 1rem;
padding-right: 1rem;
}
.login-input-group .form-control {
border-color: #cbd5e1;
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
padding: 0.75rem 1rem;
font-size: 0.95rem;
}
.login-input-group .form-control:focus {
border-color: #0284c7;
box-shadow: 0 0 0 3px rgba(2, 132, 199, 0.15);
}
.password-toggle-btn {
border-color: #cbd5e1;
background-color: #f8fafc;
color: #64748b;
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
cursor: pointer;
}
.password-toggle-btn:hover {
color: #0f172a;
background-color: #e2e8f0;
}
.btn-login {
background: linear-gradient(135deg, #0284c7 0%, #2563eb 100%);
border: none;
border-radius: 12px;
padding: 0.85rem;
font-weight: 600;
font-size: 1rem;
color: #ffffff;
transition: all 0.25s ease;
}
.btn-login:hover {
background: linear-gradient(135deg, #0369a1 0%, #1d4ed8 100%);
box-shadow: 0 10px 20px -5px rgba(37, 99, 235, 0.4);
transform: translateY(-1px);
color: #ffffff;
}
.demo-account-chip {
cursor: pointer;
border: 1px solid #e2e8f0;
background: #f8fafc;
border-radius: 8px;
padding: 0.4rem 0.75rem;
font-size: 0.78rem;
font-weight: 500;
color: #475569;
transition: all 0.2s ease;
user-select: none;
}
.demo-account-chip:hover {
background: #e0f2fe;
border-color: #bae6fd;
color: #0369a1;
transform: translateY(-1px);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

+165
View File
@@ -0,0 +1,165 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="100%" height="100%">
<defs>
<!-- Background Soft Glow Gradient -->
<radialGradient id="haloGlow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#38bdf8" stop-opacity="0.35" />
<stop offset="60%" stop-color="#0284c7" stop-opacity="0.1" />
<stop offset="100%" stop-color="#0284c7" stop-opacity="0" />
</radialGradient>
<!-- Bot Body Platinum Gradient -->
<linearGradient id="bodyGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="50%" stop-color="#f8fafc" />
<stop offset="100%" stop-color="#cbd5e1" />
</linearGradient>
<!-- Dark Glass Visor Screen -->
<linearGradient id="visorGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#0f172a" />
<stop offset="100%" stop-color="#1e293b" />
</linearGradient>
<!-- Cyan Glow Gradient for Eyes and Accents -->
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#38bdf8" />
<stop offset="100%" stop-color="#0284c7" />
</linearGradient>
<!-- Headset Band Gradient (Metallic Telcotronics Blue) -->
<linearGradient id="headsetGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#1e40af" />
<stop offset="50%" stop-color="#0284c7" />
<stop offset="100%" stop-color="#0369a1" />
</linearGradient>
<!-- Status LED Gradient -->
<linearGradient id="statusLed" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#4ade80" />
<stop offset="100%" stop-color="#10b981" />
</linearGradient>
<!-- Universal Drop Shadow (SVG 1.1) -->
<filter id="botShadow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceAlpha" stdDeviation="12" result="blur" />
<feOffset dx="0" dy="16" result="offset" />
<feComponentTransfer in="offset" result="shadowAlpha">
<feFuncA type="linear" slope="0.3" />
</feComponentTransfer>
<feMerge>
<feMergeNode in="shadowAlpha" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<!-- Neon Eyes Glow Filter -->
<filter id="neonGlow" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur stdDeviation="6" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<!-- Ambient Backdrop Halo -->
<circle cx="256" cy="256" r="230" fill="url(#haloGlow)" />
<g filter="url(#botShadow)">
<!-- Antenna Signal Waves -->
<path d="M 230 40 C 238 32, 274 32, 282 40" fill="none" stroke="#38bdf8" stroke-width="4" stroke-linecap="round" opacity="0.6" />
<path d="M 218 26 C 232 12, 280 12, 294 26" fill="none" stroke="#38bdf8" stroke-width="4" stroke-linecap="round" opacity="0.4" />
<!-- Antenna Stem & Glowing Ball -->
<rect x="251" y="48" width="10" height="42" rx="5" fill="url(#headsetGrad)" />
<circle cx="256" cy="46" r="14" fill="url(#cyanGrad)" filter="url(#neonGlow)" />
<circle cx="256" cy="46" r="6" fill="#ffffff" />
<!-- Bot Torso / Shoulder Collar (Base) -->
<path d="M 170 380
C 170 350, 342 350, 342 380
L 375 460
C 375 480, 137 480, 137 460
Z"
fill="url(#bodyGrad)"
stroke="#cbd5e1"
stroke-width="3" />
<!-- Chest Shield / Tie Detail -->
<path d="M 236 380 L 276 380 L 268 435 L 256 448 L 244 435 Z" fill="url(#headsetGrad)" />
<circle cx="256" cy="406" r="5" fill="#38bdf8" />
<!-- Bot Head Main Chassis -->
<rect x="116" y="86" width="280" height="250" rx="90" fill="url(#bodyGrad)" stroke="#e2e8f0" stroke-width="3" />
<!-- Head Gloss Highlight (Top Reflection) -->
<path d="M 150 110 C 200 96, 312 96, 362 110 C 375 114, 380 125, 370 135 C 330 118, 182 118, 142 135 C 132 125, 137 114, 150 110 Z" fill="#ffffff" opacity="0.65" />
<!-- Visor Outer Rim -->
<rect x="144" y="126" width="224" height="156" rx="60" fill="#090d16" stroke="url(#headsetGrad)" stroke-width="4" />
<!-- Visor Inner Glass Screen -->
<rect x="150" y="132" width="212" height="144" rx="54" fill="url(#visorGrad)" />
<!-- Screen Glass Light Reflection -->
<path d="M 160 144 C 200 136, 312 136, 352 144 C 340 162, 172 162, 160 144 Z" fill="rgba(255, 255, 255, 0.12)" />
<!-- Bot Friendly Eyes (Digital Neon Glow) -->
<g filter="url(#neonGlow)">
<!-- Left Eye: Cheerful Oval -->
<ellipse cx="206" cy="195" rx="20" ry="26" fill="url(#cyanGrad)" />
<ellipse cx="202" cy="188" rx="7" ry="9" fill="#ffffff" />
<ellipse cx="212" cy="204" rx="4" ry="4" fill="#ffffff" />
<!-- Right Eye: Cheerful Oval -->
<ellipse cx="306" cy="195" rx="20" ry="26" fill="url(#cyanGrad)" />
<ellipse cx="302" cy="188" rx="7" ry="9" fill="#ffffff" />
<ellipse cx="312" cy="204" rx="4" ry="4" fill="#ffffff" />
<!-- Digital Smile Curve -->
<path d="M 232 236 C 244 250, 268 250, 280 236" fill="none" stroke="#38bdf8" stroke-width="5" stroke-linecap="round" />
</g>
<!-- Rosy Digital Cheeks -->
<circle cx="178" cy="226" r="10" fill="#0ea5e9" opacity="0.35" />
<circle cx="334" cy="226" r="10" fill="#0ea5e9" opacity="0.35" />
<!-- Headset Over-the-Head Arch -->
<path d="M 106 200
C 106 82, 406 82, 406 200"
fill="none"
stroke="url(#headsetGrad)"
stroke-width="22"
stroke-linecap="round" />
<!-- Headset Top Cushion -->
<path d="M 180 102 C 220 86, 292 86, 332 102" fill="none" stroke="#38bdf8" stroke-width="6" stroke-linecap="round" />
<!-- Left Headphone Earpad -->
<g transform="translate(86, 175)">
<rect width="36" height="84" rx="18" fill="url(#headsetGrad)" stroke="#0284c7" stroke-width="2" />
<rect x="10" y="12" width="16" height="60" rx="8" fill="#38bdf8" opacity="0.8" />
</g>
<!-- Right Headphone Earpad -->
<g transform="translate(390, 175)">
<rect width="36" height="84" rx="18" fill="url(#headsetGrad)" stroke="#0284c7" stroke-width="2" />
<rect x="10" y="12" width="16" height="60" rx="8" fill="#38bdf8" opacity="0.8" />
</g>
<!-- Microphone Boom -->
<path d="M 408 240
C 408 340, 330 366, 285 366"
fill="none"
stroke="url(#headsetGrad)"
stroke-width="12"
stroke-linecap="round" />
<!-- Microphone Capsule -->
<rect x="246" y="354" width="44" height="24" rx="12" fill="url(#bodyGrad)" stroke="#94a3b8" stroke-width="2" />
<rect x="256" y="360" width="14" height="12" rx="3" fill="#0284c7" />
<!-- Online Audio Indicator LED (Green Active Pulse) -->
<circle cx="280" cy="366" r="5" fill="url(#statusLed)" filter="url(#neonGlow)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

+189 -26
View File
@@ -1,50 +1,213 @@
<%- include('../partials/header') %>
<% title = 'Iniciar Sesión'; %>
<% if (success_msg && success_msg.length > 0) { %>
<div class="login-container">
<div class="login-card-wrapper row g-0">
<!-- ========================================== -->
<!-- Columna Izquierda: Hero & Bot de Soporte -->
<!-- ========================================== -->
<div class="col-lg-5 login-hero-side">
<div>
<div class="d-flex align-items-center gap-2 mb-3">
<span class="badge bg-primary bg-opacity-25 text-info border border-info border-opacity-25 px-2.5 py-1.5 rounded-pill">
<i class="fas fa-shield-alt me-1"></i> Ecosistema Telcotronics
</span>
</div>
<h3 class="fw-bold text-white mb-2">Help Desk & FSM</h3>
<p class="text-white-50 small mb-0">Gestión inteligente de incidencias y visitas técnicas de campo en terreno.</p>
</div>
<!-- Mascota Bot de Soporte con Animación -->
<div class="bot-avatar-wrapper text-center my-4">
<img src="/img/bot-soporte.svg" alt="Bot de Soporte Help Desk" class="bot-avatar-img mb-3">
<div>
<div class="bot-status-pill">
<span class="bot-status-dot"></span>
<span>Asistente Activo • 24/7 en línea</span>
</div>
</div>
</div>
<!-- Características Clave del Sistema -->
<div>
<div class="login-feature-item">
<div class="login-feature-icon">
<i class="fas fa-bolt"></i>
</div>
<div>Triaje digital y tickets en tiempo real</div>
</div>
<div class="login-feature-item">
<div class="login-feature-icon">
<i class="fas fa-map-marked-alt"></i>
</div>
<div>Despacho FSM con GPS, evidencias y firma</div>
</div>
<div class="login-feature-item">
<div class="login-feature-icon">
<i class="fas fa-building"></i>
</div>
<div>Aislamiento multi-tenant 100% seguro</div>
</div>
<div class="mt-4 pt-3 border-top border-white border-opacity-10 d-flex justify-content-between align-items-center text-white-50" style="font-size: 0.75rem;">
<span>soporte.sigmac.app</span>
<span>v2.0 • SIGMA Cloud</span>
</div>
</div>
</div>
<!-- ========================================== -->
<!-- Columna Derecha: Formulario de Login -->
<!-- ========================================== -->
<div class="col-lg-7 login-form-side">
<!-- Mensajes Flash de Notificación -->
<% if (success_msg && success_msg.length > 0) { %>
<% success_msg.forEach(msg => { %>
<div class="alert alert-success alert-dismissible fade show mt-3">
<%= msg %>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<div class="alert alert-success alert-dismissible fade show rounded-3 shadow-sm border-0 mb-4" role="alert">
<div class="d-flex align-items-center">
<i class="fas fa-check-circle me-2 fs-5"></i>
<div><%= msg %></div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<% }) %>
<% } %>
<% } %>
<% if (error_msg && error_msg.length > 0) { %>
<% if (error_msg && error_msg.length > 0) { %>
<% error_msg.forEach(msg => { %>
<div class="alert alert-danger alert-dismissible fade show mt-3">
<%= msg %>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<div class="alert alert-danger alert-dismissible fade show rounded-3 shadow-sm border-0 mb-4" role="alert">
<div class="d-flex align-items-center">
<i class="fas fa-exclamation-triangle me-2 fs-5"></i>
<div><%= msg %></div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<% }) %>
<% } %>
<% } %>
<div class="row justify-content-center mt-5">
<div class="col-md-5">
<div class="card shadow">
<div class="card-header bg-primary text-white text-center py-3">
<img src="/img/logo.svg" alt="Help Desk" width="48" height="48" class="mb-2 shadow-sm rounded-3">
<h4 class="mb-0 fw-bold">Help Desk</h4>
<small class="text-white-50">Iniciar Sesión</small>
<div class="mb-4">
<div class="d-flex align-items-center gap-2 mb-2">
<img src="/img/logo.svg" alt="Help Desk" width="32" height="32" class="rounded-2 shadow-sm">
<h4 class="fw-bold text-dark mb-0">¡Bienvenido de vuelta!</h4>
</div>
<div class="card-body">
<form action="/login" method="POST">
<p class="text-muted small">Ingresa tus credenciales autorizadas para acceder a la plataforma.</p>
</div>
<form action="/login" method="POST" id="loginForm">
<!-- Campo de Correo Electrónico -->
<div class="mb-3">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control" required>
<label class="form-label text-secondary small fw-semibold">Correo Electrónico</label>
<div class="input-group login-input-group">
<span class="input-group-text"><i class="fas fa-envelope"></i></span>
<input type="email" name="email" id="inputEmail" class="form-control" placeholder="ejemplo@helpdesk.com" required autocomplete="email">
</div>
</div>
<!-- Campo de Contraseña -->
<div class="mb-3">
<label class="form-label">Contraseña</label>
<input type="password" name="password" class="form-control" required>
<div class="d-flex justify-content-between align-items-center">
<label class="form-label text-secondary small fw-semibold">Contraseña</label>
</div>
<button type="submit" class="btn btn-primary w-100">Ingresar</button>
<div class="input-group login-input-group">
<span class="input-group-text"><i class="fas fa-lock"></i></span>
<input type="password" name="password" id="inputPassword" class="form-control" placeholder="••••••••" required autocomplete="current-password" style="border-top-right-radius: 0; border-bottom-right-radius: 0;">
<button class="btn btn-outline-secondary password-toggle-btn px-3" type="button" id="togglePasswordBtn" title="Mostrar/Ocultar contraseña">
<i class="fas fa-eye" id="togglePasswordIcon"></i>
</button>
</div>
</div>
<!-- Opciones Adicionales -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="rememberMe">
<label class="form-check-label text-muted small" for="rememberMe">Recordar credenciales</label>
</div>
<a href="/register" class="text-decoration-none small text-primary fw-semibold">Registrar empresa</a>
</div>
<!-- Botón Principal de Acceso -->
<button type="submit" class="btn btn-login w-100 d-flex justify-content-center align-items-center shadow-sm">
<span>Ingresar a la Plataforma</span>
<i class="fas fa-arrow-right ms-2"></i>
</button>
</form>
<div class="text-center mt-3">
<a href="/register">¿No tienes cuenta? Regístrate</a>
<!-- Acceso Rápido / Credenciales Demo del Ecosistema -->
<div class="mt-4 pt-3 border-top">
<div class="d-flex align-items-center justify-content-between mb-2">
<span class="text-muted fw-semibold" style="font-size: 0.76rem;">
<i class="fas fa-key me-1 text-primary"></i> Acceso Rápido Demo:
</span>
<small class="text-muted" style="font-size: 0.72rem;">(1 clic para rellenar)</small>
</div>
<div class="d-flex flex-wrap gap-2">
<span class="demo-account-chip" onclick="fillCredentials('super@helpdesk.com', '123456')">
<i class="fas fa-crown text-warning me-1"></i> Superadmin
</span>
<span class="demo-account-chip" onclick="fillCredentials('admin@helpdesk.com', '123456')">
<i class="fas fa-user-shield text-info me-1"></i> Admin
</span>
<span class="demo-account-chip" onclick="fillCredentials('agent@helpdesk.com', '123456')">
<i class="fas fa-headset text-primary me-1"></i> Agente
</span>
<span class="demo-account-chip" onclick="fillCredentials('client@helpdesk.com', '123456')">
<i class="fas fa-user text-secondary me-1"></i> Cliente
</span>
</div>
</div>
<!-- Pie del Formulario -->
<div class="text-center mt-4">
<small class="text-muted">
¿Problemas de acceso? Contacta a <a href="mailto:soporte@telcotronics.com" class="text-decoration-none text-secondary fw-semibold">Soporte Telcotronics</a>
</small>
</div>
</div>
</div>
</div>
<script>
// Toggle de visibilidad de contraseña
const togglePasswordBtn = document.getElementById('togglePasswordBtn');
const inputPassword = document.getElementById('inputPassword');
const togglePasswordIcon = document.getElementById('togglePasswordIcon');
if (togglePasswordBtn && inputPassword && togglePasswordIcon) {
togglePasswordBtn.addEventListener('click', function() {
if (inputPassword.type === 'password') {
inputPassword.type = 'text';
togglePasswordIcon.classList.remove('fa-eye');
togglePasswordIcon.classList.add('fa-eye-slash');
} else {
inputPassword.type = 'password';
togglePasswordIcon.classList.remove('fa-eye-slash');
togglePasswordIcon.classList.add('fa-eye');
}
});
}
// Función para autocompletar credenciales demo
function fillCredentials(email, password) {
const emailInput = document.getElementById('inputEmail');
const passInput = document.getElementById('inputPassword');
if (emailInput && passInput) {
emailInput.value = email;
passInput.value = password;
// Efecto visual de feedback
emailInput.classList.add('is-valid');
passInput.classList.add('is-valid');
setTimeout(() => {
emailInput.classList.remove('is-valid');
passInput.classList.remove('is-valid');
}, 1200);
}
}
</script>
<%- include('../partials/footer') %>