feat(customers): habilitar edición de clientes desde la lista

- Hace clickeables las filas de la tabla para seleccionar un cliente y
  mostrar su panel de detalle (donde ya existe el botón Editar).
- Agrega botón de acción rápida con icono de lápiz en cada fila para
  abrir directamente el modal de edición.
- Extrae openEditModal y agrega editCustomer(id) para cargar el cliente
  vía API y abrir el modal sin depender de la selección previa.
- Actualiza colspan del estado vacío de 9 a 11 por la nueva columna.
This commit is contained in:
2026-06-15 18:10:23 +00:00
parent 584b87f82c
commit 85ecf52561
2 changed files with 21 additions and 6 deletions

View File

@@ -107,7 +107,7 @@ const Customers = (() => {
const num = String(c.id).padStart(5, '0');
const selClass = (currentCustomer && currentCustomer.id === c.id) ? 'selected' : '';
const isChecked = selectedCustomers.has(c.id) ? 'checked' : '';
return '<tr class="' + selClass + '">' +
return '<tr class="' + selClass + '" onclick="Customers.selectCustomer(' + c.id + ')">' +
'<td onclick="event.stopPropagation();"><input type="checkbox" ' + isChecked + ' onchange="Customers.toggleCustomerSelection(' + c.id + ')"></td>' +
'<td class="cell-num">' + num + '</td>' +
'<td>' +
@@ -121,6 +121,7 @@ const Customers = (() => {
'<td class="cell-credit ' + creditClass + '">' + fmt(available) + '</td>' +
'<td class="cell-date hide-mobile">' + formatDate(c.last_purchase || c.created_at) + '</td>' +
'<td>' + statusBadge(c) + '</td>' +
'<td onclick="event.stopPropagation();"><button class="btn btn-sm btn-ghost" title="Editar cliente" onclick="Customers.editCustomer(' + c.id + ')"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button></td>' +
'</tr>';
}
@@ -131,7 +132,7 @@ const Customers = (() => {
if (!tbody) return;
if (!customers || customers.length === 0) {
tbody.innerHTML = '<tr><td colspan="9">' + renderEmptyState({
tbody.innerHTML = '<tr><td colspan="11">' + renderEmptyState({
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/></svg>',
title: 'Sin clientes',
subtitle: 'No se encontraron clientes registrados.',
@@ -146,7 +147,7 @@ const Customers = (() => {
rowHeight: 52,
buffer: 3,
renderRow: renderCustomerRow,
emptyHtml: '<tr><td colspan="9">' + renderEmptyState({ title: 'Sin clientes', subtitle: 'No hay clientes registrados.' }) + '</td></tr>'
emptyHtml: '<tr><td colspan="11">' + renderEmptyState({ title: 'Sin clientes', subtitle: 'No hay clientes registrados.' }) + '</td></tr>'
});
}
customersVS.setData(customers);
@@ -415,9 +416,22 @@ const Customers = (() => {
function editCurrent() {
if (!currentCustomer) return;
const c = currentCustomer;
openEditModal(currentCustomer);
}
async function editCustomer(id) {
try {
const c = await api(`/pos/api/customers/${id}`);
currentCustomer = c;
openEditModal(c);
} catch (e) {
alert('Error: ' + e.message);
}
}
function openEditModal(c) {
const modal = document.getElementById('customerModal');
if (!modal) return;
if (!modal || !c) return;
document.getElementById('modalTitle').textContent = 'Editar Cliente';
document.getElementById('editId').value = c.id;
const safeSet = (id, v) => { const el = document.getElementById(id); if (el) el.value = v; };
@@ -802,7 +816,7 @@ const Customers = (() => {
const publicApi = {
search, goToPage, loadCustomers,
showDetail, selectCustomer, closeDetail,
showCreateModal, editCurrent, closeModal, save,
showCreateModal, editCurrent, editCustomer, closeModal, save,
showStatement, closeStatement,
showPaymentModal, closePayment, recordPayment,
};