- Backend Node.js/Express con PostgreSQL - Frontend React 19 con Vite - Docker Compose para orquestacion - Documentacion completa en README.md - Scripts SQL para base de datos - Configuracion de ejemplo (.env.example)
357 lines
12 KiB
JavaScript
357 lines
12 KiB
JavaScript
// Layout.jsx
|
|
// import React, { useState } from "react";
|
|
// import { Outlet, NavLink, useLocation } from "react-router-dom";
|
|
// import { useNavigate } from 'react-router-dom';
|
|
// import { FaBell, FaCog } from "react-icons/fa";
|
|
// import "../styles/Dashboard.css";
|
|
|
|
// const menuConfig = [
|
|
// {
|
|
// label: "Dashboards",
|
|
// basePath: "/app/income",
|
|
// submenu: [
|
|
// { label: "Income", route: "/app/income" },
|
|
// { label: "Expenses", route: "/app/expenses" },
|
|
// { label: "Cost per room", route: "/app/cost-per-room" },
|
|
// { label: "Budget", route: "/app/budget" },
|
|
// ],
|
|
// },
|
|
// {
|
|
// label: "Expenses to be approved",
|
|
// basePath: "/app/pending-approval",
|
|
// submenu: [
|
|
// { label: "Pending approval", route: "/app/pending-approval" },
|
|
// { label: "Approved", route: "/app/approved" },
|
|
// { label: "Rejected", route: "/app/rejected" },
|
|
// ],
|
|
// },
|
|
// {
|
|
// label: "Expenses",
|
|
// basePath: "/app/report",
|
|
// submenu: [
|
|
// { label: "Report", route: "/app/report" },
|
|
// { label: "New Expense", route: "/app/new-expense" },
|
|
// { label: "Payments", route: "/app/payments" },
|
|
// { label: "Monthly Payments", route: "/app/monthly-payments" },
|
|
// { label: "Id", route: "/app/expense-id" },
|
|
// ],
|
|
// },
|
|
// {
|
|
// label: "Inventory",
|
|
// basePath: "/app/products",
|
|
// submenu: [
|
|
// { label: "Products", route: "/app/products" },
|
|
// { label: "New Product", route: "/app/new-product" },
|
|
// { label: "Report", route: "/app/inventory-report" },
|
|
|
|
// ],
|
|
// },
|
|
// {
|
|
// label: "Payroll",
|
|
// basePath: "/app/payroll",
|
|
// submenu: [
|
|
// { label: "Report", route: "/app/payroll" },
|
|
// ],
|
|
// },
|
|
// {
|
|
// label: "Hotel",
|
|
// basePath: "/app/properties",
|
|
// submenu: [
|
|
// { label: "Properties", route: "/app/properties" },
|
|
// // { label: "New Property", route: "/app/properties/:id"},
|
|
// ],
|
|
// },
|
|
|
|
// ];
|
|
|
|
// export default function Layout() {
|
|
// const navigate = useNavigate();
|
|
// const location = useLocation();
|
|
// const [isSidebarOpen, setSidebarOpen] = useState(true);
|
|
// const toggleSidebar = () => setSidebarOpen(!isSidebarOpen);
|
|
// const isDetailPage = /^\/app\/properties\/\d+$/.test(location.pathname);
|
|
// const isSettingsPage = location.pathname === "/app/settings";
|
|
// // Detectar qué menú está activo según la ruta actual
|
|
// // const activeSection = menuConfig.find(section =>
|
|
// // location.pathname.startsWith(section.basePath)
|
|
// // );
|
|
|
|
// // const activeSection = menuConfig.find(section =>
|
|
// // section.submenu.some(item => location.pathname.startsWith(item.route))
|
|
// // );
|
|
|
|
// // Encuentra la sección activa o ignórala si es una ruta especial como "/app/properties/:id"
|
|
// const activeSection = menuConfig.find(section =>
|
|
// section.submenu.some(item => location.pathname.startsWith(item.route))
|
|
// );
|
|
|
|
// // Si no hay sección activa, es una página sin menú (como detalles)
|
|
// const activeSubmenu = activeSection?.submenu || [];
|
|
|
|
// return (
|
|
// <div className="dashboard-layout">
|
|
// {/* Sidebar */}
|
|
// {isSidebarOpen && (
|
|
// <aside className="sidebar">
|
|
// <nav>
|
|
// <NavLink to="/app/income">Dashboards</NavLink>
|
|
// <NavLink to="/app/pending-approval">Expenses to be approved</NavLink>
|
|
// <NavLink to="/app/report">Expenses</NavLink>
|
|
// <NavLink to="/app/products">Inventory</NavLink>
|
|
// <NavLink to="/app/payroll">Payroll</NavLink>
|
|
// <NavLink to="/app/properties">Hotel</NavLink>
|
|
// </nav>
|
|
// </aside>
|
|
// )}
|
|
|
|
// {/* Main content */}
|
|
// <div className="main-content">
|
|
// {/* Topbar */}
|
|
// <div className="topbar">
|
|
// <div className="topbar-header">
|
|
// {/* Oculta título si estamos en /app/settings */}
|
|
// {!isSettingsPage && (
|
|
// <div className="topbar-title">{activeSection?.label}</div>
|
|
// )}
|
|
|
|
// <div className="topbar-icons">
|
|
// <FaBell className="topbar-icon" />
|
|
// <FaCog
|
|
// className="topbar-icon cursor-pointer"
|
|
// onClick={() => navigate("/app/settings")}
|
|
// />
|
|
// </div>
|
|
// </div>
|
|
|
|
// {/*Oculta submenú si es página de detalles o settings */}
|
|
// {!isDetailPage && !isSettingsPage && (
|
|
// <div className="topbar-submenu">
|
|
// {activeSubmenu.map((item, index) => (
|
|
// <NavLink
|
|
// key={index}
|
|
// to={item.route}
|
|
// className={({ isActive }) =>
|
|
// isActive ? "submenu-link active" : "submenu-link"
|
|
// }
|
|
// >
|
|
// {item.label}
|
|
// </NavLink>
|
|
// ))}
|
|
// </div>
|
|
// )}
|
|
// </div>
|
|
|
|
// {/* Página actual */}
|
|
// <div className="content">
|
|
// <Outlet />
|
|
// </div>
|
|
// </div>
|
|
// </div>
|
|
// );
|
|
|
|
// return (
|
|
// <div className="dashboard-layout">
|
|
// {/* Sidebar */}
|
|
// {isSidebarOpen && (
|
|
// <aside className="sidebar">
|
|
// <nav>
|
|
// <NavLink to="/app/income">Dashboards</NavLink>
|
|
// <NavLink to="/app/pending-approval">Expenses to be approved</NavLink>
|
|
// <NavLink to="/app/report">Expenses</NavLink>
|
|
// <NavLink to="/app/products">Inventory</NavLink>
|
|
// <NavLink to="/app/payroll">Payroll</NavLink>
|
|
// <NavLink to="/app/properties">Hotel</NavLink>
|
|
// </nav>
|
|
// </aside>
|
|
// )}
|
|
|
|
// {/* Contenido principal */}
|
|
// <div className="main-content">
|
|
// {/* ÚNICO Topbar */}
|
|
// <div className="topbar">
|
|
// {/* Línea superior: título + iconos */}
|
|
// <div className="topbar-header">
|
|
// <div className="topbar-title">{activeSection?.label}</div>
|
|
// <div className="topbar-icons">
|
|
// <FaBell className="topbar-icon" />
|
|
// <FaCog className="topbar-icon cursor-pointer" onClick={() => navigate("/app/settings")} />
|
|
// </div>
|
|
// </div>
|
|
|
|
// {/* Línea inferior: submenú dinámico */}
|
|
|
|
// {/* Línea inferior: submenú dinámico */}
|
|
// {!isDetailPage && (
|
|
// <div className="topbar-submenu">
|
|
// {activeSubmenu.map((item, index) => (
|
|
// <NavLink
|
|
// key={index}
|
|
// to={item.route}
|
|
// className={({ isActive }) =>
|
|
// isActive ? "submenu-link active" : "submenu-link"
|
|
// }
|
|
// >
|
|
// {item.label}
|
|
// </NavLink>
|
|
// ))}
|
|
// </div>
|
|
// )}
|
|
|
|
// {/* <div className="topbar-submenu">
|
|
// {activeSubmenu.map((item, index) => (
|
|
// <NavLink
|
|
// key={index}
|
|
// to={item.route}
|
|
// className={({ isActive }) =>
|
|
// isActive ? "submenu-link active" : "submenu-link"
|
|
// }
|
|
// >
|
|
// {item.label}
|
|
// </NavLink>
|
|
// ))}
|
|
// </div> */}
|
|
// </div>
|
|
|
|
// {/* Aquí va el contenido de la página */}
|
|
// <div className="content">
|
|
// <Outlet />
|
|
// </div>
|
|
// </div>
|
|
// </div>
|
|
// );
|
|
//}
|
|
|
|
|
|
|
|
//{ label: "Property", route: "/app/properties/:id" },
|
|
|
|
// import React from "react";
|
|
// import { Outlet, useLocation, NavLink } from "react-router-dom";
|
|
// import { menuConfig } from "../constants/menuConfig";
|
|
// import { FaBell, FaCog } from "react-icons/fa";
|
|
// import "../styles/Dashboard.css";
|
|
|
|
// export default function Layout() {
|
|
// const location = useLocation();
|
|
// const pathname = location.pathname;
|
|
|
|
// // Encuentra la sección activa
|
|
// const activeSectionKey = Object.keys(menuConfig).find((key) =>
|
|
// pathname.startsWith(menuConfig[key].baseRoute)
|
|
// );
|
|
// const activeSection = menuConfig[activeSectionKey];
|
|
// const activeSubmenu = activeSection?.submenu || [];
|
|
|
|
// return (
|
|
// <div className="dashboard-layout">
|
|
// {/* SIDEBAR */}
|
|
// <aside className="sidebar">
|
|
// <nav>
|
|
// {Object.entries(menuConfig).map(([key, section]) => (
|
|
// <NavLink
|
|
// key={key}
|
|
// to={section.baseRoute}
|
|
// className={({ isActive }) =>
|
|
// isActive ? "menu-items a active" : "menu-items a"
|
|
// }
|
|
// >
|
|
// {section.label}
|
|
// </NavLink>
|
|
// ))}
|
|
// </nav>
|
|
// </aside>
|
|
|
|
// {/* CONTENIDO PRINCIPAL */}
|
|
// <div className="main-content">
|
|
// {/* TOPBAR */}
|
|
// <div className="topbar">
|
|
// <div className="topbar-header">
|
|
// <div className="topbar-title">{activeSection?.label}</div>
|
|
// <div className="topbar-icons">
|
|
// <FaBell className="topbar-icon" />
|
|
// <FaCog className="topbar-icon" />
|
|
// </div>
|
|
// </div>
|
|
|
|
// <div className="topbar-submenu">
|
|
// {activeSubmenu.map((item, index) => (
|
|
// <NavLink
|
|
// key={index}
|
|
// to={item.route}
|
|
// className={({ isActive }) =>
|
|
// isActive ? "submenu-link active" : "submenu-link"
|
|
// }
|
|
// >
|
|
// {item.label}
|
|
// </NavLink>
|
|
// ))}
|
|
// </div>
|
|
// </div>
|
|
|
|
|
|
// {/* CONTENIDO */}
|
|
// <div className="content">
|
|
// <Outlet />
|
|
// </div>
|
|
// </div>
|
|
// </div>
|
|
// );
|
|
// }
|
|
|
|
|
|
|
|
// // src/components/Layout.jsx
|
|
// import React, { useState } from "react";
|
|
// import { Outlet, NavLink } from "react-router-dom";
|
|
// import "../styles/Dashboard.css";
|
|
|
|
// export default function Layout() {
|
|
// const [isSidebarOpen, setSidebarOpen] = useState(true);
|
|
|
|
// const toggleSidebar = () => {
|
|
// setSidebarOpen(!isSidebarOpen);
|
|
// };
|
|
|
|
// return (
|
|
// <div className="dashboard-layout">
|
|
|
|
// {/* Sidebar */}
|
|
// {isSidebarOpen && (
|
|
// <aside className="sidebar">
|
|
// <nav>
|
|
// <h1></h1>
|
|
// <NavLink to="/app/dashboard">Dashboards</NavLink>
|
|
// <NavLink to="/app/expenses-to-approve">Expenses to be approved</NavLink>
|
|
// <NavLink to="/app/expenses">Expenses</NavLink>
|
|
// <NavLink to="/app/inventory">Inventory</NavLink>
|
|
// <NavLink to="/app/payroll">Payroll</NavLink>
|
|
// <NavLink to="/app/hotel">Hotel</NavLink>
|
|
// <NavLink to="/app/income">Income</NavLink>
|
|
// <NavLink to="/app/employees">Employees</NavLink>
|
|
// <NavLink to="/app/contracts">Contracts</NavLink>
|
|
// <NavLink to="/app/payments">Payments</NavLink>
|
|
// <NavLink to="/app/pending-approval">PendingApproval</NavLink>
|
|
// </nav>
|
|
// </aside>
|
|
// )}
|
|
|
|
// {/* Contenedor principal */}
|
|
// <div className="main-content">
|
|
// {/* Topbar */}
|
|
// <div className="topbar">
|
|
// <button onClick={toggleSidebar} style={{ fontSize: "1.2rem", marginRight: "auto", background: "none", border: "none", color: "white", cursor: "pointer" }}>
|
|
// ☰
|
|
// </button>
|
|
// <span >Dashboard</span> {/* Cambia esto dinámicamente si deseas */}
|
|
// </div>
|
|
|
|
// {/* Contenido de cada página */}
|
|
// <div className="content">
|
|
// <Outlet />
|
|
// </div>
|
|
// </div>
|
|
// </div>
|
|
// );
|
|
// }
|
|
|
|
// <NavLink to="/app/users">Users</NavLink>
|