filtro em produtos, bug tela de preparo, nome do user nas comandas e itens
This commit is contained in:
@@ -59,15 +59,19 @@ export async function renderKitchen(container: HTMLElement) {
|
||||
let currentStatus = 'PENDENTE';
|
||||
let currentFilter = '';
|
||||
let autoRefreshInterval: ReturnType<typeof setInterval> | null = null;
|
||||
let requestId = 0;
|
||||
|
||||
const loadItems = async () => {
|
||||
const grid = document.getElementById('items-grid')!;
|
||||
grid.innerHTML = '<p class="card-subtitle">Carregando...</p>';
|
||||
const myRequestId = ++requestId;
|
||||
try {
|
||||
let url = `/painel/itens?status=${currentStatus}`;
|
||||
if (currentFilter !== '') url += `&itemCozinha=${currentFilter}`;
|
||||
const items = await api.get(url);
|
||||
|
||||
if (myRequestId !== requestId) return;
|
||||
|
||||
const currentIds = new Set<string>(items.map((i: any) => i.id));
|
||||
|
||||
const previousIds = previousItemIdsByStatus.get(currentStatus);
|
||||
@@ -106,9 +110,10 @@ export async function renderKitchen(container: HTMLElement) {
|
||||
</div>
|
||||
<h3 class="card-title">${item.quantidade}× ${item.produto.nome}</h3>
|
||||
${item.observacao ? `<p style="color:var(--warning);font-size:0.875rem;margin-bottom:1rem;">⚠️ ${item.observacao}</p>` : ''}
|
||||
<p class="card-subtitle" style="margin-bottom:1.5rem;">
|
||||
<p class="card-subtitle" style="margin-bottom:0.5rem;">
|
||||
Pedido às ${new Date(item.criadoEm).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })}
|
||||
</p>
|
||||
${item.usuario ? `<p style="color:var(--text-secondary);font-size:0.8rem;margin-bottom:1rem;">Adicionado por: <strong>${item.usuario.nome}</strong></p>` : '<p style="margin-bottom:1rem;"></p>'}
|
||||
${action.label ? `
|
||||
<button class="btn" style="width:100%;" data-action="advance" data-item-id="${item.id}" data-next-status="${action.next}">
|
||||
${action.label}
|
||||
|
||||
@@ -87,6 +87,10 @@ export async function renderMenu(container: HTMLElement) {
|
||||
`;
|
||||
|
||||
let categorias: any[] = [];
|
||||
let allProducts: any[] = [];
|
||||
let filterCategoria = '';
|
||||
let filterAtivo = '';
|
||||
let filterCozinha = '';
|
||||
|
||||
const loadCategorias = async () => {
|
||||
categorias = await api.get('/categorias');
|
||||
@@ -96,47 +100,109 @@ export async function renderMenu(container: HTMLElement) {
|
||||
|
||||
const renderTabProdutos = async () => {
|
||||
const tc = document.getElementById('tab-content')!;
|
||||
const products = await api.get('/produtos');
|
||||
allProducts = await api.get('/produtos');
|
||||
tc.innerHTML = `
|
||||
<div class="page-header" style="margin-bottom:1rem;">
|
||||
<span class="card-subtitle">${products.length} produto(s) cadastrado(s)</span>
|
||||
<span class="card-subtitle" id="produtos-count"></span>
|
||||
<button class="btn" id="btn-novo-produto">+ Novo Produto</button>
|
||||
</div>
|
||||
<div style="display:flex;gap:0.5rem;flex-wrap:wrap;margin-bottom:1.5rem;align-items:flex-end;">
|
||||
<div class="form-group" style="margin:0;width:auto;">
|
||||
<label style="color:var(--text-secondary);font-size:0.8rem;">Categoria</label>
|
||||
<select id="f-categoria" class="form-control" style="padding:0.4rem 0.75rem;">
|
||||
<option value="">Todas</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" style="margin:0;width:auto;">
|
||||
<label style="color:var(--text-secondary);font-size:0.8rem;">Status</label>
|
||||
<select id="f-ativo" class="form-control" style="padding:0.4rem 0.75rem;">
|
||||
<option value="">Todos</option>
|
||||
<option value="true">Ativos</option>
|
||||
<option value="false">Inativos</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" style="margin:0;width:auto;">
|
||||
<label style="color:var(--text-secondary);font-size:0.8rem;">Tipo</label>
|
||||
<select id="f-cozinha" class="form-control" style="padding:0.4rem 0.75rem;">
|
||||
<option value="">Todos</option>
|
||||
<option value="true">🍳 Cozinha</option>
|
||||
<option value="false">🍹 Bar</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-secondary btn-sm" id="btn-clear-filters" style="margin-bottom:0;">✕ Limpar</button>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Nome</th><th>Categoria</th><th>Preço</th><th>Estoque</th><th>Tipo</th><th>Status</th><th>Ações</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${products.map((p: any) => `
|
||||
<tr>
|
||||
<td data-label="Produto"><strong>${p.nome}</strong>${p.descricao ? `<br><small style="color:var(--text-secondary)">${p.descricao}</small>` : ''}</td>
|
||||
<td data-label="Categoria">${p.categoria?.nome ?? '—'}</td>
|
||||
<td data-label="Preço">${formatBRL(p.preco)}</td>
|
||||
<td data-label="Estoque">
|
||||
<span style="color:${p.estoqueAtual <= p.estoqueMinimo ? 'var(--accent-primary)' : 'var(--text-primary)'}">
|
||||
${p.estoqueAtual} ${p.estoqueAtual <= p.estoqueMinimo ? '⚠️' : ''}
|
||||
</span>
|
||||
</td>
|
||||
<td data-label="Tipo"><span class="badge ${p.itemCozinha ? 'badge-cozinha' : 'badge-barman'}">${p.itemCozinha ? 'Cozinha' : 'Bar'}</span></td>
|
||||
<td data-label="Status"><span class="badge ${p.ativo ? 'badge-success' : 'badge-warning'}">${p.ativo ? 'Ativo' : 'Inativo'}</span></td>
|
||||
<td data-label="Ações" style="display:flex;gap:0.5rem;flex-wrap:wrap;">
|
||||
<button class="btn btn-sm btn-secondary" onclick="window.editProduct(${JSON.stringify(p).replace(/"/g, '"')})">✏️</button>
|
||||
<button class="btn btn-sm btn-danger" onclick="window.deleteProduct('${p.id}','${p.nome}')">🗑</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
<tbody id="produtos-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const catSel = document.getElementById('f-categoria') as HTMLSelectElement;
|
||||
catSel.innerHTML = '<option value="">Todas</option>' +
|
||||
categorias.map((c: any) => `<option value="${c.id}">${c.nome}</option>`).join('');
|
||||
catSel.value = filterCategoria;
|
||||
(document.getElementById('f-ativo') as HTMLSelectElement).value = filterAtivo;
|
||||
(document.getElementById('f-cozinha') as HTMLSelectElement).value = filterCozinha;
|
||||
|
||||
catSel.addEventListener('change', () => { filterCategoria = catSel.value; renderProductsTable(); });
|
||||
(document.getElementById('f-ativo') as HTMLSelectElement).addEventListener('change', (e) => { filterAtivo = (e.target as HTMLSelectElement).value; renderProductsTable(); });
|
||||
(document.getElementById('f-cozinha') as HTMLSelectElement).addEventListener('change', (e) => { filterCozinha = (e.target as HTMLSelectElement).value; renderProductsTable(); });
|
||||
document.getElementById('btn-clear-filters')?.addEventListener('click', () => {
|
||||
filterCategoria = '';
|
||||
filterAtivo = '';
|
||||
filterCozinha = '';
|
||||
(document.getElementById('f-categoria') as HTMLSelectElement).value = '';
|
||||
(document.getElementById('f-ativo') as HTMLSelectElement).value = '';
|
||||
(document.getElementById('f-cozinha') as HTMLSelectElement).value = '';
|
||||
renderProductsTable();
|
||||
});
|
||||
|
||||
renderProductsTable();
|
||||
|
||||
document.getElementById('btn-novo-produto')?.addEventListener('click', () => {
|
||||
clearProductForm();
|
||||
(document.getElementById('modal-produto') as HTMLElement).style.display = 'flex';
|
||||
});
|
||||
};
|
||||
|
||||
const renderProductsTable = () => {
|
||||
let filtered = allProducts;
|
||||
if (filterCategoria) filtered = filtered.filter((p: any) => p.categoriaId === filterCategoria);
|
||||
if (filterAtivo !== '') filtered = filtered.filter((p: any) => String(p.ativo) === filterAtivo);
|
||||
if (filterCozinha !== '') filtered = filtered.filter((p: any) => String(p.itemCozinha) === filterCozinha);
|
||||
|
||||
const countEl = document.getElementById('produtos-count');
|
||||
if (countEl) countEl.textContent = `${filtered.length} de ${allProducts.length} produto(s)`;
|
||||
|
||||
const tbody = document.getElementById('produtos-tbody')!;
|
||||
if (!filtered.length) {
|
||||
tbody.innerHTML = `<tr><td colspan="7" style="text-align:center;padding:2rem;color:var(--text-secondary);">Nenhum produto encontrado com os filtros selecionados.</td></tr>`;
|
||||
return;
|
||||
}
|
||||
tbody.innerHTML = filtered.map((p: any) => `
|
||||
<tr>
|
||||
<td data-label="Produto"><strong>${p.nome}</strong>${p.descricao ? `<br><small style="color:var(--text-secondary)">${p.descricao}</small>` : ''}</td>
|
||||
<td data-label="Categoria">${p.categoria?.nome ?? '—'}</td>
|
||||
<td data-label="Preço">${formatBRL(p.preco)}</td>
|
||||
<td data-label="Estoque">
|
||||
<span style="color:${p.estoqueAtual <= p.estoqueMinimo ? 'var(--accent-primary)' : 'var(--text-primary)'}">
|
||||
${p.estoqueAtual} ${p.estoqueAtual <= p.estoqueMinimo ? '⚠️' : ''}
|
||||
</span>
|
||||
</td>
|
||||
<td data-label="Tipo"><span class="badge ${p.itemCozinha ? 'badge-cozinha' : 'badge-barman'}">${p.itemCozinha ? 'Cozinha' : 'Bar'}</span></td>
|
||||
<td data-label="Status"><span class="badge ${p.ativo ? 'badge-success' : 'badge-warning'}">${p.ativo ? 'Ativo' : 'Inativo'}</span></td>
|
||||
<td data-label="Ações" style="display:flex;gap:0.5rem;flex-wrap:wrap;">
|
||||
<button class="btn btn-sm btn-secondary" onclick="window.editProduct(${JSON.stringify(p).replace(/"/g, '"')})">✏️</button>
|
||||
<button class="btn btn-sm btn-danger" onclick="window.deleteProduct('${p.id}','${p.nome}')">🗑</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
};
|
||||
|
||||
const renderTabCategorias = async () => {
|
||||
const tc = document.getElementById('tab-content')!;
|
||||
const cats = await api.get('/categorias');
|
||||
|
||||
@@ -108,6 +108,7 @@ export async function renderOrders(container: HTMLElement) {
|
||||
<span class="detalhes-summary-client" id="detalhes-label-client"></span>
|
||||
</div>
|
||||
<div id="detalhes-label-status"></div>
|
||||
<div id="detalhes-label-usuario" style="font-size:0.8rem;color:var(--text-secondary);margin-top:0.25rem;"></div>
|
||||
</div>
|
||||
|
||||
<div class="detalhes-edit-section">
|
||||
@@ -175,6 +176,7 @@ export async function renderOrders(container: HTMLElement) {
|
||||
<th style="text-align:center;">Qtd</th>
|
||||
<th style="text-align:right;">Subtotal</th>
|
||||
<th>Obs</th>
|
||||
<th>Adicionado por</th>
|
||||
<th>Status</th>
|
||||
<th>Ações</th>
|
||||
</tr>
|
||||
@@ -294,6 +296,7 @@ async function loadComandas(params?: { status?: string; dataInicio?: string; dat
|
||||
<tr>
|
||||
<th>Identificador</th>
|
||||
<th>Cliente</th>
|
||||
<th>Aberto por</th>
|
||||
<th>Status</th>
|
||||
<th>Total</th>
|
||||
<th>Aberta em</th>
|
||||
@@ -305,6 +308,7 @@ async function loadComandas(params?: { status?: string; dataInicio?: string; dat
|
||||
<tr>
|
||||
<td data-label="Mesa" class="cmd-ident"><strong>${c.identificador}</strong></td>
|
||||
<td data-label="Cliente" class="cmd-client">${c.nomeCliente ?? ''}</td>
|
||||
<td data-label="Aberto por" class="cmd-usuario" style="color:var(--text-secondary);font-size:0.85rem;">${c.usuario?.nome ?? '—'}</td>
|
||||
<td data-label="Status" class="cmd-status"><span class="badge status-${c.status.toLowerCase()}">${c.status}</span></td>
|
||||
<td data-label="Total" class="cmd-total"><strong>${formatBRL(calcularTotalFinal(c))}</strong></td>
|
||||
<td data-label="Aberta em" class="cmd-data">${new Date(c.criadoEm).toLocaleString('pt-BR')}</td>
|
||||
@@ -606,6 +610,12 @@ function bindOrderEvents() {
|
||||
document.getElementById('detalhes-label-ident')!.textContent = c.identificador;
|
||||
document.getElementById('detalhes-label-client')!.textContent = c.nomeCliente ? `· ${c.nomeCliente}` : '';
|
||||
document.getElementById('detalhes-label-status')!.innerHTML = statusBadge(c.status);
|
||||
|
||||
// Exibir quem abriu a comanda
|
||||
const detalhesUsuario = document.getElementById('detalhes-label-usuario');
|
||||
if (detalhesUsuario) {
|
||||
detalhesUsuario.textContent = c.usuario ? `Aberto por: ${c.usuario.nome}` : '';
|
||||
}
|
||||
|
||||
// Render itens
|
||||
const tbody = document.getElementById('detalhes-itens-tbody')!;
|
||||
@@ -614,7 +624,7 @@ function bindOrderEvents() {
|
||||
const canDelete = user && ['ADMIN', 'CAIXA'].includes(user.role) && c.status === 'ABERTA';
|
||||
|
||||
if (itens.length === 0) {
|
||||
tbody.innerHTML = `<tr><td colspan="6" style="text-align:center;color:var(--text-secondary);">Nenhum item adicionado</td></tr>`;
|
||||
tbody.innerHTML = `<tr><td colspan="7" style="text-align:center;color:var(--text-secondary);">Nenhum item adicionado</td></tr>`;
|
||||
} else {
|
||||
tbody.innerHTML = itens.map((i: any) => `
|
||||
<tr>
|
||||
@@ -627,6 +637,7 @@ function bindOrderEvents() {
|
||||
: (i.observacao ?? '')
|
||||
}
|
||||
</td>
|
||||
<td data-label="Adicionado por" class="item-usuario" style="font-size:0.8rem;color:var(--text-secondary);">${i.usuario?.nome ?? '—'}</td>
|
||||
<td data-label="Status" class="item-status"><span class="badge badge-${i.status === 'ENTREGUE' ? 'success' : 'warning'}">${i.status}</span></td>
|
||||
<td data-label="Ações" class="item-actions">
|
||||
${canDelete ? `
|
||||
|
||||
Reference in New Issue
Block a user