Files
ap_pos/ap-pos/index.html
Marco Gallegos bb0ca83820 feat: Implementar roles de usuario y gestión de administradores
Se introduce un sistema completo de roles de usuario (admin, user) para controlar el acceso a las diferentes funcionalidades de la aplicación.

Funcionalidades y Cambios:
- Se añade la columna 'role' a la tabla de usuarios en la base de datos.
- El login ahora devuelve el rol del usuario y la sesión lo almacena.
- El Dashboard y la pestaña de Configuración ahora solo son visibles para los administradores.
- Los administradores tienen una nueva sección en "Configuración" para añadir y eliminar otros usuarios.
- Se implementan endpoints de API seguros ('/api/users') para la gestión de usuarios, accesibles solo por administradores.
- Se corrige un error que impedía la navegación entre pestañas y la interactividad general.
- Se soluciona un error de renderizado del gráfico del dashboard que causaba una sensación de "bucle".
- Se actualiza el README con instrucciones detalladas de instalación, uso y despliegue con Docker.
- Se añaden archivos Dockerfile y .dockerignore para la contenerización.
2025-08-13 07:06:35 -06:00

250 lines
9.4 KiB
HTML

<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Ale Ponce | AlMa</title>
<link rel="icon" href="data:,">
<link rel="stylesheet" href="styles.css?v=1.1" />
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<main class="container">
<header class="main-header">
<!-- Logo del negocio en lugar de texto -->
<img src="src/logo.png" alt="Ale Ponce" class="header-logo">
<nav class="tabs">
<button type="button" class="tab-link" data-tab="tab-dashboard">Dashboard</button>
<button type="button" class="tab-link" data-tab="tab-movements">Recibos</button>
<button type="button" class="tab-link" data-tab="tab-clients">Clientes</button>
<button type="button" class="tab-link" data-tab="tab-settings">Configuración</button>
</nav>
<button type="button" id="btnLogout" class="btn-danger">Cerrar Sesión</button>
</header>
<!-- Pestaña de Dashboard -->
<div id="tab-dashboard" class="tab-content">
<div class="section">
<h2>Dashboard</h2>
<div class="dashboard-stats">
<div class="stat-card">
<h3>Ingresos Totales</h3>
<p id="stat-total-income">$0.00</p>
</div>
<div class="stat-card">
<h3>Servicios Realizados</h3>
<p id="stat-total-movements">0</p>
</div>
</div>
<div class="dashboard-chart">
<h3>Ingresos por Servicio</h3>
<canvas id="incomeChart"></canvas>
</div>
</div>
</div>
<!-- Pestaña de Movimientos/Recibos -->
<div id="tab-movements" class="tab-content">
<div class="section">
<h2>Nuevo Movimiento</h2>
<form id="formMove">
<div class="form-grid">
<label>Cliente:</label>
<div>
<input type="text" id="m-cliente" list="client-list" required autocomplete="off" />
<datalist id="client-list"></datalist>
</div>
<label>Servicio:</label>
<select id="m-tipo" required>
<option value="Microblading">Microblading</option>
<option value="Lashes">Lashes</option>
<option value="Nail Art">Nail Art</option>
<option value="Pago">Pago</option>
</select>
<label>Fecha de Cita:</label>
<input type="date" id="m-fecha-cita" />
<label>Hora de Cita:</label>
<input type="time" id="m-hora-cita" />
<label>Monto (MXN):</label><input type="number" id="m-monto" step="0.01" min="0" required />
<label>Método:</label>
<select id="m-metodo">
<option value="">-- Opcional --</option>
<option value="Efectivo">Efectivo</option>
<option value="Tarjeta">Tarjeta</option>
<option value="Transferencia">Transferencia</option>
<option value="Depósito">Depósito</option>
<option value="Otros">Otros</option>
</select>
<label>Concepto:</label><input type="text" id="m-concepto" />
<label>Atendió:</label><input type="text" id="m-staff" />
<label>Notas:</label><textarea id="m-notas" rows="2"></textarea>
</div>
<div class="form-actions">
<button type="submit">Guardar y Generar Recibo</button>
<button type="reset" class="btn-danger">Limpiar</button>
</div>
</form>
</div>
<div class="section">
<h2>Movimientos Recientes</h2>
<button id="btnExport" class="btn-secondary">Exportar a CSV</button>
<div class="table-wrapper">
<table id="tblMoves">
<thead>
<tr>
<th>Folio</th>
<th>Fecha</th>
<th>Cita</th>
<th>Cliente</th>
<th>Servicio</th>
<th>Monto</th>
<th>Acciones</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<!-- Pestaña de Clientes -->
<div id="tab-clients" class="tab-content">
<div class="section">
<h2>Administrar Clientes</h2>
<form id="formClient">
<input type="hidden" id="c-id" />
<div class="form-grid client-grid">
<label>Nombre:</label>
<input type="text" id="c-nombre" required />
<label>Teléfono:</label>
<input type="tel" id="c-telefono" />
<label>Cumpleaños:</label>
<input type="date" id="c-cumple" />
<label></label> <!-- Etiqueta vacía para alinear el checkbox -->
<div class="checkbox-container">
<input type="checkbox" id="c-consent" />
<label for="c-consent">Consentimiento médico</label>
</div>
</div>
<div class="form-actions">
<button type="submit">Guardar Cliente</button>
<button type="reset" id="btnCancelEditClient" class="btn-danger">Limpiar</button>
</div>
</form>
</div>
<div class="section">
<h2>Lista de Clientes</h2>
<div class="table-wrapper">
<table id="tblClients">
<thead>
<tr>
<th>Nombre</th>
<th>Teléfono</th>
<th>Cumpleaños</th>
<th>Consentimiento</th>
<th>Acciones</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<!-- Pestaña de Configuración -->
<div id="tab-settings" class="tab-content">
<div class="section">
<h2>Configuración del Negocio</h2>
<form id="formSettings">
<div class="form-grid">
<label>Nombre del negocio:</label><input type="text" id="s-negocio" required />
<label>Eslogan (opcional):</label><input type="text" id="s-tagline" />
<label>Calle y Número:</label><input type="text" id="s-calle" placeholder="Ej: Av. Siempre Viva 123" />
<label>Colonia y C.P.:</label><input type="text" id="s-colonia-cp" placeholder="Ej: Centro, 25000" />
<label>Teléfono:</label><input type="text" id="s-tel" />
<label>RFC:</label><input type="text" id="s-rfc" />
<label>Leyenda pie de ticket:</label><input type="text" id="s-leyenda" />
<label>Prefijo de folio:</label><input type="text"id="s-folioPrefix" />
</div>
<div class="form-actions">
<button type="submit">Guardar Configuración</button>
<button type="button" id="btnTestTicket" class="btn-secondary">Probar Ticket</button>
</div>
</form>
</div>
<div class="section">
<h2>Ubicación de los Datos</h2>
<p class="data-location-info">
Toda la información de tu negocio (clientes, recibos y configuración) se guarda de forma segura en el archivo <strong>ap-pos.db</strong>, ubicado en la misma carpeta que la aplicación. Para hacer un respaldo, simplemente copia este archivo.
</p>
</div>
<div class="section" id="credentials-section">
<h2>Mis Credenciales</h2>
<form id="formCredentials">
<div class="form-grid">
<label>Mi Usuario:</label>
<input type="text" id="s-username" required />
<label>Nueva Contraseña:</label>
<input type="password" id="s-password" placeholder="Dejar en blanco para no cambiar" />
</div>
<div class="form-actions">
<button type="submit">Guardar Credenciales</button>
</div>
</form>
</div>
<!-- Sección de Gestión de Usuarios (Solo para Admins) -->
<div class="section" id="user-management-section" style="display: none;">
<h2>Gestión de Usuarios</h2>
<div class="sub-section">
<h3>Añadir Nuevo Usuario</h3>
<form id="formAddUser">
<div class="form-grid">
<label>Nuevo Usuario:</label>
<input type="text" id="u-username" required />
<label>Contraseña:</label>
<input type="password" id="u-password" required />
<label>Rol:</label>
<select id="u-role">
<option value="user">Usuario (Ventas)</option>
<option value="admin">Administrador</option>
</select>
</div>
<div class="form-actions">
<button type="submit">Crear Usuario</button>
</div>
</form>
</div>
<div class="sub-section">
<h3>Usuarios Existentes</h3>
<div class="table-wrapper">
<table id="tblUsers">
<thead>
<tr>
<th>Usuario</th>
<th>Rol</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<!-- Los usuarios se insertarán aquí -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
<footer class="main-footer-credits">
<p>v0.2.1</p>
<p>Autores: Gemini + Marco G.</p>
</footer>
<!-- Área de impresión oculta -->
<div id="printArea" class="no-print"></div>
<script type="module" src="app.js?v=1.3"></script>
</body>
</html>