Desarrollo de views + frontend

This commit is contained in:
2025-08-29 02:27:28 +00:00
parent 09610df995
commit 44d1adecdc
15 changed files with 1027 additions and 358 deletions
@@ -0,0 +1,18 @@
<div class="d-flex align-items-center justify-content-between mb-3">
<h1 class="h4 m-0">Estado de Comandas</h1>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="soloAbiertas">
<label class="form-check-label" for="soloAbiertas">Solo abiertas</label>
</div>
</div>
<!-- tu tabla/listado acá -->
<table class="table table-sm table-bordered">
<thead><tr><th>ID</th><th>Mesa</th><th>Estado</th></tr></thead>
<tbody>
<tr>
<td>1</td><td>5</td>
<td><span class="badge badge-outline badge-estado-abierta">Abierta</span></td>
</tr>
</tbody>
</table>
+16
View File
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="es">
<head>
<% include ../partials/_head %>
</head>
<body data-page="<%= pageId %>">
<% include ../partials/_navbar %>
<main class="container">
<%- body %>
</main>
<% include ../partials/_sidebar %>
<% include ../partials/_footer %>
</body>
</html>
@@ -0,0 +1,42 @@
<!-- /partials/_footer.html -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
/**
* (Solo si usas HTML estático)
* Carga “partials” desde elementos con [data-include="/partials/..."].
* Si usas EJS/templating, podés quitar esto.
*/
async function scLoadPartials(){
const includes = document.querySelectorAll("[data-include]");
for (const el of includes) {
const url = el.getAttribute("data-include");
try {
const res = await fetch(url, {cache:"no-store"});
el.innerHTML = await res.text();
} catch (e) {
el.innerHTML = `<div class="text-danger small">No se pudo cargar ${url}</div>`;
}
}
}
// Export util por si querés llamarlo manualmente
window.scLoadPartials = scLoadPartials;
// Eventos genéricos que el sidebar dispara (ajustá a tu lógica real)
window.addEventListener("sc:toggle-abiertas", () => {
// Ej.: togglear checkbox/estado en páginas que lo usen
const chk = document.getElementById("soloAbiertas");
if (chk) { chk.checked = !chk.checked; chk.dispatchEvent(new Event("change")); }
});
window.addEventListener("sc:export-csv", () => {
// Implementá tu export acá
if (window.scExportCsv) return window.scExportCsv();
alert("Exportar CSV: implementame 😄");
});
window.addEventListener("sc:refresh-list", () => {
if (window.scRefreshList) return window.scRefreshList();
location.reload();
});
</script>
@@ -0,0 +1,22 @@
<!-- /partials/_head.html -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= typeof pageTitle !== "undefined" ? pageTitle : "SuiteCoffee" %></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
:root { --navbar-h: 56px; }
body { padding-top: var(--navbar-h); background: #f7f8fb; }
.brand-mini { font-weight: 700; letter-spacing: .2px; }
/* Layout contenedor principal */
main { padding-block: 1rem 2rem; }
/* Tabla compacta */
.table-sm th, .table-sm td { padding: .5rem .6rem; }
/* Chips/etiquetas de estado */
.badge-outline { border: 1px solid #dee2e6; background: #fff; color: #495057; }
.badge-estado-abierta { border-color:#198754; color:#198754; }
.badge-estado-cerrada { border-color:#6c757d; color:#6c757d; }
.badge-estado-anulada { border-color:#dc3545; color:#dc3545; }
.badge-estado-pagada { border-color:#146c43; color:#146c43; }
</style>
@@ -0,0 +1,29 @@
<!-- /partials/_navbar.html -->
<nav class="navbar navbar-expand-lg navbar-light bg-white border-bottom fixed-top">
<div class="container-fluid">
<a class="navbar-brand brand-mini" href="/">SuiteCoffee</a>
<!-- Links principales (colapsables en mobile) -->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#scNav" aria-controls="scNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <!-- hamburguesa principal -->
</button>
<div class="collapse navbar-collapse" id="scNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0 small">
<li class="nav-item"><a class="nav-link" href="/comandas">Comandas</a></li>
<li class="nav-item"><a class="nav-link" href="/estadoComandas">Estado</a></li>
<li class="nav-item"><a class="nav-link" href="/productos">Productos</a></li>
<li class="nav-item"><a class="nav-link" href="/mesas">Mesas</a></li>
<li class="nav-item"><a class="nav-link" href="/reportes">Reportes</a></li>
<!-- agrega las que necesites -->
</ul>
<!-- Botón “hamburguesa” para abrir el menú contextual (sidebar derecha) -->
<button class="btn btn-outline-secondary btn-sm d-flex align-items-center" type="button"
data-bs-toggle="offcanvas" data-bs-target="#scSidebar" aria-controls="scSidebar">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" class="me-1" viewBox="0 0 24 24" fill="none" stroke="currentColor"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
Opciones
</button>
</div>
</div>
</nav>
@@ -0,0 +1,62 @@
<!-- /partials/_sidebar.html -->
<div class="offcanvas offcanvas-end" tabindex="-1" id="scSidebar" aria-labelledby="scSidebarLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="scSidebarLabel">Opciones</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Cerrar"></button>
</div>
<div class="offcanvas-body">
<!-- Contenido se inyecta según la página actual -->
<div id="scSidebarContent" class="list-group list-group-flush small"></div>
</div>
</div>
<script>
// Map de opciones por página. Usa body[data-page] o window.scPageId.
const SC_SIDEBAR_ITEMS = {
// === ejemplos ===
"estadoComandas": [
{ text: " Nueva comanda", href: "/comandas" },
{ text: "Solo abiertas", href: "#", attr: { "data-action": "toggle-abiertas" } },
{ text: "Exportar (CSV)", href: "#", attr: { "data-action": "export-csv" } },
{ text: "Actualizar listado", href: "#", attr: { "data-action": "refresh-list" } },
],
"comandas": [
{ text: "Volver a Estado", href: "/estadoComandas" },
{ text: "Cargar productos", href: "/productos" },
{ text: "Mesas", href: "/mesas" },
],
"productos": [
{ text: "Nuevo producto", href: "/productos/nuevo" },
{ text: "Importar catálogo", href: "/productos/importar" },
{ text: "Reportes", href: "/reportes" },
]
};
(function initSidebar(){
const page = (document.body.dataset.page || window.scPageId || "").trim();
const items = SC_SIDEBAR_ITEMS[page] || [
{ text: "Inicio", href: "/" }
];
const box = document.getElementById("scSidebarContent");
box.innerHTML = "";
for (const it of items) {
const a = document.createElement("a");
a.className = "list-group-item list-group-item-action";
a.textContent = it.text;
a.href = it.href || "#";
if (it.attr) for (const [k,v] of Object.entries(it.attr)) a.setAttribute(k,v);
box.appendChild(a);
}
// Acciones ejemplo (opcionales). Adaptá a tus funciones reales.
box.addEventListener("click", (ev) => {
const a = ev.target.closest("a[data-action]");
if (!a) return;
ev.preventDefault();
const action = a.getAttribute("data-action");
if (action === "toggle-abiertas") window.dispatchEvent(new CustomEvent("sc:toggle-abiertas"));
if (action === "export-csv") window.dispatchEvent(new CustomEvent("sc:export-csv"));
if (action === "refresh-list") window.dispatchEvent(new CustomEvent("sc:refresh-list"));
});
})();
</script>