+ vehiclesEl.innerHTML = vehicles.map(v =>
+ `
`
).join('');
}
+ }
- // Recent purchases
+ // Purchases
+ const purchasesEl = document.getElementById('panelPurchases');
+ if (purchasesEl) {
const purchases = c.recent_purchases || [];
if (purchases.length === 0) {
- document.getElementById('detailPurchases').innerHTML = '
';
} else {
- document.getElementById('detailPurchases').innerHTML = purchases.map(p =>
- `
-
Venta #${p.id} - ${new Date(p.created_at).toLocaleDateString('es-MX')}
+ purchasesEl.innerHTML = purchases.slice(0, 5).map(p =>
+ `
+ NX-${String(p.id).padStart(5, '0')} — ${formatDate(p.created_at)}
${fmt(p.total)}
`
).join('');
}
-
- document.getElementById('detailPanel').classList.add('active');
- } catch (e) {
- alert('Error: ' + e.message);
}
}
function closeDetail() {
- document.getElementById('detailPanel').classList.remove('active');
currentCustomer = null;
+ const emptyEl = document.getElementById('detailEmpty');
+ const contentEl = document.getElementById('detailContent');
+ if (emptyEl) emptyEl.style.display = 'flex';
+ if (contentEl) contentEl.style.display = 'none';
+ loadCustomers(currentPage);
+ }
+
+ // Wire action buttons in detail panel
+ function wireActionButtons() {
+ const btns = document.querySelectorAll('.quick-actions .action-btn');
+ // Order: Nueva Venta, Editar, Estado de Cuenta, Historial
+ if (btns.length >= 1) btns[0].onclick = () => {
+ if (currentCustomer) window.location.href = '/pos/?customer=' + currentCustomer.id;
+ };
+ if (btns.length >= 2) btns[1].onclick = () => editCurrent();
+ if (btns.length >= 3) btns[2].onclick = () => showStatement();
+ if (btns.length >= 4) btns[3].onclick = () => {
+ if (currentCustomer) selectCustomer(currentCustomer.id);
+ };
}
// ─── Create/Edit Modal ───────────────
function showCreateModal() {
+ const modal = document.getElementById('customerModal');
+ if (!modal) return;
document.getElementById('modalTitle').textContent = 'Nuevo Cliente';
document.getElementById('editId').value = '';
document.getElementById('fName').value = '';
@@ -186,13 +381,15 @@ const Customers = (() => {
document.getElementById('fAddress').value = '';
document.getElementById('fPriceTier').value = '1';
document.getElementById('fCreditLimit').value = '0';
- document.getElementById('customerModal').classList.add('active');
+ modal.classList.add('active');
document.getElementById('fName').focus();
}
function editCurrent() {
if (!currentCustomer) return;
const c = currentCustomer;
+ const modal = document.getElementById('customerModal');
+ if (!modal) return;
document.getElementById('modalTitle').textContent = 'Editar Cliente';
document.getElementById('editId').value = c.id;
document.getElementById('fName').value = c.name || '';
@@ -206,32 +403,36 @@ const Customers = (() => {
document.getElementById('fAddress').value = c.address || '';
document.getElementById('fPriceTier').value = c.price_tier || '1';
document.getElementById('fCreditLimit').value = c.credit_limit || 0;
- document.getElementById('customerModal').classList.add('active');
+ modal.classList.add('active');
}
function closeModal() {
- document.getElementById('customerModal').classList.remove('active');
+ const modal = document.getElementById('customerModal');
+ if (modal) modal.classList.remove('active');
}
async function save() {
- const name = document.getElementById('fName').value.trim();
+ const nameEl = document.getElementById('fName');
+ const name = nameEl ? nameEl.value.trim() : '';
if (!name) { alert('Nombre es requerido'); return; }
+ const val = (id) => { const el = document.getElementById(id); return el ? el.value.trim() : ''; };
+
const body = {
name: name,
- rfc: document.getElementById('fRfc').value.trim() || null,
- razon_social: document.getElementById('fRazonSocial').value.trim() || null,
- regimen_fiscal: document.getElementById('fRegimenFiscal').value || null,
- uso_cfdi: document.getElementById('fUsoCfdi').value || 'G03',
- cp: document.getElementById('fCp').value.trim() || null,
- phone: document.getElementById('fPhone').value.trim() || null,
- email: document.getElementById('fEmail').value.trim() || null,
- address: document.getElementById('fAddress').value.trim() || null,
- price_tier: parseInt(document.getElementById('fPriceTier').value) || 1,
- credit_limit: parseFloat(document.getElementById('fCreditLimit').value) || 0,
+ rfc: val('fRfc') || null,
+ razon_social: val('fRazonSocial') || null,
+ regimen_fiscal: val('fRegimenFiscal') || null,
+ uso_cfdi: val('fUsoCfdi') || 'G03',
+ cp: val('fCp') || null,
+ phone: val('fPhone') || null,
+ email: val('fEmail') || null,
+ address: val('fAddress') || null,
+ price_tier: parseInt(val('fPriceTier')) || 1,
+ credit_limit: parseFloat(val('fCreditLimit')) || 0,
};
- const editId = document.getElementById('editId').value;
+ const editId = val('editId');
try {
if (editId) {
@@ -249,37 +450,43 @@ const Customers = (() => {
closeModal();
loadCustomers();
if (editId && currentCustomer) {
- showDetail(editId);
+ selectCustomer(editId);
}
} catch (e) {
alert('Error: ' + e.message);
}
}
- // ─── Statement ───────────────────────
+ // ─── Statement Modal ─────────────────
async function showStatement() {
if (!currentCustomer) return;
- document.getElementById('statementName').textContent = currentCustomer.name;
+ const modal = document.getElementById('statementModal');
+ if (!modal) return;
+ const nameEl = document.getElementById('statementName');
+ if (nameEl) nameEl.textContent = currentCustomer.name;
+
+ const content = document.getElementById('statementContent');
+ if (content) content.innerHTML = '
Cargando...
';
+ modal.classList.add('active');
try {
const data = await api(`/pos/api/customers/${currentCustomer.id}/statement`);
let html = `
Saldo actual: ${fmt(data.balance)} |
- Limite: ${fmt(data.customer.credit_limit)}
+ Limite: ${fmt(data.customer ? data.customer.credit_limit : 0)}
`;
- if (data.entries.length === 0) {
- html += '
Sin movimientos
';
+ if (!data.entries || data.entries.length === 0) {
+ html += '
Sin movimientos
';
} else {
html += '
';
- html += '| Fecha | Concepto | Cargo | Abono | Saldo |
';
+ html += '| Fecha | Concepto | Cargo | Abono | Saldo |
';
data.entries.forEach(e => {
- const dateStr = new Date(e.date).toLocaleDateString('es-MX');
- html += `
- | ${dateStr} |
- ${e.description} |
+ html += `
+ | ${formatDate(e.date)} |
+ ${e.description || ''} |
${e.type === 'charge' ? fmt(e.amount) : ''} |
${e.type === 'payment' ? fmt(e.amount) : ''} |
${fmt(e.running_balance)} |
@@ -288,20 +495,280 @@ const Customers = (() => {
html += '
';
}
- document.getElementById('statementContent').innerHTML = html;
- document.getElementById('statementModal').classList.add('active');
+ if (content) content.innerHTML = html;
+ } catch (e) {
+ if (content) content.innerHTML = `
Error: ${e.message}
`;
+ }
+ }
+
+ function closeStatement() {
+ const modal = document.getElementById('statementModal');
+ if (modal) modal.classList.remove('active');
+ }
+
+ // ─── Payment Modal ───────────────────
+ function showPaymentModal() {
+ if (!currentCustomer) return;
+ const modal = document.getElementById('paymentModal');
+ if (!modal) return;
+ document.getElementById('paymentCustomerName').textContent = currentCustomer.name;
+ document.getElementById('paymentAmount').value = '';
+ document.getElementById('paymentMethod').value = 'cash';
+ document.getElementById('paymentRef').value = '';
+ modal.classList.add('active');
+ document.getElementById('paymentAmount').focus();
+ }
+
+ function closePayment() {
+ const modal = document.getElementById('paymentModal');
+ if (modal) modal.classList.remove('active');
+ }
+
+ async function recordPayment() {
+ if (!currentCustomer) return;
+ const amount = parseFloat(document.getElementById('paymentAmount').value);
+ if (!amount || amount <= 0) { alert('Ingresa un monto valido'); return; }
+
+ const method = document.getElementById('paymentMethod').value;
+ const reference = document.getElementById('paymentRef').value.trim();
+
+ try {
+ await api(`/pos/api/customers/${currentCustomer.id}/payment`, {
+ method: 'POST',
+ body: JSON.stringify({ amount, method, reference }),
+ });
+ closePayment();
+ selectCustomer(currentCustomer.id);
} catch (e) {
alert('Error: ' + e.message);
}
}
+ // ─── Slide Panel (from HTML) ─────────
+ // The HTML already has openSlidePanel/closeSlidePanel. We hook showDetail into it.
+ function showDetail(id) {
+ selectCustomer(id);
+ if (typeof openSlidePanel === 'function') openSlidePanel();
+ }
+
// ─── Init ────────────────────────────
- loadCustomers();
+ function init() {
+ // Auth check
+ if (!token) {
+ window.location.href = '/pos/login';
+ return;
+ }
+
+ // Wire the "Nuevo Cliente" button from the page header
+ window.openNewCustomerModal = showCreateModal;
+
+ // Wire action buttons in detail panel
+ wireActionButtons();
+
+ // Wire the inline HTML viewCustomer to our showDetail
+ window.viewCustomer = showDetail;
+
+ // Inject modals if not present
+ injectModals();
+
+ // Load data
+ loadCustomers();
+ loadSummary();
+ }
+
+ function injectModals() {
+ // Customer Create/Edit Modal
+ if (!document.getElementById('customerModal')) {
+ const div = document.createElement('div');
+ div.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
+ document.body.appendChild(div);
+ }
+
+ // Statement Modal
+ if (!document.getElementById('statementModal')) {
+ const div = document.createElement('div');
+ div.innerHTML = `
+
`;
+ document.body.appendChild(div);
+ }
+
+ // Payment Modal
+ if (!document.getElementById('paymentModal')) {
+ const div = document.createElement('div');
+ div.innerHTML = `
+
+
+
+
+
Cliente:
+
+
+
+
+
+
`;
+ document.body.appendChild(div);
+ }
+
+ // Inject modal styles
+ if (!document.getElementById('modalStyles')) {
+ const style = document.createElement('style');
+ style.id = 'modalStyles';
+ style.textContent = `
+ .modal-overlay {
+ position: fixed; top: 0; left: 0; right: 0; bottom: 0;
+ background: rgba(0,0,0,0.5); z-index: 9000;
+ display: flex; align-items: center; justify-content: center;
+ backdrop-filter: blur(2px);
+ }
+ .modal-overlay.active { display: flex !important; }
+ .modal-box {
+ background: var(--color-bg-elevated); border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg); width: 90%; max-width: 600px;
+ max-height: 85vh; overflow-y: auto;
+ box-shadow: var(--shadow-xl);
+ }
+ .modal-box--wide { max-width: 800px; }
+ .modal-header {
+ display: flex; align-items: center; justify-content: space-between;
+ padding: var(--space-4) var(--space-5);
+ border-bottom: 1px solid var(--color-border);
+ }
+ .modal-header h3 {
+ font-family: var(--font-heading); font-size: var(--text-h6);
+ font-weight: var(--heading-weight-primary); color: var(--color-text-primary);
+ letter-spacing: var(--tracking-wide); text-transform: uppercase;
+ }
+ .modal-body { padding: var(--space-5); }
+ .modal-footer {
+ display: flex; justify-content: flex-end; gap: var(--space-3);
+ padding: var(--space-4) var(--space-5);
+ border-top: 1px solid var(--color-border);
+ }
+ .form-row { display: flex; gap: var(--space-4); margin-bottom: var(--space-4); }
+ .form-row > .form-group { flex: 1; }
+ .form-group { margin-bottom: var(--space-4); }
+ .form-group label {
+ display: block; font-size: var(--text-caption); font-weight: var(--font-weight-semibold);
+ color: var(--color-text-muted); text-transform: uppercase; letter-spacing: var(--tracking-wider);
+ margin-bottom: var(--space-1);
+ }
+ .form-input {
+ width: 100%; height: 38px; padding: 0 var(--space-3);
+ background: var(--color-bg-overlay); border: 1.5px solid var(--color-border);
+ border-radius: var(--radius-md); font-family: var(--font-body);
+ font-size: var(--text-body-sm); color: var(--color-text-primary); outline: none;
+ transition: var(--transition-fast);
+ }
+ .form-input:focus { border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }
+ select.form-input { cursor: pointer; }
+ `;
+ document.head.appendChild(style);
+ }
+ }
+
+ // Run init
+ init();
return {
search, goToPage, loadCustomers,
- showDetail, closeDetail,
+ showDetail, selectCustomer, closeDetail,
showCreateModal, editCurrent, closeModal, save,
- showStatement,
+ showStatement, closeStatement,
+ showPaymentModal, closePayment, recordPayment,
};
})();
diff --git a/pos/templates/customers.html b/pos/templates/customers.html
index adb79f2..701b0c0 100644
--- a/pos/templates/customers.html
+++ b/pos/templates/customers.html
@@ -2044,428 +2044,7 @@
}
/* ------------------------------------------------------------------
- SAMPLE DATA
- ------------------------------------------------------------------ */
- const customers = [
- {
- id: 1, num: '00001',
- name: 'Taller Star Automotriz', initials: 'TS',
- rfc: 'TSA820115HDF', phone: '55 1234-5678', email: 'ventas@tallerstar.mx',
- tipo: 'Taller', credit: 48500, creditLimit: 80000,
- lastPurchase: '28 Mar 2026', estado: 'Activo',
- address: 'Av. Insurgentes Sur 1602, Col. Crédito Constructor, CDMX',
- since: '14 Mar 2019',
- history: [
- { date: '28 Mar 2026', folio: 'NX-20498', total: '$4,820.00', status: 'Pagado' },
- { date: '22 Mar 2026', folio: 'NX-20341', total: '$1,250.50', status: 'Pagado' },
- { date: '15 Mar 2026', folio: 'NX-20188', total: '$9,670.00', status: 'Pendiente' },
- { date: '07 Mar 2026', folio: 'NX-19982', total: '$3,100.00', status: 'Pagado' },
- { date: '28 Feb 2026', folio: 'NX-19740', total: '$6,450.00', status: 'Pagado' },
- { date: '14 Feb 2026', folio: 'NX-19510', total: '$2,890.00', status: 'Vencido' },
- { date: '05 Feb 2026', folio: 'NX-19301', total: '$5,220.00', status: 'Pagado' },
- { date: '25 Ene 2026', folio: 'NX-19050', total: '$780.00', status: 'Pagado' },
- { date: '18 Ene 2026', folio: 'NX-18890', total: '$11,340.00', status: 'Pagado' },
- { date: '10 Ene 2026', folio: 'NX-18720', total: '$4,100.00', status: 'Pagado' },
- ]
- },
- {
- id: 2, num: '00002',
- name: 'Refacciones El Trueno', initials: 'RT',
- rfc: 'RTE930720HDF', phone: '55 9876-5432', email: 'compras@eltrueno.com',
- tipo: 'Mayoreo', credit: 120000, creditLimit: 200000,
- lastPurchase: '27 Mar 2026', estado: 'Activo',
- address: 'Blvd. Adolfo López Mateos 2855, Álvaro Obregón, CDMX',
- since: '02 Jun 2017',
- history: [
- { date: '27 Mar 2026', folio: 'NX-20481', total: '$28,500.00', status: 'Pendiente' },
- { date: '20 Mar 2026', folio: 'NX-20290', total: '$15,670.00', status: 'Pagado' },
- { date: '12 Mar 2026', folio: 'NX-20110', total: '$42,300.00', status: 'Pagado' },
- { date: '04 Mar 2026', folio: 'NX-19930', total: '$8,900.00', status: 'Pagado' },
- { date: '25 Feb 2026', folio: 'NX-19720', total: '$33,100.00', status: 'Pagado' },
- { date: '16 Feb 2026', folio: 'NX-19480', total: '$21,450.00', status: 'Pagado' },
- { date: '07 Feb 2026', folio: 'NX-19240', total: '$9,800.00', status: 'Pagado' },
- { date: '28 Ene 2026', folio: 'NX-19010', total: '$47,200.00', status: 'Pagado' },
- { date: '19 Ene 2026', folio: 'NX-18840', total: '$18,660.00', status: 'Pagado' },
- { date: '10 Ene 2026', folio: 'NX-18650', total: '$12,400.00', status: 'Pagado' },
- ]
- },
- {
- id: 3, num: '00003',
- name: 'Karla Mendoza Jiménez', initials: 'KM',
- rfc: 'MEJK891004MDF', phone: '55 5551-2233', email: 'karla.m@gmail.com',
- tipo: 'Mostrador', credit: 0, creditLimit: 5000,
- lastPurchase: '25 Mar 2026', estado: 'Activo',
- address: 'Calle Fresno 44, Col. San Marcos, Xochimilco, CDMX',
- since: '10 Ene 2023',
- history: [
- { date: '25 Mar 2026', folio: 'NX-20445', total: '$340.00', status: 'Pagado' },
- { date: '10 Mar 2026', folio: 'NX-20080', total: '$780.00', status: 'Pagado' },
- { date: '18 Feb 2026', folio: 'NX-19560', total: '$210.00', status: 'Pagado' },
- { date: '05 Feb 2026', folio: 'NX-19290', total: '$1,200.00',status: 'Pagado' },
- { date: '20 Ene 2026', folio: 'NX-18920', total: '$450.00', status: 'Pagado' },
- { date: '03 Ene 2026', folio: 'NX-18600', total: '$890.00', status: 'Pagado' },
- { date: '15 Dic 2025', folio: 'NX-18100', total: '$620.00', status: 'Pagado' },
- { date: '01 Dic 2025', folio: 'NX-17890', total: '$330.00', status: 'Pagado' },
- { date: '18 Nov 2025', folio: 'NX-17620', total: '$1,100.00',status: 'Pagado' },
- { date: '05 Nov 2025', folio: 'NX-17390', total: '$780.00', status: 'Pagado' },
- ]
- },
- {
- id: 4, num: '00004',
- name: 'Distribuidora Central Norte', initials: 'DC',
- rfc: 'DCN760315B24', phone: '55 8888-0011', email: 'pedidos@cnorte.mx',
- tipo: 'Mayoreo', credit: 65000, creditLimit: 300000,
- lastPurchase: '26 Mar 2026', estado: 'Mora',
- address: 'Calz. de los Misterios 1420, Gustavo A. Madero, CDMX',
- since: '30 Ago 2014',
- history: [
- { date: '26 Mar 2026', folio: 'NX-20470', total: '$55,200.00', status: 'Pendiente' },
- { date: '15 Mar 2026', folio: 'NX-20140', total: '$38,900.00', status: 'Vencido' },
- { date: '05 Mar 2026', folio: 'NX-19960', total: '$22,100.00', status: 'Vencido' },
- { date: '18 Feb 2026', folio: 'NX-19500', total: '$41,700.00', status: 'Pagado' },
- { date: '06 Feb 2026', folio: 'NX-19270', total: '$29,800.00', status: 'Pagado' },
- { date: '25 Ene 2026', folio: 'NX-19040', total: '$63,400.00', status: 'Pagado' },
- { date: '14 Ene 2026', folio: 'NX-18810', total: '$17,600.00', status: 'Pagado' },
- { date: '03 Ene 2026', folio: 'NX-18580', total: '$34,900.00', status: 'Pagado' },
- { date: '22 Dic 2025', folio: 'NX-18200', total: '$48,100.00', status: 'Pagado' },
- { date: '10 Dic 2025', folio: 'NX-17980', total: '$26,300.00', status: 'Pagado' },
- ]
- },
- {
- id: 5, num: '00005',
- name: 'Taller Rápido Orozco', initials: 'TR',
- rfc: 'ORCH751122HMC', phone: '55 7744-3322', email: 'rapidorozco@outlook.com',
- tipo: 'Taller', credit: 18000, creditLimit: 25000,
- lastPurchase: '24 Mar 2026', estado: 'Activo',
- address: 'Calle Morelos 88, Col. Centro, Naucalpan, Edo. Méx.',
- since: '05 May 2021',
- history: [
- { date: '24 Mar 2026', folio: 'NX-20430', total: '$3,450.00', status: 'Pagado' },
- { date: '17 Mar 2026', folio: 'NX-20210', total: '$1,880.00', status: 'Pagado' },
- { date: '09 Mar 2026', folio: 'NX-20040', total: '$6,100.00', status: 'Pendiente' },
- { date: '01 Mar 2026', folio: 'NX-19870', total: '$2,200.00', status: 'Pagado' },
- { date: '22 Feb 2026', folio: 'NX-19660', total: '$4,760.00', status: 'Pagado' },
- { date: '12 Feb 2026', folio: 'NX-19420', total: '$920.00', status: 'Pagado' },
- { date: '02 Feb 2026', folio: 'NX-19200', total: '$3,340.00', status: 'Pagado' },
- { date: '24 Ene 2026', folio: 'NX-18990', total: '$1,560.00', status: 'Pagado' },
- { date: '15 Ene 2026', folio: 'NX-18800', total: '$5,280.00', status: 'Pagado' },
- { date: '06 Ene 2026', folio: 'NX-18610', total: '$2,990.00', status: 'Pagado' },
- ]
- },
- {
- id: 6, num: '00006',
- name: 'Servicios Automotrices Luna', initials: 'SL',
- rfc: 'SAL880903HDF', phone: '55 3311-7766', email: 'luna.serv@yahoo.com',
- tipo: 'Taller', credit: 4500, creditLimit: 15000,
- lastPurchase: '23 Mar 2026', estado: 'Activo',
- address: 'Av. Taxqueña 1201, Coyoacán, CDMX',
- since: '12 Sep 2020',
- history: [
- { date: '23 Mar 2026', folio: 'NX-20410', total: '$2,100.00', status: 'Pagado' },
- { date: '14 Mar 2026', folio: 'NX-20170', total: '$880.00', status: 'Pagado' },
- { date: '06 Mar 2026', folio: 'NX-19990', total: '$3,650.00', status: 'Pagado' },
- { date: '25 Feb 2026', folio: 'NX-19730', total: '$1,220.00', status: 'Pagado' },
- { date: '15 Feb 2026', folio: 'NX-19490', total: '$4,400.00', status: 'Pagado' },
- { date: '05 Feb 2026', folio: 'NX-19260', total: '$760.00', status: 'Pagado' },
- { date: '26 Ene 2026', folio: 'NX-19030', total: '$2,980.00', status: 'Pagado' },
- { date: '16 Ene 2026', folio: 'NX-18820', total: '$1,450.00', status: 'Pagado' },
- { date: '07 Ene 2026', folio: 'NX-18630', total: '$3,100.00', status: 'Pagado' },
- { date: '28 Dic 2025', folio: 'NX-18240', total: '$590.00', status: 'Pagado' },
- ]
- },
- {
- id: 7, num: '00007',
- name: 'Autoservicio El Piston', initials: 'AP',
- rfc: 'AEP920601HDF', phone: '55 2299-1144', email: 'elpiston@hotmail.com',
- tipo: 'Mostrador', credit: 3200, creditLimit: 10000,
- lastPurchase: '21 Mar 2026', estado: 'Inactivo',
- address: 'Calle Hidalgo 557, Tlalnepantla, Edo. Méx.',
- since: '20 Feb 2022',
- history: [
- { date: '21 Mar 2026', folio: 'NX-20380', total: '$1,800.00', status: 'Pagado' },
- { date: '08 Mar 2026', folio: 'NX-20020', total: '$940.00', status: 'Pagado' },
- { date: '18 Feb 2026', folio: 'NX-19520', total: '$2,600.00', status: 'Pagado' },
- { date: '05 Feb 2026', folio: 'NX-19290', total: '$780.00', status: 'Pagado' },
- { date: '23 Ene 2026', folio: 'NX-19010', total: '$3,400.00', status: 'Pagado' },
- { date: '12 Ene 2026', folio: 'NX-18790', total: '$560.00', status: 'Pagado' },
- { date: '02 Ene 2026', folio: 'NX-18560', total: '$1,230.00', status: 'Pagado' },
- { date: '20 Dic 2025', folio: 'NX-18160', total: '$2,100.00', status: 'Pagado' },
- { date: '09 Dic 2025', folio: 'NX-17960', total: '$870.00', status: 'Pagado' },
- { date: '28 Nov 2025', folio: 'NX-17740', total: '$1,540.00', status: 'Pagado' },
- ]
- },
- {
- id: 8, num: '00008',
- name: 'Mega Partes Industriales', initials: 'MP',
- rfc: 'MPI840718B56', phone: '55 6600-9988', email: 'compras@megapartes.mx',
- tipo: 'Mayoreo', credit: 200000, creditLimit: 500000,
- lastPurchase: '29 Mar 2026', estado: 'Activo',
- address: 'Av. 5 de Mayo 3800, Zona Industrial, Tlalnepantla, Edo. Méx.',
- since: '08 Nov 2012',
- history: [
- { date: '29 Mar 2026', folio: 'NX-20495', total: '$88,400.00', status: 'Pendiente' },
- { date: '23 Mar 2026', folio: 'NX-20415', total: '$62,100.00', status: 'Pagado' },
- { date: '16 Mar 2026', folio: 'NX-20230', total: '$105,800.00', status: 'Pagado' },
- { date: '08 Mar 2026', folio: 'NX-20050', total: '$47,300.00', status: 'Pagado' },
- { date: '28 Feb 2026', folio: 'NX-19750', total: '$93,600.00', status: 'Pagado' },
- { date: '19 Feb 2026', folio: 'NX-19540', total: '$38,700.00', status: 'Pagado' },
- { date: '09 Feb 2026', folio: 'NX-19310', total: '$71,200.00', status: 'Pagado' },
- { date: '30 Ene 2026', folio: 'NX-19080', total: '$56,900.00', status: 'Pagado' },
- { date: '21 Ene 2026', folio: 'NX-18870', total: '$43,500.00', status: 'Pagado' },
- { date: '12 Ene 2026', folio: 'NX-18680', total: '$82,100.00', status: 'Pagado' },
- ]
- },
- {
- id: 9, num: '00009',
- name: 'Taller Mecánico Pérez Hnos.', initials: 'TP',
- rfc: 'PHM950228HDF', phone: '55 4433-6677', email: 'perezhermanos@gmail.com',
- tipo: 'Taller', credit: 12000, creditLimit: 20000,
- lastPurchase: '20 Mar 2026', estado: 'Activo',
- address: 'Calle 5 No. 234, Col. Agrícola Oriental, Iztacalco, CDMX',
- since: '14 Jul 2020',
- history: [
- { date: '20 Mar 2026', folio: 'NX-20360', total: '$5,600.00', status: 'Pagado' },
- { date: '11 Mar 2026', folio: 'NX-20100', total: '$2,340.00', status: 'Pendiente' },
- { date: '02 Mar 2026', folio: 'NX-19900', total: '$7,800.00', status: 'Pagado' },
- { date: '21 Feb 2026', folio: 'NX-19640', total: '$1,450.00', status: 'Pagado' },
- { date: '11 Feb 2026', folio: 'NX-19400', total: '$3,900.00', status: 'Pagado' },
- { date: '01 Feb 2026', folio: 'NX-19180', total: '$6,200.00', status: 'Pagado' },
- { date: '23 Ene 2026', folio: 'NX-18970', total: '$2,890.00', status: 'Pagado' },
- { date: '14 Ene 2026', folio: 'NX-18780', total: '$4,100.00', status: 'Pagado' },
- { date: '05 Ene 2026', folio: 'NX-18590', total: '$1,780.00', status: 'Pagado' },
- { date: '27 Dic 2025', folio: 'NX-18210', total: '$8,400.00', status: 'Pagado' },
- ]
- },
- {
- id: 10, num: '00010',
- name: 'Import Autoparts Villanueva', initials: 'IV',
- rfc: 'IAV010315B34', phone: '55 9900-1122', email: 'ventas@importav.mx',
- tipo: 'Mayoreo', credit: 95000, creditLimit: 150000,
- lastPurchase: '28 Mar 2026', estado: 'Activo',
- address: 'Blvd. Manuel Ávila Camacho 600, Naucalpan, Edo. Méx.',
- since: '22 Mar 2015',
- history: [
- { date: '28 Mar 2026', folio: 'NX-20490', total: '$34,500.00', status: 'Pendiente' },
- { date: '21 Mar 2026', folio: 'NX-20370', total: '$22,800.00', status: 'Pagado' },
- { date: '13 Mar 2026', folio: 'NX-20160', total: '$48,900.00', status: 'Pagado' },
- { date: '05 Mar 2026', folio: 'NX-19950', total: '$16,700.00', status: 'Pagado' },
- { date: '24 Feb 2026', folio: 'NX-19700', total: '$39,200.00', status: 'Pagado' },
- { date: '14 Feb 2026', folio: 'NX-19460', total: '$27,600.00', status: 'Pagado' },
- { date: '04 Feb 2026', folio: 'NX-19220', total: '$11,400.00', status: 'Pagado' },
- { date: '26 Ene 2026', folio: 'NX-19000', total: '$53,800.00', status: 'Pagado' },
- { date: '17 Ene 2026', folio: 'NX-18810', total: '$24,100.00', status: 'Pagado' },
- { date: '08 Ene 2026', folio: 'NX-18620', total: '$31,600.00', status: 'Pagado' },
- ]
- },
- {
- id: 11, num: '00011',
- name: 'Centro Automotriz Garza', initials: 'CG',
- rfc: 'GAR780904HNL', phone: '81 2244-5566', email: 'garza.auto@prodigy.net',
- tipo: 'Taller', credit: 9000, creditLimit: 30000,
- lastPurchase: '18 Mar 2026', estado: 'Activo',
- address: 'Av. Constitución 1888, Monterrey, N.L.',
- since: '07 Feb 2018',
- history: [
- { date: '18 Mar 2026', folio: 'NX-20280', total: '$7,200.00', status: 'Pagado' },
- { date: '08 Mar 2026', folio: 'NX-20030', total: '$3,400.00', status: 'Pagado' },
- { date: '26 Feb 2026', folio: 'NX-19760', total: '$9,100.00', status: 'Pendiente' },
- { date: '16 Feb 2026', folio: 'NX-19510', total: '$4,800.00', status: 'Pagado' },
- { date: '06 Feb 2026', folio: 'NX-19270', total: '$6,550.00', status: 'Pagado' },
- { date: '27 Ene 2026', folio: 'NX-19040', total: '$2,100.00', status: 'Pagado' },
- { date: '17 Ene 2026', folio: 'NX-18820', total: '$8,300.00', status: 'Pagado' },
- { date: '08 Ene 2026', folio: 'NX-18630', total: '$3,670.00', status: 'Pagado' },
- { date: '29 Dic 2025', folio: 'NX-18250', total: '$11,400.00', status: 'Pagado' },
- { date: '18 Dic 2025', folio: 'NX-18020', total: '$5,890.00', status: 'Pagado' },
- ]
- },
- {
- id: 12, num: '00012',
- name: 'Refacciones El Farol', initials: 'RF',
- rfc: 'REF011120B45', phone: '33 5577-8899', email: 'elfarol.ref@gmail.com',
- tipo: 'Mostrador', credit: 7500, creditLimit: 10000,
- lastPurchase: '17 Mar 2026', estado: 'Inactivo',
- address: 'Calz. Independencia Sur 2340, Guadalajara, Jal.',
- since: '15 Oct 2021',
- history: [
- { date: '17 Mar 2026', folio: 'NX-20260', total: '$2,500.00', status: 'Pendiente' },
- { date: '06 Mar 2026', folio: 'NX-20010', total: '$1,800.00', status: 'Pagado' },
- { date: '23 Feb 2026', folio: 'NX-19680', total: '$3,200.00', status: 'Pagado' },
- { date: '12 Feb 2026', folio: 'NX-19440', total: '$900.00', status: 'Pagado' },
- { date: '01 Feb 2026', folio: 'NX-19200', total: '$4,100.00', status: 'Pagado' },
- { date: '21 Ene 2026', folio: 'NX-18960', total: '$1,550.00', status: 'Pagado' },
- { date: '11 Ene 2026', folio: 'NX-18770', total: '$2,800.00', status: 'Pagado' },
- { date: '02 Ene 2026', folio: 'NX-18540', total: '$680.00', status: 'Pagado' },
- { date: '21 Dic 2025', folio: 'NX-18170', total: '$3,600.00', status: 'Pagado' },
- { date: '10 Dic 2025', folio: 'NX-17970', total: '$1,200.00', status: 'Pagado' },
- ]
- },
- ];
-
- let selectedCustomerId = null;
- let filteredCustomers = [...customers];
-
- /* ------------------------------------------------------------------
- RENDER TABLE
- ------------------------------------------------------------------ */
- function renderTable(data) {
- const tbody = document.getElementById('customersBody');
- tbody.innerHTML = '';
-
- if (data.length === 0) {
- tbody.innerHTML = `
| Sin resultados para la búsqueda. |
`;
- return;
- }
-
- data.forEach(c => {
- const pct = Math.round(((c.creditLimit - c.credit) / c.creditLimit) * 100);
- const creditClass = pct >= 80 ? 'none' : pct >= 60 ? 'low' : '';
- const creditFormatted = new Intl.NumberFormat('es-MX', { style:'currency', currency:'MXN', minimumFractionDigits:0 }).format(c.credit);
-
- let estadoBadge = '';
- if (c.estado === 'Activo') estadoBadge = `
Activo`;
- else if (c.estado === 'Inactivo') estadoBadge = `
Inactivo`;
- else estadoBadge = `
Mora`;
-
- const tipoClass = c.tipo === 'Taller' ? 'tipo-chip--taller' : c.tipo === 'Mayoreo' ? 'tipo-chip--mayoreo' : 'tipo-chip--mostrador';
- const isSelected = c.id === selectedCustomerId;
-
- const tr = document.createElement('tr');
- tr.className = isSelected ? 'selected' : '';
- tr.onclick = () => selectCustomer(c.id);
- tr.innerHTML = `
-
${c.num} |
-
- ${c.name}
- ${c.email}
- |
-
${c.rfc} |
-
${c.phone} |
-
${c.email} |
-
${c.tipo} |
-
${creditFormatted} |
-
${c.lastPurchase} |
-
${estadoBadge} |
- `;
- tbody.appendChild(tr);
- });
- }
-
- /* ------------------------------------------------------------------
- FILTER
- ------------------------------------------------------------------ */
- function filterCustomers() {
- const q = document.getElementById('searchInput').value.toLowerCase();
- const tipo = document.getElementById('tipoFilter').value;
- const estado = document.getElementById('estadoFilter').value;
-
- filteredCustomers = customers.filter(c => {
- const matchQ = !q || c.name.toLowerCase().includes(q) || c.rfc.toLowerCase().includes(q) || c.phone.includes(q) || c.email.toLowerCase().includes(q);
- const matchT = !tipo || c.tipo === tipo;
- const matchE = !estado || c.estado === estado;
- return matchQ && matchT && matchE;
- });
-
- renderTable(filteredCustomers);
- document.getElementById('tableInfo').textContent = `Mostrando ${filteredCustomers.length} de ${customers.length} clientes`;
- }
-
- /* ------------------------------------------------------------------
- SELECT CUSTOMER — populate detail panel
- ------------------------------------------------------------------ */
- function selectCustomer(id) {
- const c = customers.find(x => x.id === id);
- if (!c) return;
- selectedCustomerId = id;
-
- // Toggle empty/content
- document.getElementById('detailEmpty').style.display = 'none';
- const detail = document.getElementById('detailContent');
- detail.style.display = 'flex';
-
- // Avatar
- document.getElementById('detailAvatar').textContent = c.initials;
-
- // Header
- document.getElementById('detailName').textContent = c.name.toUpperCase();
- document.getElementById('detailRFC').textContent = c.rfc;
-
- // Tipo chip
- const tipoEl = document.getElementById('detailTipo');
- tipoEl.textContent = c.tipo;
- tipoEl.className = `tipo-chip tipo-chip--${c.tipo.toLowerCase()}`;
-
- // Status badge
- const statEl = document.getElementById('detailStatus');
- if (c.estado === 'Activo') {
- statEl.className = 'badge badge--active';
- statEl.innerHTML = '
Activo';
- } else if (c.estado === 'Inactivo') {
- statEl.className = 'badge badge--inactive';
- statEl.innerHTML = '
Inactivo';
- } else {
- statEl.className = 'badge badge--warning';
- statEl.innerHTML = '
En Mora';
- }
-
- // Contact
- document.getElementById('detailAddress').textContent = c.address;
- document.getElementById('detailPhone').textContent = c.phone;
- document.getElementById('detailEmail').textContent = c.email;
- document.getElementById('detailSince').textContent = c.since;
- document.getElementById('detailLastPurchase').textContent = c.lastPurchase;
-
- // Credit
- const fmt = n => new Intl.NumberFormat('es-MX', { style:'currency', currency:'MXN', minimumFractionDigits:0 }).format(n);
- const used = c.creditLimit - c.credit;
- const pct = Math.round((used / c.creditLimit) * 100);
- document.getElementById('detailCreditLimit').textContent = fmt(c.creditLimit);
- document.getElementById('detailCreditAvail').textContent = fmt(c.credit);
- document.getElementById('detailCreditUsed').textContent = fmt(used);
- document.getElementById('detailCreditPct').textContent = `${pct}%`;
-
- const bar = document.getElementById('detailCreditBar');
- bar.style.width = `${pct}%`;
- bar.className = `progress-bar__fill ${pct < 40 ? 'low' : pct > 75 ? 'high' : ''}`;
-
- // Purchase History
- const hbody = document.getElementById('historyBody');
- hbody.innerHTML = '';
- c.history.forEach(h => {
- const statusClass = h.status === 'Pagado' ? 'mbadge--paid' : h.status === 'Vencido' ? 'mbadge--overdue' : 'mbadge--pending';
- const tr = document.createElement('tr');
- tr.innerHTML = `
-
${h.date} |
-
${h.folio} |
-
${h.total} |
-
${h.status} |
- `;
- hbody.appendChild(tr);
- });
-
- // Re-render table to update selected row highlight
- renderTable(filteredCustomers);
- }
-
- function closeDetail() {
- selectedCustomerId = null;
- document.getElementById('detailEmpty').style.display = 'flex';
- document.getElementById('detailContent').style.display = 'none';
- renderTable(filteredCustomers);
- }
-
- function openNewCustomerModal() {
- alert('Modal "Nuevo Cliente" — pendiente de implementación en sprint 2.');
- }
-
- /* ------------------------------------------------------------------
- INIT
- ------------------------------------------------------------------ */
- renderTable(customers);
- // Pre-select first customer on load for demo
- selectCustomer(1);
-
- /* ------------------------------------------------------------------
- SLIDE PANEL — wired to Customers.showDetail()
+ SLIDE PANEL
------------------------------------------------------------------ */
const panelOverlay = document.getElementById('panelOverlay');
const slidePanel = document.getElementById('slidePanel');
@@ -2487,18 +2066,25 @@
}
document.addEventListener('keydown', function(e) {
- if (e.key === 'Escape') closeSlidePanel();
+ if (e.key === 'Escape') {
+ closeSlidePanel();
+ if (typeof Customers !== 'undefined') Customers.closeModal();
+ }
});
- // Override viewCustomer / showDetail to open the slide panel
- window.viewCustomer = function(id) {
- if (typeof Customers !== 'undefined' && Customers.showDetail) {
- Customers.showDetail(id);
- }
+ /* Placeholder — overridden by customers.js init */
+ function openNewCustomerModal() {}
+ function filterCustomers() {}
+ function closeDetail() {
+ if (typeof Customers !== 'undefined') Customers.closeDetail();
+ }
+ function viewCustomer(id) {
+ if (typeof Customers !== 'undefined') Customers.showDetail(id);
openSlidePanel();
- };
+ }
+