diff --git a/package-lock.json b/package-lock.json index a31296a..3dc0ea2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -917,7 +917,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js new file mode 100644 index 0000000..19e3a21 --- /dev/null +++ b/src/components/Sidebar.js @@ -0,0 +1,48 @@ +export function createSidebar(pages, currentPage) { + const sidebar = document.createElement('aside'); + sidebar.className = 'sidebar'; + sidebar.id = 'sidebar'; + + const sidebarHeader = document.createElement('div'); + sidebarHeader.className = 'sidebar-header'; + sidebarHeader.innerHTML = /*html*/` +

Doli App

+ + `; + + const nav = document.createElement('nav'); + nav.className = 'sidebar-nav'; + + const navList = document.createElement('ul'); + navList.className = 'sidebar-menu'; + + pages.forEach(page => { + const listItem = document.createElement('li'); + const link = document.createElement('a'); + link.href = `#${page.route}`; + link.className = 'sidebar-link'; + if (page.route === currentPage) { + link.classList.add('active'); + } + link.innerHTML = /*html*/` + ${page.icon || '📄'} + ${page.name} + `; + listItem.appendChild(link); + navList.appendChild(listItem); + }); + + nav.appendChild(navList); + sidebar.appendChild(sidebarHeader); + sidebar.appendChild(nav); + + // Toggle functionality + const toggleBtn = sidebarHeader.querySelector('#sidebar-toggle'); + toggleBtn.addEventListener('click', () => { + sidebar.classList.toggle('collapsed'); + }); + + return sidebar; +} diff --git a/src/pages/pagesRegistry.js b/src/pages/pagesRegistry.js new file mode 100644 index 0000000..ac5bd89 --- /dev/null +++ b/src/pages/pagesRegistry.js @@ -0,0 +1,46 @@ +// Registry of all pages available in the application +export const pagesRegistry = [ + { + route: 'dashboard', + name: 'Dashboard', + icon: '🏠', + requiresAuth: true + }, + { + route: 'test', + name: 'Test Page', + icon: '🧪', + requiresAuth: true + }, + { + route: 'invoices', + name: 'Facturas', + icon: '📄', + requiresAuth: true + }, + { + route: 'clients', + name: 'Clientes', + icon: '👥', + requiresAuth: true + }, + { + route: 'settings', + name: 'Configuración', + icon: '⚙️', + requiresAuth: true + } +]; + +// Get pages that the user can access +export function getAvailablePages(isAuthenticated) { + if (!isAuthenticated) { + return []; + } + return pagesRegistry.filter(page => !page.requiresAuth || isAuthenticated); +} + +// Get page by route +export function getPageByRoute(route) { + return pagesRegistry.find(page => page.route === route); +} diff --git a/src/pages/test.js b/src/pages/test.js new file mode 100644 index 0000000..890d52a --- /dev/null +++ b/src/pages/test.js @@ -0,0 +1,19 @@ +export function renderTestPage() { + const container = document.createElement('div'); + container.className = 'test-page'; + + container.innerHTML = /*html*/` +

Test Page

+

This is a test page to verify sidebar navigation functionality.

+
+

The sidebar should be visible and allow navigation between different pages.

+ +
+ `; + + container.querySelector('#test-btn').addEventListener('click', () => { + alert('Test button clicked!'); + }); + + return container; +} diff --git a/src/router.js b/src/router.js index a565ba9..6ecda04 100644 --- a/src/router.js +++ b/src/router.js @@ -1,12 +1,16 @@ import { auth } from './services/auth.js'; import { renderLoginPage } from './pages/LoginPage.js'; import { renderDashboard } from './pages/DashboardPage.js'; +import { renderTestPage } from './pages/test.js'; +import { createSidebar } from './components/Sidebar.js'; +import { getAvailablePages } from './pages/pagesRegistry.js'; export function initRouter() { const app = document.querySelector('#app'); function navigate() { const hash = window.location.hash || '#login'; + const route = hash.substring(1); // Remove the # symbol if (!auth.checkAuth() && hash !== '#login') { window.location.hash = '#login'; @@ -15,17 +19,48 @@ export function initRouter() { app.innerHTML = ''; - switch (hash) { - case '#login': - app.appendChild(renderLoginPage(() => { + // For authenticated routes, add sidebar and main content wrapper + if (auth.checkAuth() && hash !== '#login') { + const layout = document.createElement('div'); + layout.className = 'app-layout'; + + // Create sidebar + const pages = getAvailablePages(true); + const sidebar = createSidebar(pages, route); + + // Create main content area + const mainContent = document.createElement('main'); + mainContent.className = 'main-content'; + + let pageContent; + switch (hash) { + case '#dashboard': + pageContent = renderDashboard(); + break; + case '#test': + pageContent = renderTestPage(); + break; + default: + // For unregistered routes, redirect to dashboard window.location.hash = '#dashboard'; - })); - break; - case '#dashboard': - app.appendChild(renderDashboard()); - break; - default: - window.location.hash = '#login'; + return; + } + + mainContent.appendChild(pageContent); + layout.appendChild(sidebar); + layout.appendChild(mainContent); + app.appendChild(layout); + } else { + // For login page, no sidebar + switch (hash) { + case '#login': + app.appendChild(renderLoginPage(() => { + window.location.hash = '#dashboard'; + })); + break; + default: + window.location.hash = '#login'; + } } } diff --git a/src/style.css b/src/style.css index 16aad82..17b2db5 100644 --- a/src/style.css +++ b/src/style.css @@ -37,8 +37,6 @@ a:hover { body { margin: 0; - display: flex; - place-items: center; min-width: 320px; min-height: 100vh; } @@ -49,10 +47,8 @@ h1 { } #app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; + width: 100%; + min-height: 100vh; } .logo { @@ -283,3 +279,198 @@ button:focus-visible { .card-stat.negative .card-stat-value { color: var(--danger); } + +/* App Layout with Sidebar */ +.app-layout { + display: flex; + min-height: 100vh; + width: 100%; +} + +/* Sidebar Styles */ +.sidebar { + width: 250px; + background-color: var(--card-bg); + border-right: 1px solid var(--border-color); + display: flex; + flex-direction: column; + transition: width 0.3s ease, transform 0.3s ease; + position: relative; + z-index: 100; +} + +.sidebar-header { + padding: 1.5rem 1rem; + border-bottom: 1px solid var(--border-color); + display: flex; + justify-content: space-between; + align-items: center; +} + +.sidebar-header h2 { + margin: 0; + font-size: 1.5rem; + color: var(--text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.sidebar-toggle { + background: transparent; + border: none; + cursor: pointer; + padding: 0.5rem; + color: var(--text-secondary); + font-size: 1.25rem; + border-radius: 4px; + transition: background-color 0.2s; +} + +.sidebar-toggle:hover { + background-color: var(--border-color); + border-color: transparent; +} + +.sidebar-nav { + flex: 1; + overflow-y: auto; + padding: 1rem 0; +} + +.sidebar-menu { + list-style: none; + margin: 0; + padding: 0; +} + +.sidebar-menu li { + margin: 0; +} + +.sidebar-link { + display: flex; + align-items: center; + padding: 0.75rem 1rem; + color: var(--text-secondary); + text-decoration: none; + transition: background-color 0.2s, color 0.2s; + cursor: pointer; +} + +.sidebar-link:hover { + background-color: var(--border-color); + color: var(--text-primary); +} + +.sidebar-link.active { + background-color: rgba(37, 99, 235, 0.1); + color: var(--primary); + border-left: 3px solid var(--primary); +} + +.sidebar-icon { + font-size: 1.25rem; + margin-right: 0.75rem; + min-width: 1.5rem; + text-align: center; +} + +.sidebar-text { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Collapsed Sidebar State */ +.sidebar.collapsed { + width: 70px; +} + +.sidebar.collapsed .sidebar-header h2, +.sidebar.collapsed .sidebar-text { + opacity: 0; + width: 0; + overflow: hidden; +} + +.sidebar.collapsed .sidebar-icon { + margin-right: 0; +} + +/* Main Content Area */ +.main-content { + flex: 1; + overflow-y: auto; + background-color: #f8fafc; +} + +.main-content > div { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + z-index: 1000; + transform: translateX(0); + } + + .sidebar.collapsed { + transform: translateX(-100%); + width: 250px; + } + + .main-content { + margin-left: 0; + } +} + +/* Test Page Styles */ +.test-page { + padding: 2rem; +} + +.test-page h1 { + color: var(--text-primary); + margin-bottom: 1rem; + font-size: 2rem; +} + +.test-content { + background: var(--card-bg); + padding: 2rem; + border-radius: 12px; + border: 1px solid var(--border-color); + margin-top: 1rem; +} + +.test-button { + background-color: var(--success); + color: white; + padding: 0.5rem 1rem; + border: none; + border-radius: 6px; + cursor: pointer; + margin-top: 1rem; +} + +.test-button:hover { + background-color: var(--success-hover); + border-color: transparent; +} + +/* Adjust dashboard and login for new layout */ +.login-container { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + width: 100%; +}