| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
| <meta name="description" content=""> |
| <meta name="author" content=""> |
| <link rel="icon" href="favicon.ico"> |
| <title>Login</title> |
| <link rel="stylesheet" href="css/simplebar.css"> |
| <link href="https://fonts.googleapis.com/css2?family=Overpass:ital,wght@0,100;0,200;0,300;0,400;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet"> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <link rel="stylesheet" href="css/daterangepicker.css"> |
| <link rel="stylesheet" href="css/app-light.css" id="lightTheme"> |
| <link rel="stylesheet" href="css/app-dark.css" id="darkTheme" disabled> |
| <style> |
| .error-message { |
| color: #dc3545; |
| font-size: 0.875rem; |
| margin-top: 0.25rem; |
| display: none; |
| } |
| .is-invalid { |
| border-color: #dc3545 !important; |
| } |
| #loadingIndicator { |
| display: none; |
| } |
| </style> |
| </head> |
| <body class="dark"> |
| <div class="wrapper vh-100"> |
| <div class="row align-items-center h-100"> |
| <form class="col-lg-3 col-md-4 col-10 mx-auto text-center" id="loginForm"> |
| <a class="navbar-brand mx-auto mt-2 flex-fill text-center"> |
| <img src="assets/images/LOGO.png" alt="Logo"> |
| </a> |
| <h1 class="h6 mb-3">Iniciar Sesión</h1> |
|
|
| <div id="generalError" class="alert alert-danger" role="alert" style="display: none;"></div> |
|
|
| <div class="form-group"> |
| <label for="inputUsername" class="sr-only">Usuario</label> |
| <input type="text" id="inputUsername" class="form-control form-control-lg" placeholder="Usuario" required autofocus> |
| <div id="usernameError" class="error-message">Usuario requerido</div> |
| </div> |
| <div class="form-group"> |
| <label for="inputPassword" class="sr-only">Contraseña</label> |
| <input type="password" id="inputPassword" class="form-control form-control-lg" placeholder="Contraseña" required> |
| <div id="passwordError" class="error-message">Contraseña requerida</div> |
| </div> |
|
|
| <button class="btn btn-lg btn-primary btn-block" type="submit" id="loginButton"> |
| <span id="loginText">Ingresar</span> |
| <span id="loadingIndicator" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> |
| </button> |
| <p class="mt-5 mb-3 text-muted">Sistema de Diagnóstico asistido por Inteligencia Artificial para la detección de Retinopatía Diabética</p> |
| </form> |
| </div> |
| </div> |
|
|
| <script src="js/jquery.min.js"></script> |
| <script src="js/popper.min.js"></script> |
| <script src="js/bootstrap.min.js"></script> |
| <script src="api.js"></script> |
|
|
| <script> |
| $(document).ready(function() { |
| checkCurrentUser(); |
| loadRememberedUser(); |
| $('#loginForm').on('submit', handleLogin); |
| |
| async function checkCurrentUser() { |
| try { |
| const r = await api.getSession(); |
| if (r && r.success) window.location.href = 'index.html'; |
| } catch(e) {} |
| } |
| |
| function loadRememberedUser() { |
| if (localStorage.getItem('rememberMe') === 'true') { |
| const u = localStorage.getItem('username'); |
| if (u) $('#inputUsername').val(u); |
| } |
| } |
| |
| async function handleLogin(e) { |
| e.preventDefault(); |
| $('.error-message').hide(); |
| $('#inputUsername, #inputPassword').removeClass('is-invalid'); |
| $('#generalError').hide().removeClass('alert-success').addClass('alert-danger'); |
| |
| const username = $('#inputUsername').val().trim(); |
| const password = $('#inputPassword').val().trim(); |
| if (!username) { $('#inputUsername').addClass('is-invalid'); $('#usernameError').show(); return; } |
| if (!password) { $('#inputPassword').addClass('is-invalid'); $('#passwordError').show(); return; } |
| |
| $('#loginText').hide(); $('#loadingIndicator').show(); $('#loginButton').prop('disabled', true); |
| const response = await api.login(username, password).catch(() => null); |
| $('#loginText').show(); $('#loadingIndicator').hide(); $('#loginButton').prop('disabled', false); |
| |
| if (response && response.success) { |
| if ($('#rememberMe').is(':checked')) { |
| localStorage.setItem('rememberMe', 'true'); |
| localStorage.setItem('username', username); |
| } else { |
| localStorage.removeItem('rememberMe'); |
| localStorage.removeItem('username'); |
| } |
| $('#generalError').removeClass('alert-danger').addClass('alert-success') |
| .text('¡Bienvenido! Redirigiendo...').show(); |
| setTimeout(() => { window.location.href = 'index.html'; }, 800); |
| } else { |
| const msg = response?.message || 'Usuario o contraseña incorrectos'; |
| $('#generalError').text(msg).show(); |
| $('#inputUsername, #inputPassword').addClass('is-invalid'); |
| $('#inputPassword').val(''); |
| } |
| } |
| }); |
| </script> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| <script> |
| |
| |
| function _initFeather() { |
| if (typeof feather !== 'undefined') feather.replace({ 'stroke-width': 1.8 }); |
| } |
| document.addEventListener('DOMContentLoaded', _initFeather); |
| |
| var _fObserver = new MutationObserver(function(mutations) { |
| var needsRefresh = mutations.some(function(m) { |
| return m.addedNodes.length > 0; |
| }); |
| if (needsRefresh) _initFeather(); |
| }); |
| document.addEventListener('DOMContentLoaded', function() { |
| _fObserver.observe(document.body, { childList: true, subtree: true }); |
| }); |
| </script> |
| </body> |
| </html> |
| </html> |