Implement authentication and routing features with login and dashboard pages
This commit is contained in:
parent
f35cd90cd6
commit
282118b93f
|
|
@ -0,0 +1,31 @@
|
||||||
|
export function renderLogin(onLogin) {
|
||||||
|
const loginContainer = document.createElement('div');
|
||||||
|
loginContainer.className = 'login-container';
|
||||||
|
|
||||||
|
loginContainer.innerHTML = `
|
||||||
|
<div class="login-card">
|
||||||
|
<h2 class="login-title">Iniciar Sesión</h2>
|
||||||
|
<form id="login-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<input type="email" id="email" name="email" required autocomplete="email">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Contraseña</label>
|
||||||
|
<input type="password" id="password" name="password" required autocomplete="current-password">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="login-button">Ingresar</button>
|
||||||
|
</form>
|
||||||
|
<p id="login-error" class="login-error"></p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
loginContainer.querySelector('#login-form').addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const email = loginContainer.querySelector('#email').value;
|
||||||
|
const password = loginContainer.querySelector('#password').value;
|
||||||
|
onLogin(email, password);
|
||||||
|
});
|
||||||
|
|
||||||
|
return loginContainer;
|
||||||
|
}
|
||||||
24
src/main.js
24
src/main.js
|
|
@ -1,24 +1,4 @@
|
||||||
import './style.css'
|
import './style.css'
|
||||||
import javascriptLogo from './javascript.svg'
|
import { initRouter } from './router.js'
|
||||||
import viteLogo from '/vite.svg'
|
|
||||||
import { setupCounter } from './counter.js'
|
|
||||||
|
|
||||||
document.querySelector('#app').innerHTML = `
|
initRouter()
|
||||||
<div>
|
|
||||||
<a href="https://vite.dev" target="_blank">
|
|
||||||
<img src="${viteLogo}" class="logo" alt="Vite logo" />
|
|
||||||
</a>
|
|
||||||
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank">
|
|
||||||
<img src="${javascriptLogo}" class="logo vanilla" alt="JavaScript logo" />
|
|
||||||
</a>
|
|
||||||
<h1>Hello Vite!</h1>
|
|
||||||
<div class="card">
|
|
||||||
<button id="counter" type="button"></button>
|
|
||||||
</div>
|
|
||||||
<p class="read-the-docs">
|
|
||||||
Click on the Vite logo to learn more
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
|
|
||||||
setupCounter(document.querySelector('#counter'))
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { auth } from '../services/auth.js';
|
||||||
|
|
||||||
|
export function renderDashboard() {
|
||||||
|
const user = auth.getUser();
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'dashboard';
|
||||||
|
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="dashboard-header">
|
||||||
|
<h1>Bienvenido, ${user?.email || 'Usuario'}</h1>
|
||||||
|
<button id="logout-button" class="logout-button">Cerrar Sesión</button>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-content">
|
||||||
|
<p>Dashboard protegido</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
container.querySelector('#logout-button').addEventListener('click', () => {
|
||||||
|
auth.logout();
|
||||||
|
window.location.hash = '#login';
|
||||||
|
});
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { auth } from '../services/auth.js';
|
||||||
|
import { renderLogin } from '../components/Login.js';
|
||||||
|
|
||||||
|
export function renderLoginPage(onLoginSuccess) {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'login-page';
|
||||||
|
|
||||||
|
const loginComponent = renderLogin((email, password) => {
|
||||||
|
if (auth.login(email, password)) {
|
||||||
|
if (onLoginSuccess) {
|
||||||
|
onLoginSuccess();
|
||||||
|
} else {
|
||||||
|
window.location.hash = '#dashboard';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const error = container.querySelector('#login-error');
|
||||||
|
error.textContent = 'Credenciales inválidas';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
container.appendChild(loginComponent);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { auth } from './services/auth.js';
|
||||||
|
import { renderLoginPage } from './pages/LoginPage.js';
|
||||||
|
import { renderDashboard } from './pages/DashboardPage.js';
|
||||||
|
|
||||||
|
export function initRouter() {
|
||||||
|
const app = document.querySelector('#app');
|
||||||
|
|
||||||
|
function navigate() {
|
||||||
|
const hash = window.location.hash || '#login';
|
||||||
|
|
||||||
|
if (!auth.checkAuth() && hash !== '#login') {
|
||||||
|
window.location.hash = '#login';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.innerHTML = '';
|
||||||
|
|
||||||
|
switch (hash) {
|
||||||
|
case '#login':
|
||||||
|
app.appendChild(renderLoginPage(() => {
|
||||||
|
window.location.hash = '#dashboard';
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
case '#dashboard':
|
||||||
|
app.appendChild(renderDashboard());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
window.location.hash = '#login';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('hashchange', navigate);
|
||||||
|
navigate();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
export class Auth {
|
||||||
|
constructor() {
|
||||||
|
this.currentUser = null;
|
||||||
|
this.isAuthenticated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
login(email, password) {
|
||||||
|
if (email && password) {
|
||||||
|
this.currentUser = { email };
|
||||||
|
this.isAuthenticated = true;
|
||||||
|
localStorage.setItem('user', JSON.stringify(this.currentUser));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
this.currentUser = null;
|
||||||
|
this.isAuthenticated = false;
|
||||||
|
localStorage.removeItem('user');
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAuth() {
|
||||||
|
const user = localStorage.getItem('user');
|
||||||
|
if (user) {
|
||||||
|
this.currentUser = JSON.parse(user);
|
||||||
|
this.isAuthenticated = true;
|
||||||
|
}
|
||||||
|
return this.isAuthenticated;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser() {
|
||||||
|
return this.currentUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const auth = new Auth();
|
||||||
113
src/style.css
113
src/style.css
|
|
@ -94,3 +94,116 @@ button:focus-visible {
|
||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-card {
|
||||||
|
background: #2a2a2a;
|
||||||
|
padding: 2.5rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-title {
|
||||||
|
color: #fff;
|
||||||
|
margin: 0 0 2rem 0;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #444;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #646cff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.875rem;
|
||||||
|
background-color: #646cff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button:hover {
|
||||||
|
background-color: #535bf2;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-error {
|
||||||
|
color: #ff6b6b;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-header h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-content {
|
||||||
|
background: #2a2a2a;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-button {
|
||||||
|
background-color: #ff6b6b;
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-button:hover {
|
||||||
|
background-color: #fa5252;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue