Poner una barra lateral adaptable
This commit is contained in:
parent
6932c71717
commit
ac84ee86c1
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { auth } from '../services/auth.js';
|
||||||
|
import { getPages } from '../pages/pagesRegistry.js';
|
||||||
|
|
||||||
|
export function renderSidebar() {
|
||||||
|
const sidebar = document.createElement('aside');
|
||||||
|
sidebar.className = 'sidebar';
|
||||||
|
|
||||||
|
const appName = document.createElement('div');
|
||||||
|
appName.className = 'sidebar-brand';
|
||||||
|
appName.textContent = 'Aventa';
|
||||||
|
|
||||||
|
const nav = document.createElement('nav');
|
||||||
|
nav.className = 'sidebar-nav';
|
||||||
|
|
||||||
|
const items = getPages().map(p => ({ label: p.label, hash: p.hash }));
|
||||||
|
|
||||||
|
items.forEach(({ label, hash }) => {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.className = 'nav-link';
|
||||||
|
link.href = hash;
|
||||||
|
link.textContent = label;
|
||||||
|
|
||||||
|
if (window.location.hash === hash) {
|
||||||
|
link.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.appendChild(link);
|
||||||
|
});
|
||||||
|
|
||||||
|
const footer = document.createElement('div');
|
||||||
|
footer.className = 'sidebar-footer';
|
||||||
|
|
||||||
|
const logoutBtn = document.createElement('button');
|
||||||
|
logoutBtn.className = 'logout-button';
|
||||||
|
logoutBtn.textContent = 'Log out';
|
||||||
|
logoutBtn.addEventListener('click', () => {
|
||||||
|
auth.logout();
|
||||||
|
window.location.hash = '#login';
|
||||||
|
});
|
||||||
|
|
||||||
|
footer.appendChild(logoutBtn);
|
||||||
|
|
||||||
|
sidebar.appendChild(appName);
|
||||||
|
sidebar.appendChild(nav);
|
||||||
|
sidebar.appendChild(footer);
|
||||||
|
|
||||||
|
return sidebar;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
function toLabel(name) {
|
||||||
|
return name
|
||||||
|
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
||||||
|
.replace(/^./, s => s.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
function toHash(name) {
|
||||||
|
return `#${name.toLowerCase()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findRender(mod) {
|
||||||
|
if (typeof mod.renderPage === 'function') return mod.renderPage;
|
||||||
|
for (const key of Object.keys(mod)) {
|
||||||
|
const val = mod[key];
|
||||||
|
if (typeof val === 'function' && /^render/i.test(key)) return val;
|
||||||
|
}
|
||||||
|
if (typeof mod.default === 'function') return mod.default;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modules = import.meta.glob('./*.js', { eager: true });
|
||||||
|
|
||||||
|
const bannedPages = ['LoginPage.js'];
|
||||||
|
|
||||||
|
const pages = Object.entries(modules)
|
||||||
|
.map(([path, mod]) => {
|
||||||
|
const file = path.split('/').pop();
|
||||||
|
const base = file.replace('.js', '');
|
||||||
|
if (bannedPages.includes(file)) return null;
|
||||||
|
|
||||||
|
// Derive page name from filename, strip common suffix "Page"
|
||||||
|
const name = base.replace(/Page$/, '');
|
||||||
|
const render = findRender(mod);
|
||||||
|
if (!render) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
path,
|
||||||
|
name,
|
||||||
|
label: toLabel(name),
|
||||||
|
hash: toHash(name),
|
||||||
|
render
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.sort((a, b) => a.label.localeCompare(b.label));
|
||||||
|
|
||||||
|
export function getPages() {
|
||||||
|
return pages.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPageByHash(hash) {
|
||||||
|
return pages.find(p => p.hash === hash) || null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
export function renderDashboard() {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'dashboard';
|
||||||
|
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="dashboard-content">
|
||||||
|
<p>esto es un test</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { auth } from './services/auth.js';
|
import { auth } from './services/auth.js';
|
||||||
import { renderLoginPage } from './pages/LoginPage.js';
|
import { renderLoginPage } from './pages/LoginPage.js';
|
||||||
import { renderDashboard } from './pages/DashboardPage.js';
|
import { getPageByHash } from './pages/pagesRegistry.js';
|
||||||
|
import { renderSidebar } from './components/Sidebar.js';
|
||||||
|
|
||||||
export function initRouter() {
|
export function initRouter() {
|
||||||
const app = document.querySelector('#app');
|
const app = document.querySelector('#app');
|
||||||
|
|
@ -21,11 +22,23 @@ export function initRouter() {
|
||||||
window.location.hash = '#dashboard';
|
window.location.hash = '#dashboard';
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
case '#dashboard':
|
default: {
|
||||||
app.appendChild(renderDashboard());
|
// Dynamic page rendering with sidebar
|
||||||
|
const page = getPageByHash(hash);
|
||||||
|
if (!page) {
|
||||||
|
window.location.hash = '#dashboard';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const layout = document.createElement('div');
|
||||||
|
layout.className = 'app-layout';
|
||||||
|
const sidebar = renderSidebar();
|
||||||
|
const main = document.createElement('main');
|
||||||
|
main.appendChild(page.render());
|
||||||
|
layout.appendChild(sidebar);
|
||||||
|
layout.appendChild(main);
|
||||||
|
app.appendChild(layout);
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
window.location.hash = '#login';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -194,10 +194,16 @@ button:focus-visible {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||||
|
min-height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-header h1 {
|
.dashboard-header h1 {
|
||||||
font-size: 2rem;
|
font-size: 2.4rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -283,3 +289,58 @@ button:focus-visible {
|
||||||
.card-stat.negative .card-stat-value {
|
.card-stat.negative .card-stat-value {
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Layout with sidebar */
|
||||||
|
.app-layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 260px 1fr;
|
||||||
|
gap: 1.5rem;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
position: sticky;
|
||||||
|
top: 1rem;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1rem;
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-brand {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
display: block;
|
||||||
|
padding: 0.6rem 0.8rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link:hover {
|
||||||
|
background: #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link.active {
|
||||||
|
background: #eff6ff;
|
||||||
|
border-color: #bfdbfe;
|
||||||
|
color: #1e40af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-footer {
|
||||||
|
margin-top: 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue