Romaneio: lista de veículos visível, busca com contador e logo escolhível
Traz para a tela as funções do programa original (Romaneiro PDF), na linha de
design do projeto:
- Veículos deixam de ser um <select> e viram lista visível com a contagem de
paradas de cada carro — que é a conferência feita antes de imprimir e ficava
invisível mostrando um veículo por vez. Cada item é link (?veiculo=), então
sobrevive ao 'voltar' e ao F5. No celular vira faixa horizontal.
- Na mesma coluna: motorista do veículo, 'PDF deste veículo' e 'PDF de TODOS'.
- Busca ganha 'Limpar busca' e contador vivo ('4 de 31 paradas'). A contagem
saiu do cabeçalho da tabela: dois números para a mesma coisa, um atualizando e
o outro não, é a divergência que a diretriz 1 proíbe.
- Logo do cabeçalho escolhível (Configuracao 'romaneio_logo', separado do
empresa_logo — o romaneio é documento do cliente). PNG/JPG até 2MB, gravado em
storage/logos/ e não em public/, com nome gerado pelo sistema.
This commit is contained in:
@@ -10,7 +10,7 @@ import { Controller } from "@hotwired/stimulus"
|
||||
// plano inteiro. A regra de reconciliação (Romaneios::Importador) só funciona com
|
||||
// escrita por campo.
|
||||
export default class extends Controller {
|
||||
static targets = ["celula", "preview", "veiculo", "motorista", "rotulo", "status", "linha"]
|
||||
static targets = ["celula", "preview", "itemVeiculo", "motorista", "rotulo", "status", "linha", "busca", "contador"]
|
||||
static values = { url: String, pdfUrl: String, veiculo: String }
|
||||
|
||||
// Recarregar o PDF a cada tecla travaria a tela; 600ms depois do último save é o
|
||||
@@ -19,6 +19,10 @@ export default class extends Controller {
|
||||
|
||||
connect() {
|
||||
this.timerPreview = null
|
||||
// Com 72 carros a lista rola; sem isto, ao trocar de veículo o selecionado
|
||||
// pode nascer fora da área visível e parece que a seleção não pegou.
|
||||
const atual = this.itemVeiculoTargets.find((a) => a.dataset.atual === "1")
|
||||
if (atual) atual.scrollIntoView({ block: "nearest", inline: "nearest" })
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
@@ -74,26 +78,50 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
// ── Navegação e filtro ──────────────────────────────────────────────────
|
||||
trocarVeiculo() {
|
||||
window.location.search = `?veiculo=${encodeURIComponent(this.veiculoTarget.value)}`
|
||||
}
|
||||
|
||||
// A lista de veículos são LINKS de verdade (o veículo vive na query string), então
|
||||
// clicar já navega sem JS. As setas só precisam achar o vizinho na mesma lista —
|
||||
// é o que o operador faz o dia inteiro: percorrer carro a carro.
|
||||
anterior() { this.mover(-1) }
|
||||
proximo() { this.mover(1) }
|
||||
|
||||
mover(passo) {
|
||||
const select = this.veiculoTarget
|
||||
const destino = select.selectedIndex + passo
|
||||
if (destino < 0 || destino >= select.options.length) return
|
||||
select.selectedIndex = destino
|
||||
this.trocarVeiculo()
|
||||
const itens = this.itemVeiculoTargets
|
||||
if (itens.length === 0) return
|
||||
|
||||
const atual = itens.findIndex((a) => a.dataset.atual === "1")
|
||||
const destino = (atual === -1 ? 0 : atual) + passo
|
||||
// Sem dar a volta de propósito: chegar na ponta e voltar ao primeiro carro faz
|
||||
// o operador reimprimir folha que já separou, sem perceber.
|
||||
if (destino < 0 || destino >= itens.length) return
|
||||
|
||||
window.location.href = itens[destino].href
|
||||
}
|
||||
|
||||
filtrar(event) {
|
||||
const termo = event.currentTarget.value.trim().toLowerCase()
|
||||
filtrar() {
|
||||
const termo = this.hasBuscaTarget ? this.buscaTarget.value.trim().toLowerCase() : ""
|
||||
let visiveis = 0
|
||||
|
||||
this.linhaTargets.forEach((linha) => {
|
||||
linha.hidden = termo !== "" && !linha.dataset.busca.includes(termo)
|
||||
const casa = termo === "" || linha.dataset.busca.includes(termo)
|
||||
linha.hidden = !casa
|
||||
if (casa) visiveis += 1
|
||||
})
|
||||
|
||||
// Sem o contador, uma busca que não casa com nada deixa a tabela vazia e
|
||||
// parece plano vazio — o operador acha que perdeu o romaneio.
|
||||
if (!this.hasContadorTarget) return
|
||||
const total = this.linhaTargets.length
|
||||
const plural = visiveis === 1 ? "parada" : "paradas"
|
||||
this.contadorTarget.textContent = termo === ""
|
||||
? `${total} ${total === 1 ? "parada" : "paradas"} nesta folha`
|
||||
: `${visiveis} de ${total} ${plural} — filtrando por "${termo}"`
|
||||
}
|
||||
|
||||
limparBusca() {
|
||||
if (!this.hasBuscaTarget) return
|
||||
this.buscaTarget.value = ""
|
||||
this.filtrar()
|
||||
this.buscaTarget.focus()
|
||||
}
|
||||
|
||||
atualizarPreview() {
|
||||
|
||||
Reference in New Issue
Block a user