v15: config sorted alphabetical, vales nome-only, clientes free-text, datalist Enter 50ms, auto-accumulate names
This commit is contained in:
+38
-18
@@ -509,6 +509,11 @@
|
||||
<div id="cfg-clientes-list"></div>
|
||||
<button class="btn-add-config" id="cfg-add-cliente">+ Adicionar cliente</button>
|
||||
</div>
|
||||
<div class="cfg-section">
|
||||
<div class="cfg-section-title">💰 Vales (nomes para autofill)</div>
|
||||
<div id="cfg-vales-list"></div>
|
||||
<button class="btn-add-config" id="cfg-add-vales">+ Adicionar nome</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-primary" id="cfg-salvar">Salvar Configuração</button>
|
||||
@@ -554,7 +559,8 @@ let config = {
|
||||
operadores: ['Isabelle', 'Silvanea', 'Rafael'],
|
||||
gerentes: ['Carlos', 'Ana', 'João'],
|
||||
despesas: ['Energia', 'Água', 'Manutenção', 'Limpeza', 'Insumos'],
|
||||
clientes: []
|
||||
clientes: [], // preenchimento livre — não é datalist fixo
|
||||
vales_nomes: [], // nomes usados em vales ( histórico para autofill)
|
||||
};
|
||||
|
||||
// ─── App State ────────────────────────────────────────────────────────────
|
||||
@@ -588,7 +594,7 @@ const TABLES = {
|
||||
'tbl-sangrias': { field: 'sangrias', cols: ['hora','retirada','gerente'], enterNav: { hora:'retirada', retirada:'gerente', gerente:'_nextRow' } },
|
||||
'tbl-despesas': { field: 'despesas', cols: ['desc','obs','valor'], enterNav: { desc:'obs', obs:'valor', valor:'_nextRow' } },
|
||||
'tbl-pix': { field: 'pixcnpj', cols: ['nome','valor'], enterNav: { nome:'valor', valor:'_nextRow' } },
|
||||
'tbl-vales': { field: 'vales', cols: ['nome','obs','valor'], enterNav: { nome:'obs', obs:'valor', valor:'_nextRow' } },
|
||||
'tbl-vales': { field: 'vales', cols: ['nome','valor'], enterNav: { nome:'valor', valor:'_nextRow' } },
|
||||
'tbl-receber': { field: 'receber', cols: ['nome','valor'], enterNav: { nome:'valor', valor:'_nextRow' } },
|
||||
'tbl-cancelamentos': { field: 'cancelamentos', cols: ['numero','valor','motivo'], enterNav: { numero:'valor', valor:'motivo', motivo:'_nextRow' } },
|
||||
'tbl-cartao-credito': { field: 'cartoes.credito', cols: ['valor'], enterNav: { valor:'_nextRow' } },
|
||||
@@ -619,10 +625,10 @@ function makeRow(tblId, rowIndex, rowData) {
|
||||
inp.dataset.col = col;
|
||||
if (col === 'gerente') inp.setAttribute('list', 'dl-gerente');
|
||||
if (col === 'desc') inp.setAttribute('list', 'dl-despesa');
|
||||
if (col === 'nome') {
|
||||
const listId = tblId === 'tbl-vales' ? 'dl-vales' : (tblId === 'tbl-receber' ? 'dl-cliente' : 'dl-generico');
|
||||
inp.setAttribute('list', listId);
|
||||
}
|
||||
// tbl-receber: clientes free-text (sem datalist)
|
||||
// tbl-vales: usa dl-vales
|
||||
if (col === 'nome' && tblId === 'tbl-vales') inp.setAttribute('list', 'dl-vales');
|
||||
if (col === 'nome' && tblId === 'tbl-pix') inp.setAttribute('list', 'dl-generico');
|
||||
inp.addEventListener('input', onTableInput);
|
||||
inp.addEventListener('blur', onCellBlur);
|
||||
inp.addEventListener('keydown', onCellKeydown);
|
||||
@@ -677,8 +683,8 @@ function onCellBlur(e) {
|
||||
|
||||
function onCellKeydown(e) {
|
||||
if (e.key !== 'Enter') return;
|
||||
// Don't preventDefault — let the browser fill the datalist value first,
|
||||
// then navigate after a tiny delay
|
||||
// 50ms delay so the browser finishes filling the datalist value
|
||||
// before we read e.target.value and move focus
|
||||
setTimeout(() => {
|
||||
const { tbl, row, col } = e.target.dataset;
|
||||
const cfg = TABLES[tbl];
|
||||
@@ -694,7 +700,7 @@ function onCellKeydown(e) {
|
||||
} else if (next) {
|
||||
focusInput(tbl, parseInt(row), next);
|
||||
}
|
||||
}, 0);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function focusInput(tblId, rowIndex, col) {
|
||||
@@ -930,18 +936,31 @@ function syncHeader() {
|
||||
|
||||
// ─── Datalists ─────────────────────────────────────────────────────────────
|
||||
function buildDatalists() {
|
||||
// Auto-build vales_nomes from current entries (autofill dinâmico)
|
||||
const valesUsed = [...new Set(estado.vales.map(v => v.nome).filter(Boolean))];
|
||||
config.vales_nomes = [...new Set([...config.vales_nomes, ...valesUsed])]
|
||||
.sort((a, b) => a.localeCompare(b, 'pt-BR'));
|
||||
|
||||
// Gerentes e Operadores também acumulam dos lançamentos
|
||||
const gerentesUsed = [...new Set(estado.sangrias.map(r => r.gerente).filter(Boolean))];
|
||||
config.gerentes = [...new Set([...config.gerentes, ...gerentesUsed])]
|
||||
.sort((a, b) => a.localeCompare(b, 'pt-BR'));
|
||||
|
||||
const lists = {
|
||||
'dl-operadores': config.operadores,
|
||||
'dl-gerente': config.gerentes,
|
||||
'dl-despesa': config.despesas,
|
||||
'dl-cliente': config.clientes,
|
||||
'dl-vales': config.operadores,
|
||||
'dl-operadores': [...new Set([...config.operadores])].sort((a,b) => a.localeCompare(b, 'pt-BR')),
|
||||
'dl-gerente': [...new Set([...config.gerentes])].sort((a,b) => a.localeCompare(b, 'pt-BR')),
|
||||
'dl-despesa': [...new Set([...config.despesas])].sort((a,b) => a.localeCompare(b, 'pt-BR')),
|
||||
// Clientes: escrita livre — sem datalist fixo
|
||||
'dl-vales': [...new Set([...config.vales_nomes])].sort((a,b) => a.localeCompare(b, 'pt-BR')),
|
||||
};
|
||||
Object.entries(lists).forEach(([id, items]) => {
|
||||
let dl = document.getElementById(id);
|
||||
if (!dl) { dl = document.createElement('datalist'); dl.id = id; document.body.appendChild(dl); }
|
||||
dl.innerHTML = items.map(v => `<option value="${v}">`).join('');
|
||||
});
|
||||
// Clientes: sem datalist (escrita livre)
|
||||
const clienteDl = document.getElementById('dl-cliente');
|
||||
if (clienteDl) clienteDl.remove();
|
||||
}
|
||||
|
||||
// ─── Init ─────────────────────────────────────────────────────────────────
|
||||
@@ -1060,6 +1079,7 @@ function openConfig() {
|
||||
renderCfgList('gerentes', 'cfg-gerentes-list');
|
||||
renderCfgList('despesas', 'cfg-despesas-list');
|
||||
renderCfgList('clientes', 'cfg-clientes-list');
|
||||
renderCfgList('vales_nomes', 'cfg-vales-list');
|
||||
}
|
||||
function closeConfig() { document.getElementById('modal-config').classList.remove('open'); }
|
||||
function renderCfgList(key, containerId) {
|
||||
@@ -1083,10 +1103,10 @@ document.getElementById('btn-config').addEventListener('click', openConfig);
|
||||
document.getElementById('modal-close').addEventListener('click', closeConfig);
|
||||
document.getElementById('modal-config').addEventListener('click', e => { if (e.target.id === 'modal-config') closeConfig(); });
|
||||
|
||||
['operador','gerente','despesa','cliente'].forEach((_, i) => {
|
||||
const btnMap = ['cfg-add-operador','cfg-add-gerente','cfg-add-despesa','cfg-add-cliente'];
|
||||
const keyMap = ['operadores','gerentes','despesas','clientes'];
|
||||
const listMap = ['cfg-operadores-list','cfg-gerentes-list','cfg-despesas-list','cfg-clientes-list'];
|
||||
['operador','gerente','despesa','cliente','vales'].forEach((_, i) => {
|
||||
const btnMap = ['cfg-add-operador','cfg-add-gerente','cfg-add-despesa','cfg-add-cliente','cfg-add-vales'];
|
||||
const keyMap = ['operadores','gerentes','despesas','clientes','vales_nomes'];
|
||||
const listMap = ['cfg-operadores-list','cfg-gerentes-list','cfg-despesas-list','cfg-clientes-list','cfg-vales-list'];
|
||||
document.getElementById(btnMap[i]).addEventListener('click', () => {
|
||||
const k = keyMap[i]; config[k].push(''); renderCfgList(k, listMap[i]);
|
||||
const inputs = document.getElementById(listMap[i]).querySelectorAll('input');
|
||||
|
||||
Reference in New Issue
Block a user