Files
Autoparts-DB/dashboard/tienda.js
2026-03-18 22:25:32 +00:00

188 lines
7.5 KiB
JavaScript

/**
* tienda.js — Store / Tablet dashboard logic for Nexus Autoparts
*/
(function () {
'use strict';
var API = '';
// ================================================================
// Utility
// ================================================================
function fmt(n) {
return '$' + (parseFloat(n) || 0).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
function esc(s) {
if (!s) return '';
var d = document.createElement('div');
d.textContent = s;
return d.innerHTML;
}
// ================================================================
// Clock
// ================================================================
function updateClock() {
var now = new Date();
var h = now.getHours();
var m = String(now.getMinutes()).padStart(2, '0');
var ampm = h >= 12 ? 'PM' : 'AM';
h = h % 12 || 12;
document.getElementById('clock').textContent = h + ':' + m + ' ' + ampm;
}
updateClock();
setInterval(updateClock, 30000);
// ================================================================
// Load Dashboard Stats
// ================================================================
function loadStats() {
fetch(API + '/api/tienda/stats')
.then(function (r) { return r.json(); })
.then(function (d) {
var st = d.sales_today || {};
var sm = d.sales_month || {};
var pt = d.payments_today || {};
// KPIs
document.getElementById('kpi-sales-today').textContent = fmt(st.total);
document.getElementById('kpi-sales-count').textContent = (st.count || 0) + ' facturas';
document.getElementById('kpi-month').textContent = fmt(sm.total);
document.getElementById('kpi-month-count').textContent = (sm.count || 0) + ' facturas';
document.getElementById('kpi-customers').textContent = d.total_customers || 0;
document.getElementById('kpi-parts-count').textContent = (d.total_parts || 0) + ' partes';
document.getElementById('kpi-pending').textContent = fmt(d.pending_balance || 0);
document.getElementById('kpi-pending-count').textContent = (d.pending_invoices || 0) + ' facturas';
// Today's payments
document.getElementById('payments-today-amount').textContent = fmt(pt.total);
document.getElementById('payments-today-count').textContent = (pt.count || 0) + ' pagos registrados';
// Top debtors
renderDebtors(d.top_debtors || []);
// Recent invoices
renderInvoices(d.recent_invoices || []);
})
.catch(function (err) {
console.error('Error loading stats:', err);
});
}
// ================================================================
// Render Debtors
// ================================================================
function renderDebtors(debtors) {
var el = document.getElementById('debtors-list');
if (debtors.length === 0) {
el.innerHTML = '<div class="t-empty">Sin cuentas pendientes</div>';
return;
}
el.innerHTML = debtors.map(function (d) {
var limitPct = d.credit_limit > 0 ? Math.round(d.balance / d.credit_limit * 100) : 0;
return '<a href="/cuentas" class="t-debtor">'
+ '<div>'
+ '<div class="t-debtor-name">' + esc(d.name) + '</div>'
+ (d.credit_limit > 0 ? '<div class="t-debtor-invoices">' + limitPct + '% de l\u00edmite</div>' : '')
+ '</div>'
+ '<span class="t-debtor-amount">' + fmt(d.balance) + '</span>'
+ '</a>';
}).join('');
}
// ================================================================
// Render Recent Invoices
// ================================================================
function renderInvoices(invoices) {
var el = document.getElementById('recent-invoices');
if (invoices.length === 0) {
el.innerHTML = '<div class="t-empty">Sin facturas recientes</div>';
return;
}
el.innerHTML = invoices.map(function (inv) {
var statusClass = inv.status || 'pending';
var statusLabel = { pending: 'Pendiente', paid: 'Pagada', partial: 'Parcial', cancelled: 'Cancelada' };
return '<div class="t-invoice">'
+ '<div class="t-invoice-left">'
+ '<span class="t-invoice-folio">' + esc(inv.folio) + '</span>'
+ '<span class="t-invoice-customer">' + esc(inv.customer_name) + '</span>'
+ '</div>'
+ '<div class="t-invoice-right">'
+ '<span class="t-invoice-total">' + fmt(inv.total) + '</span>'
+ '<span class="t-invoice-status ' + statusClass + '">' + (statusLabel[statusClass] || statusClass) + '</span>'
+ '</div>'
+ '</div>';
}).join('');
}
// ================================================================
// Global Search
// ================================================================
var searchTimer = null;
var searchInput = document.getElementById('global-search');
var searchResults = document.getElementById('global-results');
if (searchInput) {
searchInput.addEventListener('input', function () {
clearTimeout(searchTimer);
var q = this.value.trim();
if (q.length < 2) {
searchResults.classList.remove('active');
searchResults.innerHTML = '';
return;
}
searchTimer = setTimeout(function () {
fetch(API + '/api/pos/search-parts?q=' + encodeURIComponent(q))
.then(function (r) { return r.json(); })
.then(function (results) {
if (results.length === 0) {
searchResults.innerHTML = '<div style="padding:0.8rem;color:var(--text-secondary);font-size:0.85rem">Sin resultados para "' + esc(q) + '"</div>';
} else {
searchResults.innerHTML = results.slice(0, 8).map(function (p) {
return '<div class="t-search-result-item">'
+ '<div>'
+ '<span class="sri-number">' + esc(p.oem_part_number) + '</span>'
+ '<span class="sri-name">' + esc(p.name_part) + '</span>'
+ '</div>'
+ '</div>';
}).join('');
}
searchResults.classList.add('active');
});
}, 250);
});
searchInput.addEventListener('blur', function () {
setTimeout(function () { searchResults.classList.remove('active'); }, 200);
});
searchInput.addEventListener('focus', function () {
if (searchResults.innerHTML.trim()) {
searchResults.classList.add('active');
}
});
}
// ================================================================
// Init
// ================================================================
loadStats();
// Auto-refresh every 2 minutes
setInterval(loadStats, 120000);
})();