VENDEDOR EN PEDIDO Y EN ADMINISTRADOR
This commit is contained in:
@@ -12,7 +12,8 @@ const TABS = [
|
||||
{ id: 'agotados', label: 'Agotados', icon: '🚫' },
|
||||
{ id: 'descargas', label: 'Descargas', icon: '⬇️' },
|
||||
{ id: 'categorias', label: 'Categorías', icon: '🗂️' },
|
||||
{ id: 'usuarios', label: 'Usuarios', icon: '👥' }
|
||||
{ id: 'usuarios', label: 'Usuarios', icon: '👥' },
|
||||
{ id: 'pedidos', label: 'Pedidos', icon: '📋' }
|
||||
];
|
||||
|
||||
const ROLES = {
|
||||
@@ -166,6 +167,10 @@ function cambiarVista(view) {
|
||||
if (view === 'descargas') {
|
||||
cargarDescargas();
|
||||
}
|
||||
|
||||
if (view === 'pedidos') {
|
||||
cargarPedidos();
|
||||
}
|
||||
}
|
||||
|
||||
async function cargarEstadisticas() {
|
||||
@@ -2163,6 +2168,203 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (btn) btn.addEventListener('click', confirmarEliminarProductoDef);
|
||||
});
|
||||
|
||||
// ════════════════════════════════
|
||||
// PEDIDOS — vendedores + listado de pedidos
|
||||
// ════════════════════════════════
|
||||
async function cargarPedidos() {
|
||||
console.log('[pedidos] cargando…');
|
||||
setupVendedores();
|
||||
setupPedidosFiltro();
|
||||
try { await actualizarSelectVendedores(); } catch (e) { console.error('[pedidos] select error:', e); }
|
||||
await Promise.all([
|
||||
cargarVendedores().catch(e => console.error('[pedidos] vendedores error:', e)),
|
||||
cargarListadoPedidos().catch(e => console.error('[pedidos] listado error:', e))
|
||||
]);
|
||||
}
|
||||
|
||||
// ─── VENDEDORES ───
|
||||
async function cargarVendedores() {
|
||||
const listEl = document.getElementById('vendedoresList');
|
||||
const errEl = document.getElementById('vendedorError');
|
||||
errEl.classList.remove('show');
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/vendedores', { credentials: 'include' });
|
||||
if (!res.ok) throw new Error('Error al cargar vendedores');
|
||||
const vendedores = await res.json();
|
||||
|
||||
if (vendedores.length === 0) {
|
||||
listEl.innerHTML = '<p class="pedidos-empty" style="text-align:left;padding:8px 0">No hay vendedores. Agrega el primero.</p>';
|
||||
} else {
|
||||
listEl.innerHTML = vendedores.map(v => `
|
||||
<div class="vendedor-item" data-id="${v.id}">
|
||||
<span class="vendedor-nombre">${escapeHtml(v.nombre)}</span>
|
||||
<button class="vendedor-remove" data-id="${v.id}" title="Eliminar vendedor">✕</button>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Wire delete
|
||||
listEl.querySelectorAll('.vendedor-remove').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const id = btn.dataset.id;
|
||||
try {
|
||||
const delRes = await fetch(`/api/vendedores/${id}`, {
|
||||
method: 'DELETE',
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!delRes.ok) throw new Error('Error al eliminar');
|
||||
await cargarVendedores();
|
||||
actualizarSelectVendedores();
|
||||
} catch (err) {
|
||||
errEl.textContent = err.message;
|
||||
errEl.classList.add('show');
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
listEl.innerHTML = `<p class="pedidos-empty" style="text-align:left;padding:8px 0">Error: ${err.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function actualizarSelectVendedores() {
|
||||
// Actualiza el select del modal público y el filtro de pedidos
|
||||
try {
|
||||
const res = await fetch('/api/vendedores');
|
||||
const vendedores = await res.json();
|
||||
const selects = ['pedidosFilterVendedor'];
|
||||
selects.forEach(id => {
|
||||
const sel = document.getElementById(id);
|
||||
if (!sel) return;
|
||||
const actual = sel.value;
|
||||
sel.innerHTML = '<option value="">Todos los vendedores</option>' +
|
||||
vendedores.map(v => `<option value="${escapeHtml(v.nombre)}">${escapeHtml(v.nombre)}</option>`).join('');
|
||||
if (actual) sel.value = actual;
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function setupVendedores() {
|
||||
const btn = document.getElementById('btnAgregarVendedor');
|
||||
const input = document.getElementById('vendedorInput');
|
||||
const errEl = document.getElementById('vendedorError');
|
||||
|
||||
if (!btn || btn.dataset.vendWired) return;
|
||||
btn.dataset.vendWired = '1';
|
||||
|
||||
async function agregar() {
|
||||
const nombre = input.value.trim();
|
||||
if (!nombre) {
|
||||
errEl.textContent = 'Escribe un nombre de vendedor';
|
||||
errEl.classList.add('show');
|
||||
return;
|
||||
}
|
||||
errEl.classList.remove('show');
|
||||
try {
|
||||
const res = await fetch('/api/vendedores', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ nombre })
|
||||
});
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
throw new Error(data.error || 'Error al agregar');
|
||||
}
|
||||
input.value = '';
|
||||
await cargarVendedores();
|
||||
await actualizarSelectVendedores();
|
||||
} catch (err) {
|
||||
errEl.textContent = err.message;
|
||||
errEl.classList.add('show');
|
||||
}
|
||||
}
|
||||
|
||||
btn.addEventListener('click', agregar);
|
||||
input.addEventListener('keydown', e => { if (e.key === 'Enter') agregar(); });
|
||||
}
|
||||
|
||||
// ─── LISTADO DE PEDIDOS ───
|
||||
async function cargarListadoPedidos() {
|
||||
const tbody = document.getElementById('pedidosListBody');
|
||||
const filter = document.getElementById('pedidosFilterVendedor');
|
||||
const vendedor = filter ? filter.value : '';
|
||||
|
||||
try {
|
||||
let url = '/api/pedidos';
|
||||
if (vendedor) url += `?vendedor=${encodeURIComponent(vendedor)}`;
|
||||
const res = await fetch(url, { credentials: 'include' });
|
||||
if (!res.ok) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="pedidos-empty">No autorizado</td></tr>';
|
||||
return;
|
||||
}
|
||||
const pedidos = await res.json();
|
||||
|
||||
// Ordenar por fecha descendente
|
||||
pedidos.sort((a, b) => new Date(b.fecha) - new Date(a.fecha));
|
||||
|
||||
if (pedidos.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="pedidos-empty">No hay pedidos aún.</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
const renderedRows = pedidos.map(p => {
|
||||
const fecha = new Date(p.fecha).toLocaleDateString('es-CO', {
|
||||
year: 'numeric', month: '2-digit', day: '2-digit',
|
||||
hour: '2-digit', minute: '2-digit'
|
||||
});
|
||||
const itemsCount = Array.isArray(p.items) ? p.items.reduce((s, i) => s + (i.cantidad || 1), 0) : 0;
|
||||
const total = p.total || 0;
|
||||
const clienteNombre = p.cliente ? p.cliente.nombre : '—';
|
||||
const vendedorNombre = p.vendedor || '—';
|
||||
return `<tr>
|
||||
<td>${escapeHtml(fecha)}</td>
|
||||
<td>${escapeHtml(clienteNombre)}</td>
|
||||
<td>${escapeHtml(vendedorNombre)}</td>
|
||||
<td>${itemsCount}</td>
|
||||
<td class="pedidos-total">$${total.toLocaleString('es-CO')}</td>
|
||||
<td>
|
||||
<span class="pedidos-actions-group">
|
||||
<a href="/pedidos/${p.filename}" class="btn btn-ghost btn-sm" target="_blank" download>📄 Ver PDF</a>
|
||||
<button type="button" class="btn btn-danger btn-sm pedido-delete" data-filename="${p.filename}" title="Eliminar pedido">Eliminar</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>`;
|
||||
});
|
||||
tbody.innerHTML = renderedRows.join('');
|
||||
|
||||
// Wire delete buttons
|
||||
tbody.querySelectorAll('.pedido-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const filename = btn.dataset.filename;
|
||||
if (!confirm(`¿Eliminar el pedido "${filename}"? Esta acción no se puede deshacer.`)) return;
|
||||
try {
|
||||
const res = await fetch(`/api/pedidos/${encodeURIComponent(filename)}`, {
|
||||
method: 'DELETE',
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
throw new Error(data.error || 'Error al eliminar');
|
||||
}
|
||||
await cargarListadoPedidos();
|
||||
} catch (err) {
|
||||
alert('Error: ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
tbody.innerHTML = `<tr><td colspan="6" class="pedidos-empty">Error: ${err.message}</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
function setupPedidosFiltro() {
|
||||
const filter = document.getElementById('pedidosFilterVendedor');
|
||||
if (!filter || filter.dataset.pedWired) return;
|
||||
filter.dataset.pedWired = '1';
|
||||
filter.addEventListener('change', cargarListadoPedidos);
|
||||
}
|
||||
|
||||
// ════════════════════════════════
|
||||
// DESCARGAS — gestor genérico de árbol de selección
|
||||
// Usado por los subtabs PDF y Fotos.
|
||||
|
||||
Reference in New Issue
Block a user