Files
pyfleet/templates/viat/list.html
T
2026-09-03 19:40:44 +02:00

230 lines
9.8 KiB
HTML
Executable File

{% extends "base.html" %}
{% block title %}Via-T{% endblock %}
{% block content %}
<div class="page-header">
<div class="page-eyebrow">Gestión</div>
<div class="page-title"><i class="bi bi-broadcast"></i> Via-T</div>
<div class="page-subtitle">Listado de vehículos con dispositivo Via-T. Búsqueda por Via-T, m.oficial, m.reservada, marca, modelo, poblacion, unidad, tipo.</div>
</div>
<div class="crud-toolbar">
<div class="search-wrap" style="max-width:420px;flex:1;">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
<input type="text" id="searchInput"
placeholder="Buscar… palabras separadas por comas"
value="{{ q }}" autocomplete="off" style="padding-right:32px;" autofocus>
<button type="button" id="clearBtn"
style="display:{{ 'flex' if q else 'none' }};position:absolute;right:10px;top:50%;
transform:translateY(-50%);background:none;border:none;cursor:pointer;
color:var(--muted);padding:4px;align-items:center;z-index:2;"
onmouseover="this.style.color='var(--text)'"
onmouseout="this.style.color='var(--muted)'">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2.5" style="pointer-events:none;display:block;">
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
<div style="display:flex;gap:8px;align-items:center;">
<a href="{{ url_for('viat_exportar', q=q) }}" class="btn btn-ghost" id="btnExcel" title="Exportar Excel">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/>
<polyline points="14 2 14 8 20 8"/>
<line x1="12" y1="18" x2="12" y2="12"/>
<line x1="9" y1="15" x2="12" y2="18"/><line x1="15" y1="15" x2="12" y2="18"/>
</svg>
Excel
</a>
<button type="button" class="btn btn-ghost" id="btnPDF" title="Imprimir PDF">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="6 9 6 2 18 2 18 9"/>
<path d="M6 18H4a2 2 0 01-2-2v-5a2 2 0 012-2h16a2 2 0 012 2v5a2 2 0 01-2 2h-2"/>
<rect x="6" y="14" width="12" height="8"/>
</svg>
Imprimir - PDF
</button>
<button type="button" class="btn btn-ghost" id="btnCopiar" title="Copiar al portapapeles">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2"/>
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/>
</svg>
Copiar
</button>
</div>
</div>
<p id="counter" style="margin-bottom:10px;font-size:12px;color:var(--muted)">
{{ rows|length }} registro{{ 's' if rows|length != 1 }}{% if q %} — filtrado por «{{ q }}»{% endif %}
</p>
<div class="table-wrap">
<table id="tablaViat">
<thead>
<tr>
<th>Via-T</th>
<th>M. Oficial</th>
<th>M. Reservada</th>
<th>Marca</th>
<th>Modelo</th>
<th>Población</th>
<th>Unidad</th>
<th>Tipo</th>
</tr>
</thead>
<tbody id="tbody">
{% include 'viat/rows.html' %}
</tbody>
</table>
</div>
<script>
var input = document.getElementById('searchInput');
var tbody = document.getElementById('tbody');
var counter = document.getElementById('counter');
var clearBtn = document.getElementById('clearBtn');
var timer;
input.addEventListener('input', function() {
clearBtn.style.display = this.value ? 'flex' : 'none';
clearTimeout(timer);
timer = setTimeout(function() { buscar(input.value.trim()); }, 300);
});
clearBtn.addEventListener('click', function() {
input.value = '';
this.style.display = 'none';
input.focus();
buscar('');
});
function buscar(q) {
var btnExcel = document.getElementById('btnExcel');
if (btnExcel) {
btnExcel.href = '{{ url_for("viat_exportar") }}?q=' + encodeURIComponent(q);
}
fetch('{{ url_for("viat_list") }}?q=' + encodeURIComponent(q) + '&solo_filas=1')
.then(function(r) {
if (r.redirected || r.url.includes('/login')) {
window.location = '{{ url_for("login") }}'; return null;
}
return r.text();
})
.then(function(html) {
if (!html) return;
tbody.innerHTML = html;
var filas = document.querySelectorAll('#tbody tr[data-id]').length;
if (counter) {
counter.textContent = filas + ' registro' + (filas !== 1 ? 's' : '')
+ (q ? ' — filtrado por «' + q + '»' : '');
}
});
}
/* ── PDF ── */
document.getElementById('btnPDF').addEventListener('click', function() {
var titulo = 'Listado Via-T';
var fecha = new Date().toLocaleDateString('es-ES');
var heads = ['Via-T','M. Oficial','M. Reservada','Marca','Modelo','Población','Unidad','Tipo'];
var filas = '';
document.querySelectorAll('#tablaViat tbody tr[data-id]').forEach(function(tr) {
filas += '<tr>';
tr.querySelectorAll('td').forEach(function(td) {
filas += '<td>' + td.innerText.trim() + '</td>';
});
filas += '</tr>';
});
var html = `<!DOCTYPE html><html><head><meta charset="UTF-8"><title>${titulo}</title>
<style>
body{font-family:Arial,sans-serif;font-size:10px;color:#1a1d29;margin:20px}
h2{font-size:14px;margin-bottom:4px}.meta{font-size:9px;color:#666;margin-bottom:14px}
table{width:100%;border-collapse:collapse}
th{background:#5c6ef8;color:#fff;padding:6px 7px;text-align:left;font-size:9px}
td{padding:5px 7px;border-bottom:1px solid #e3e6ef;font-size:9px}
tr:nth-child(even) td{background:#eef0f8}
@media print{body{margin:10px}}
</style></head><body>
<h2>${titulo}</h2>
<div class="meta">Generado el ${fecha} · ${document.querySelectorAll('#tablaViat tbody tr[data-id]').length} registros</div>
<table><thead><tr>${heads.map(function(h){return '<th>'+h+'</th>';}).join('')}</tr></thead>
<tbody>${filas}</tbody></table></body></html>`;
var win = window.open('','_blank','width=1000,height=700');
win.document.write(html); win.document.close(); win.focus();
setTimeout(function(){ win.print(); }, 400);
});
/* ── COPIAR ── */
document.getElementById('btnCopiar').addEventListener('click', function() {
var btn = this;
var heads = ['Via-T','M. Oficial','M. Reservada','Marca','Modelo','Población','Unidad','Tipo'];
var lineas = [heads.join('\t')];
document.querySelectorAll('#tablaViat tbody tr[data-id]').forEach(function(tr) {
var fila = [];
tr.querySelectorAll('td').forEach(function(td) { fila.push(td.innerText.trim()); });
lineas.push(fila.join('\t'));
});
var texto = lineas.join('\n');
if (!texto.trim() || lineas.length <= 1) { alert('No hay datos para copiar.'); return; }
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(texto)
.then(function(){ mostrarCopiado(btn); })
.catch(function(){ copiarFallback(texto, btn); });
} else {
copiarFallback(texto, btn);
}
});
function copiarFallback(texto, btn) {
var ta = document.createElement('textarea');
ta.value = texto;
ta.style.cssText = 'position:fixed;top:0;left:0;width:1px;height:1px;opacity:0;';
document.body.appendChild(ta);
ta.focus(); ta.select(); ta.setSelectionRange(0, 99999);
var ok = false;
try { ok = document.execCommand('copy'); } catch(e) { ok = false; }
document.body.removeChild(ta);
if (ok) { mostrarCopiado(btn); }
else { alert('No se pudo copiar automáticamente.'); }
}
function mostrarCopiado(btn) {
var orig = btn.innerHTML;
btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg> Copiado';
btn.style.color = 'var(--success)'; btn.style.borderColor = 'var(--success)';
setTimeout(function(){ btn.innerHTML = orig; btn.style.color=''; btn.style.borderColor=''; }, 2000);
}
/* ── ORDENACIÓN ── */
(function() {
var estado = { col: -1, asc: true };
document.querySelectorAll('#tablaViat thead th').forEach(function(th, idx) {
th.style.cursor = 'pointer'; th.style.userSelect = 'none';
var icon = document.createElement('span');
icon.style.cssText = 'margin-left:5px;opacity:.3;font-size:10px;';
icon.textContent = '⇅'; icon.className = 'sort-icon';
th.appendChild(icon);
th.addEventListener('click', function() {
var filas = Array.from(document.querySelectorAll('#tablaViat tbody tr[data-id]'));
if (!filas.length) return;
var mismaCol = estado.col === idx;
estado.asc = mismaCol ? !estado.asc : true; estado.col = idx;
document.querySelectorAll('.sort-icon').forEach(function(ic){ ic.textContent='⇅'; ic.style.opacity='.3'; });
icon.textContent = estado.asc ? '↑' : '↓'; icon.style.opacity='1'; icon.style.color='var(--accent)';
filas.sort(function(a, b) {
var valA = (a.querySelectorAll('td')[idx]||{innerText:''}).innerText.trim().toLowerCase();
var valB = (b.querySelectorAll('td')[idx]||{innerText:''}).innerText.trim().toLowerCase();
var numA = parseFloat(valA.replace(/[^0-9.-]/g,'')), numB = parseFloat(valB.replace(/[^0-9.-]/g,''));
var r = (!isNaN(numA)&&!isNaN(numB)) ? numA-numB : valA.localeCompare(valB,'es');
return estado.asc ? r : -r;
});
var tb = document.querySelector('#tablaViat tbody');
filas.forEach(function(f){ tb.appendChild(f); });
});
});
})();
</script>
{% endblock %}