@@ -876,18 +874,15 @@ function handleTableClick(e) {
async function handleClientForm(e) {
e.preventDefault();
await saveClient();
- // Después de guardar, cambiar a la pestaña de consulta
activateClientSubTab('sub-tab-consult');
}
function activateClientSubTab(subTabId) {
if (!subTabId) return;
- // Desactivar todas las sub-pestañas y contenidos de clientes
document.querySelectorAll('#tab-clients .sub-tab-link').forEach(tab => tab.classList.remove('active'));
document.querySelectorAll('#tab-clients .sub-tab-content').forEach(content => content.classList.remove('active'));
- // Activar la sub-pestaña y el contenido correctos
const tabButton = document.querySelector(`[data-subtab="${subTabId}"]`);
const tabContent = document.getElementById(subTabId);
@@ -910,11 +905,9 @@ function handleClientTabChange(e) {
function activateTab(tabId) {
if (!tabId) return;
- // Desactivar todas las pestañas y contenidos
document.querySelectorAll('.tab-link').forEach(tab => tab.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
- // Activar la pestaña y el contenido correctos
const tabButton = document.querySelector(`[data-tab="${tabId}"]`);
const tabContent = document.getElementById(tabId);
@@ -925,9 +918,7 @@ function activateTab(tabId) {
tabContent.classList.add('active');
}
- // Cargar datos dinámicos si es la pestaña del dashboard
if (tabId === 'tab-dashboard' && currentUser.role === 'admin') {
- // Si es la primera vez que se visita la pestaña, inicializar el gráfico
if (!incomeChart) {
const ctx = document.getElementById('incomeChart').getContext('2d');
incomeChart = new Chart(ctx, {
@@ -966,7 +957,6 @@ function activateTab(tabId) {
}
});
}
- // Cargar (o recargar) los datos del dashboard
loadDashboardData();
}
}
@@ -975,13 +965,11 @@ function handleTabChange(e) {
const tabButton = e.target.closest('.tab-link');
if (!tabButton) return;
- // Solo prevenir el comportamiento por defecto si es un botón para cambiar de pestaña
if (tabButton.dataset.tab) {
e.preventDefault();
const tabId = tabButton.dataset.tab;
activateTab(tabId);
}
- // Si no tiene data-tab (es un enlace normal como el de Clientes), no hacer nada y permitir la navegación.
}
function handleTestTicket() {
@@ -1043,7 +1031,7 @@ function populateFooter() {
const versionElement = document.getElementById('footer-version');
if (dateElement) {
- dateElement.textContent = new Date().toLocaleDateString('es-MX', { year: 'numeric', month: 'long', day: 'numeric' });
+ dateElement.textContent = formatDate(new Date().toISOString());
}
if (versionElement) {
versionElement.textContent = `Versión ${APP_VERSION}`;
@@ -1054,17 +1042,14 @@ function populateFooter() {
// --- INICIALIZACIÓN ---
async function initializeApp() {
- // 1. Verificar autenticación y obtener datos del usuario.
let userResponse;
try {
userResponse = await fetch('/api/user');
if (!userResponse.ok) {
- // Si la respuesta no es 2xx, el usuario no está autenticado o hay un error.
window.location.href = '/login.html';
return;
}
- // Verificar que la respuesta sea JSON antes de procesarla.
const contentType = userResponse.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
console.error('La respuesta del servidor no es JSON. Redirigiendo al login.');
@@ -1072,17 +1057,14 @@ async function initializeApp() {
return;
}
- // 2. Procesar datos del usuario.
currentUser = await userResponse.json();
} catch (error) {
- // Si hay un error de red, es probable que el servidor no esté corriendo.
console.error('Error de conexión al verificar la autenticación. Redirigiendo al login.', error);
window.location.href = '/login.html';
return;
}
- // 3. Añadir manejadores de eventos.
const tabs = document.querySelector('.tabs');
const btnLogout = document.getElementById('btnLogout');
const btnCancelEditUser = document.getElementById('btnCancelEditUser');
@@ -1193,7 +1175,6 @@ async function initializeApp() {
showAddCourseModal(clientId);
});
- // 4. Cargar el resto de los datos de la aplicación.
Promise.all([
load(KEY_SETTINGS, DEFAULT_SETTINGS),
load(KEY_DATA, []),
@@ -1213,7 +1194,6 @@ async function initializeApp() {
renderProductTables();
console.log('Updating client datalist...');
updateClientDatalist();
- // Initial population of the articulo dropdown
populateArticuloDropdown(document.getElementById('m-categoria').value);
if (currentUser) {
@@ -1247,5 +1227,4 @@ async function initializeApp() {
});
}
-
document.addEventListener('DOMContentLoaded', initializeApp);
\ No newline at end of file
diff --git a/clients.js b/clients.js
index cefc315..eb03e1b 100644
--- a/clients.js
+++ b/clients.js
@@ -2,6 +2,18 @@ import { load, save, remove, KEY_CLIENTS } from './storage.js';
let clients = [];
+// --- UTILITIES ---
+function formatDate(dateString) {
+ if (!dateString) return '';
+ const date = new Date(dateString);
+ const userTimezoneOffset = date.getTimezoneOffset() * 60000;
+ const adjustedDate = new Date(date.getTime() + userTimezoneOffset);
+ const day = String(adjustedDate.getDate()).padStart(2, '0');
+ const month = String(adjustedDate.getMonth() + 1).padStart(2, '0');
+ const year = adjustedDate.getFullYear();
+ return `${day}/${month}/${year}`;
+}
+
// --- DOM ELEMENTS ---
const formClient = document.getElementById('formClient');
const tblClientsBody = document.getElementById('tblClients')?.querySelector('tbody');
@@ -64,11 +76,10 @@ async function deleteClient(id) {
function exportClientHistoryCSV(client, history) {
const headers = 'Folio,Fecha,Servicio,Monto';
const rows = history.map(mov => {
- const fecha = new Date(mov.fechaISO).toLocaleDateString('es-MX');
const servicio = mov.subtipo ? `${mov.tipo} (${mov.subtipo})` : mov.tipo;
return [
mov.folio,
- fecha,
+ formatDate(mov.fechaISO),
`"${servicio}"`, // Corrected: escaped inner quotes for CSV compatibility
Number(mov.monto).toFixed(2)
].join(',');
@@ -101,19 +112,18 @@ async function toggleClientHistory(row, client) {
historyRow.id = historyRowId;
historyRow.className = 'client-history-row';
- let historyHtml = '
+ let historyHtml = `