actualizado 3-sept

This commit is contained in:
2026-09-03 19:40:44 +02:00
parent 89992823b4
commit 5d2912f789
4338 changed files with 350157 additions and 386 deletions
+12 -69
View File
@@ -12,10 +12,8 @@
</script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Space+Grotesk:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="static/css/animate.min.css"/>
<!-- <link rel="stylesheet" href="{{ url_for('static', filename='css/animate.css') }}"> -->
<!-- <link rel="stylesheet" type="text/css" href="DataTables/DataTables-1.11.4/css/dataTables.bootstrap5.css"/> -->
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"> -->
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/animate.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap-icons-1.13.1/bootstrap-icons.css') }}">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
@@ -647,8 +645,7 @@
</div>
<div class="nav-logo">
<span class="dot"></span>PyFleet
<!-- <span class="theme-switch-label">Fleet Operations Platform</span> -->
<span></span><h3>PyFleet</h3>
</div>
<div class="theme-switch-row">
@@ -660,103 +657,49 @@
<div class="nav-links">
<a href="{{ url_for('index') }}" class="{% if page == 'inicio' %}active{% endif %}">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/>
</svg>
<i class="bi bi-house"style="font-size: 1rem;"></i>
Inicio
<!-- {% if session.get('mensaje') and page == 'inicio' %}<span class="msg-badge">!</span>{% endif %} -->
</a>
<!-- <a href="{{ url_for('explorar') }}" class="{% if page == 'explorar' %}active{% endif %}">
<svg class="icon" 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>
Explorar
{% if session.get('mensaje') and page == 'explorar' %}<span class="msg-badge">!</span>{% endif %}
</a> -->
<a href="{{ url_for('vehiculos_list') }}" class="{% if page == 'vehiculos' %}active{% endif %}">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 17H3a2 2 0 01-2-2V9a2 2 0 012-2h11l4 4v4a2 2 0 01-2 2h-1"/><circle cx="7" cy="17" r="2"/><circle cx="17" cy="17" r="2"/>
</svg>
<i class="bi bi-car-front" style="font-size: 1rem;"></i>
Vehículos
</a>
<a href="{{ url_for('itv_list') }}" class="{% if page == 'itv' %}active{% endif %}">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/>
<line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>
<path d="M9 16l2 2 4-4"/>
</svg>
<i class="bi bi-calendar-check" style="font-size: 1rem;"></i>
ITV
</a>
<a href="{{ url_for('viat_list') }}" class="{% if page == 'viat' %}active{% endif %}">
<svg class="icon" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<rect x="5" y="7" width="10" height="10" rx="2"/>
<circle cx="10" cy="12" r="1"/>
<path d="M17 9c1.7 1.7 1.7 4.3 0 6"/>
<path d="M19.5 6.5c3 3 3 8 0 11"/>
</svg>
<i class="bi bi-broadcast" style="font-size: 1rem;"></i>
Via-T
</a>
<!-- <a href="{{ url_for('campus_list') }}" class="{% if page == 'campus' %}active{% endif %}">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 7V5a2 2 0 00-2-2h-4a2 2 0 00-2 2v2"/><line x1="12" y1="12" x2="12" y2="16"/><line x1="10" y1="14" x2="14" y2="14"/>
</svg>
Campus
</a> -->
<a href="{{ url_for('contacto') }}" class="{% if page == 'contacto' %}active{% endif %}">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/>
</svg>
<i class="bi bi-person-vcard" style="font-size: 1rem;"></i>
Contacto
{% if session.get('mensaje') and page == 'contacto' %}<span class="msg-badge">!</span>{% endif %}
</a>
<div class="nav-submenu" style="margin-top: 100px;">
<hr style="opacity: 0.5;margin-bottom: 5px;">
<button class="nav-submenu-toggle {% if page in ['poblaciones','unidades','tipo'] %}active{% endif %}"
onclick="toggleSubmenu(this)">
<svg class="icon" viewBox="0 0 24 24" fill="none" style="width: 15px;" stroke="currentColor" stroke-width="2">
<ellipse cx="12" cy="5" rx="9" ry="3"/>
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
</svg>
<i class="bi bi-database-check" style="font-size: 1rem;"></i>
Configuración
<svg class="chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="margin-top:10px;margin-left:auto;transition:transform .2s">
<polyline points="6 9 12 15 18 9"/>
</svg>
</button>
<div class="nav-submenu-items {% if page in ['poblaciones','unidades','tipo'] %}open{% endif %}">
<!-- <a href="{{ url_for('poblaciones_list') }}" class="{% if page == 'poblaciones' %}active{% endif %}">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/>
<path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"/>
</svg>
Poblaciones
</a> -->
<a href="{{ url_for('unidades_list') }}" class="{% if page == 'unidades' %}active{% endif %}">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/>
<rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
</svg>
<i class="bi bi-building"></i>
Unidades
</a>
<a href="{{ url_for('tipo_list') }}" class="{% if page == 'tipo' %}active{% endif %}">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/>
<line x1="8" y1="18" x2="21" y2="18"/>
<line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/>
<line x1="3" y1="18" x2="3.01" y2="18"/>
</svg>
<i class="bi bi-bus-front"></i>
Tipo vehiculo
</a>
</div>
@@ -867,6 +810,6 @@
items.classList.contains('open') ? 'rotate(180deg)' : '';
}
</script>
∫
</body>
</html>
+1 -1
View File
@@ -20,7 +20,7 @@
<div class="page-header">
<div class="page-eyebrow">Comunicación</div>
<div class="page-title">Contacto</div>
<div class="page-title"><i class="bi bi-person-vcard"></i> Contacto</div>
</div>
<div class="card-grid">
+1 -1
View File
@@ -4,7 +4,7 @@
{% block content %}
<div class="page-header">
<div class="page-eyebrow">Panel principal</div>
<div class="page-title">Bienvenido a PyFleet</div>
<div class="page-title"><i class="bi bi-house"></i> Bienvenido a PyFleet</div>
<div class="page-subtitle">Gestión de flota.</div>
</div>
+5 -5
View File
@@ -50,7 +50,7 @@
{% block content %}
<div class="page-header">
<div class="page-eyebrow">Revisiones</div>
<div class="page-title">Control de ITV</div>
<div class="page-title"><i class="bi bi-calendar-check"></i> Control de ITV</div>
<div class="page-subtitle">Filtra los vehículos por fecha de vencimiento de la ITV, población, unidad, tipo vehículo.</div>
</div>
@@ -170,7 +170,7 @@
<tr>
<th hidden>ID</th>
<th>M. Oficial</th>
<th>M. Civil</th>
<th>M. Reservada</th>
<th>Marca</th>
<th>Modelo</th>
<th>Tipo</th>
@@ -190,7 +190,7 @@
onmouseout="this.style.background=''">
<td hidden class="td-id">#{{ r['id'] }}</td>
<td>{{ r['m_oficial'] or '—' }}</td>
<td>{{ r['m_civil'] or '—' }}</td>
<td>{{ r['m_reservada'] or '—' }}</td>
<td><strong>{{ r['marca'] or '—' }}</strong></td>
<td>{{ r['modelo'] or '—' }}</td>
<td>{{ r['tipo'] or '—' }}</td>
@@ -261,7 +261,7 @@
var desde = '{{ desde }}', hasta = '{{ hasta }}';
var titulo = 'Control ITV' + (desde ? ' — Desde: ' + desde : '') + (hasta ? ' Hasta: ' + hasta : '');
var fecha = new Date().toLocaleDateString('es-ES');
var heads = ['M. Oficial','M. Civil','Marca','Modelo','Tipo','Poblacion',"Unidad",'Fecha ITV','Estado'];
var heads = ['M. Oficial','M. Reservada','Marca','Modelo','Tipo','Poblacion',"Unidad",'Fecha ITV','Estado'];
var filas = '';
document.querySelectorAll('#tablaITV tbody tr').forEach(function(tr) {
@@ -296,7 +296,7 @@
// COPIAR
document.getElementById('btnCopiar') && document.getElementById('btnCopiar').addEventListener('click', function() {
var btn = this;
var heads = ['M. Oficial','M. Civil','Marca','Modelo','Tipo','Poblacion ','Unidad','Fecha ITV','Estado'];
var heads = ['M. Oficial','M. Reservada','Marca','Modelo','Tipo','Poblacion ','Unidad','Fecha ITV','Estado'];
var lineas = [heads.join('\t')];
-122
View File
@@ -1,122 +0,0 @@
{% extends "base.html" %}
{% block title %}Poblaciones{% endblock %}
{% block content %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap-icons/bootstrap-icons.css') }}">
<div class="page-header">
<div class="page-eyebrow">Configuración</div>
<div class="page-title">Poblaciones</div>
<div class="page-subtitle">Gestiona las poblaciones administradas.</div>
</div>
<div style="display:flex;gap:24px;align-items:flex-start;flex-wrap:wrap;">
{# Formulario añadir #}
<div style="background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:24px;min-width:260px;">
<p class="form-label" style="margin-bottom:14px;">Nueva población</p>
<form method="POST" action="{{ url_for('poblaciones_nueva') }}">
<div class="form-group">
<input class="form-input" type="text" name="poblacion"
placeholder="Nombre de la población" required autofocus>
</div>
<button type="submit" class="btn btn-primary" style="width:100%;margin-top:8px;">
✚ Añadir
</button>
</form>
</div>
{# Listado #}
<div style="flex:1;min-width:280px;max-width: 800px;">
<p style="margin-top:8px;font-size:12px;color:var(--muted)">{{ rows|length }} poblaciones</p>
<div class="table-wrap border">
<table id="tabla" class="">
<thead class="">
<tr class="">
<th hidden >ID</th>
<th>Población</th>
<!-- <th style="text-align:right">Acción</th> -->
</tr>
</thead>
<tbody>
{% for r in rows %}
{% set nombre_pob = r['poblacion'] %}
<tr class="border-bottom">
<td hidden class="td-id">{{ r['id'] }}</td>
<td>{{ r['poblacion'] }}</td>
</tr>
{% else %}
<tr><td colspan="3"><div class="empty-state">Sin poblaciones registradas.</div></td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/jquery-3.7.1.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/tabledit.min.js') }}"></script>
<script>
$('#tabla').Tabledit({
url:'/editar_poblaciones',
editButton:true,
deleteButton:true,
restoreButton:false,
hideIdentifier:false,
buttons:{
edit:{
class:'btn btn-sm border rounded me-1',
html:'<i class="bi bi-pencil"></i> '
},
delete:{
class:'btn btn-sm btn-light text-danger border me-1',
html:'<i class="bi bi-trash"></i>'
// html:'Borrar'
},
save:{
class:'btn btn-sm btn-success',
html:'Guardar'
},
confirm:{
class:'btn btn-sm btn-warning',
html:'¿Seguro?'
}
},
columns:{
identifier:[0,'id'],
editable:[
[1,'poblacion']
]
},
onSuccess:function(data){
if (data.action == "edit") {
location.reload();
}
if (data.action == "delete") {
location.reload();
}
console.log(data);
}
});
$('#tabla td:last-child').attr(
'style',
'white-space: nowrap; width:185px;'
);
</script>
{% endblock %}
+1 -1
View File
@@ -9,7 +9,7 @@
<div class="page-header">
<div class="page-eyebrow">Configuración</div>
<div class="page-title">Tipo vehículo</div>
<div class="page-title"><i class="bi bi-bus-front"></i> Tipo vehículo</div>
<div class="page-subtitle">Gestiona los tipos de vehículos.</div>
</div>
+1 -1
View File
@@ -11,7 +11,7 @@
<div class="page-header">
<div class="page-eyebrow">Configuración</div>
<div class="page-title">Unidades</div>
<div class="page-title"><i class="bi bi-building"></i> Unidades</div>
<div class="page-subtitle">Gestiona los unidades a las que se asignan los vehículos.</div>
</div>
+828
View File
@@ -0,0 +1,828 @@
```html
{% extends "base.html" %}
{% block title %}Unidades{% endblock %}
{% block content %}
<link
rel="stylesheet"
href="https://unpkg.com/[email protected]/dist/css/tabulator_bootstrap5.min.css"
>
<style>
/* CONTENEDOR TABULATOR */
#tabla-unidades {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
}
/* CABECERA */
.unidades-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 10px;
}
.unidades-search {
position: relative;
width: 500px;
max-width: 100%;
}
.unidades-search svg {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
width: 17px;
height: 17px;
color: var(--muted);
pointer-events: none;
}
.unidades-search input {
width: 100%;
padding: 9px 38px 9px 38px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--surface);
color: var(--text);
outline: none;
}
.unidades-search input:focus {
border-color: var(--primary);
}
.clear-search {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
display: none;
align-items: center;
justify-content: center;
width: 26px;
height: 26px;
border: 0;
background: transparent;
color: var(--muted);
cursor: pointer;
border-radius: 5px;
}
.clear-search:hover {
background: var(--border);
color: var(--text);
}
/* =====================================================
TABULATOR
===================================================== */
.tabulator {
border: 0 !important;
background: var(--surface);
font-size: 14px;
}
.tabulator .tabulator-header {
background: #f8f9fa;
border-bottom: 1px solid var(--border);
}
.tabulator .tabulator-header .tabulator-col {
background: transparent;
border-right: 0;
}
.tabulator .tabulator-row {
background: var(--surface);
border-bottom: 1px solid var(--border);
min-height: 48px;
}
.tabulator .tabulator-row:hover {
background: rgba(0,0,0,.025);
}
/* .tabulator .tabulator-cell {
display: flex;
align-items: center;
border-right: 0;
} */
/* ESTADO */
#estadoTabla {
font-size: 12px;
color: var(--muted);
margin-top: 8px;
min-height: 18px;
}
/* =====================================================
RESPONSIVE
===================================================== */
@media (max-width: 700px) {
.unidades-toolbar {
flex-direction: column;
align-items: stretch;
}
.unidades-search {
width: 100%;
}
}
</style>
<!-- =========================================================
CABECERA
========================================================== -->
<div class="page-header">
<div class="page-eyebrow">
Configuración
</div>
<div class="page-title">
<i class="bi bi-building"></i>
Unidades
</div>
<div class="page-subtitle">
Gestiona las unidades a las que se asignan los vehículos.
</div>
</div>
<div style="
display:flex;
gap:24px;
align-items:flex-start;
flex-wrap:wrap;
">
<!-- =====================================================
FORMULARIO NUEVA UNIDAD
====================================================== -->
<div style="
background:var(--surface);
border:1px solid var(--border);
border-radius:var(--radius);
padding:24px;
min-width:400px;
">
<p
class="form-label"
style="margin-bottom:14px;"
>
Nueva unidad
</p>
<form
method="POST"
action="{{ url_for('unidades_nueva') }}"
>
<!-- POBLACIÓN -->
<div class="form-group">
<label class="form-label">
Población
</label>
<div class="select-wrap">
<select
class="form-input form-select"
name="poblacion_id"
required
>
<option value="">
— Selecciona población —
</option>
{% for p in poblaciones %}
<option value="{{ p['id'] }}">
{{ p['poblacion'] }}
</option>
{% endfor %}
</select>
<svg
class="select-arrow"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
>
<polyline points="6 9 12 15 18 9"/>
</svg>
</div>
</div>
<!-- UNIDAD -->
<div class="form-group">
<label class="form-label">
Unidad
</label>
<input
class="form-input"
type="text"
name="unidad"
placeholder="Nombre de la unidad"
required
>
</div>
<button
type="submit"
class="btn btn-sm btn-primary"
style="
width:50%;
margin-top:12px;
display:block;
margin-left:auto;
margin-right:auto;
"
>
✚ Añadir
</button>
</form>
</div>
<!-- =====================================================
LISTADO
====================================================== -->
<div style="
flex:1;
min-width:320px;
max-width:800px;
">
<!-- TOOLBAR -->
<div class="unidades-toolbar">
<!-- BUSCADOR -->
<div class="unidades-search">
<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="searchUnidades"
placeholder="Buscar por población o unidad…"
autocomplete="off"
autofocus
>
<button
type="button"
id="clearSearch"
class="clear-search"
>
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
>
<line
x1="18"
y1="6"
x2="6"
y2="18"
/>
<line
x1="6"
y1="6"
x2="18"
y2="18"
/>
</svg>
</button>
</div>
</div>
<!-- CONTADOR -->
<div
id="contador"
style="
margin:8px 0;
font-size:12px;
color:var(--muted);
"
>
{{ rows|length }}
unidad{% if rows|length != 1 %}es{% endif %}
</div>
<!-- TABLA -->
<div id="tabla-unidades"></div>
<!-- ESTADO -->
<div id="estadoTabla"></div>
</div>
</div>
<!-- DATOS PARA TABULATOR=============================== -->
<script>
const unidades = [
{% for r in rows %}
{
id: {{ r['id'] | tojson }},
poblacion_id:
{{ r['poblacion_id'] | tojson }},
poblacion:
{{ (r['poblacion'] or '') | tojson }},
unidad:
{{ r['unidad'] | tojson }}
}
{% if not loop.last %},{% endif %}
{% endfor %}
];
const poblaciones = [
{% for p in poblaciones %}
{
id: {{ p['id'] | tojson }},
nombre:
{{ p['poblacion'] | tojson }}
}
{% if not loop.last %},{% endif %}
{% endfor %}
];
</script>
<!-- TABULATOR -->
<script
src="https://unpkg.com/[email protected]/dist/js/tabulator.min.js">
</script>
<script>
// =========================================================
// ELEMENTOS
// =========================================================
const buscador =
document.getElementById(
"searchUnidades"
);
const clearSearch =
document.getElementById(
"clearSearch"
);
const contador =
document.getElementById(
"contador"
);
const estadoTabla =
document.getElementById(
"estadoTabla"
);
// =========================================================
// LISTA DE POBLACIONES PARA EL SELECT
// =========================================================
const opcionesPoblaciones = {};
poblaciones.forEach(function(p) {
opcionesPoblaciones[p.id] =
p.nombre;
});
// =========================================================
// CONTADOR
// =========================================================
function actualizarContador() {
const cantidad =
tabla.getDataCount("active");
contador.textContent =
cantidad +
" unidad" +
(cantidad !== 1 ? "es" : "");
}
// =========================================================
// MENSAJE
// =========================================================
function mostrarEstado(texto, error = false) {
estadoTabla.textContent = texto;
estadoTabla.style.color =
error
? "#dc3545"
: "#198754";
setTimeout(function() {
estadoTabla.textContent = "";
}, 3000);
}
// TABULATOR
const tabla = new Tabulator(
"#tabla-unidades",
{
data: unidades,
layout: "fitColumns",
height: "500px",
placeholder:
"Sin unidades registradas.",
editTriggerEvent: "click",
columns: [
{ title: "ID",
field: "id",
visible: false
},
{ title: "Población",
field: "poblacion_id",
minWidth: 220,
editor: "list",
editorParams: {
values:
opcionesPoblaciones,
autocomplete: true,
listOnEmpty: true
},
formatter: function(cell) {
const id =
cell.getValue();
return opcionesPoblaciones[id]
|| "—";
}
},
{
title: "Unidad",
field: "unidad",
editor: "input",
validator: "required",
minWidth: 250,
formatter: function(cell) {
const valor =
cell.getValue();
return valor
? `<strong>${escapeHtml(valor)}</strong>`
: "";
}
}
]
}
);
// ESCAPAR HTML
function escapeHtml(text) {
const div =
document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
// EDICIÓN DE CELDA
tabla.on(
"cellEdited",
async function(cell) {
const fila =
cell.getRow();
const datos =
fila.getData();
const campo =
cell.getField();
const valor =
cell.getValue();
console.log(
"Edición:",
{
id: datos.id,
campo: campo,
valor: valor
}
);
// Guardar en Flask
try {
const respuesta =
await fetch(
`/api/unidades/${datos.id}`,
{
method: "PATCH",
headers: {
"Content-Type":
"application/json"
},
body: JSON.stringify({
campo: campo,
valor: valor
})
}
);
const resultado =
await respuesta.json();
if (!respuesta.ok) {
throw new Error(
resultado.error ||
"No se pudo guardar"
);
}
// Actualizar fila con respuesta de Flask
await fila.update({
id: resultado.id,
poblacion_id: resultado.poblacion_id,
poblacion: resultado.poblacion,
unidad: resultado.unidad
});
mostrarEstado(
"✓ Cambios guardados"
);
}
catch (error) {
console.error(
"Error:",
error
);
mostrarEstado(
error.message,
true
);
// Volvemos a obtener los datos originales
// desde el servidor.
tabla.replaceData(
unidades
);
}
}
);
// BUSCADOR
buscador.addEventListener(
"input",
function() {
const texto =
this.value
.trim()
.toLowerCase();
clearSearch.style.display =
texto
? "flex"
: "none";
if (!texto) {
tabla.clearFilter();
actualizarContador();
return;
}
tabla.setFilter(
function(data) {
const poblacion =
String(
data.poblacion || ""
).toLowerCase();
const unidad =
String(
data.unidad || ""
).toLowerCase();
return (
poblacion.includes(texto) ||
unidad.includes(texto)
);
}
);
setTimeout(
actualizarContador,
0
);
}
);
// LIMPIAR BUSCADOR
clearSearch.addEventListener(
"click",
function() {
buscador.value = "";
clearSearch.style.display =
"none";
tabla.clearFilter();
buscador.focus();
actualizarContador();
}
);
// CONTADOR INICIAL
actualizarContador();
</script>
{% endblock %}
```
+327 -48
View File
@@ -25,6 +25,8 @@
{% endblock %}
{% block content %}
<div class="page-header">
<div class="page-eyebrow">Vehículos</div>
<div class="page-title">{{ 'Editar vehículo ' ~ vehiculo['m_oficial'] if vehiculo else 'Nuevo vehículo' }}</div>
@@ -55,11 +57,11 @@
</div>
<div class="form-group">
<label class="form-label" for="m_civil">Matrícula civil</label>
<input class="form-input" type="text" id="m_civil" name="m_civil"
<label class="form-label" for="m_reservada">Matrícula reservada</label>
<input class="form-input" type="text" id="m_reservada" name="m_reservada"
placeholder="Ej. 5678XYZ"
oninput="this.value = this.value.toUpperCase();"
value="{{ (vehiculo['m_civil'] or '') if vehiculo else request.form.get('m_civil', '') }}">
value="{{ (vehiculo['m_reservada'] or '') if vehiculo else request.form.get('m_reservada', '') }}">
</div>
<div class="form-group">
@@ -145,19 +147,6 @@
value="{{ (vehiculo['color'] or '') if vehiculo else request.form.get('color', '') }}">
</div>
<div class="form-group">
<label class="form-label" for="cilindrada">Cilindrada</label>
<input class="form-input" type="text" id="cilindrada" name="cilindrada"
placeholder="Ej. 1600cc"
value="{{ (vehiculo['cilindrada'] or '') if vehiculo else request.form.get('cilindrada', '') }}">
</div>
<div class="form-group">
<label class="form-label" for="potencia">Potencia (CV)</label>
<input class="form-input" type="number" id="potencia" name="potencia"
placeholder="Ej. 120"
value="{{ (vehiculo['potencia'] or '') if vehiculo else request.form.get('potencia', '') }}">
</div>
<div class="form-group">
<label class="form-label" for="combustible">Combustible</label>
@@ -166,13 +155,6 @@
value="{{ (vehiculo['combustible'] or '') if vehiculo else request.form.get('combustible', '') }}">
</div>
<div class="form-group">
<label class="form-label" for="neumaticos">Neumáticos</label>
<input class="form-input" type="text" id="neumaticos" name="neumaticos"
placeholder="Ej. 205/55 R16"
value="{{ (vehiculo['neumaticos'] or '') if vehiculo else request.form.get('neumaticos', '') }}">
</div>
<div class="form-group">
<label class="form-label" for="plazas">Plazas</label>
<input class="form-input" type="text" id="plazas" name="plazas"
@@ -180,27 +162,45 @@
value="{{ (vehiculo['plazas'] or '') if vehiculo else request.form.get('plazas', '') }}">
</div>
<div class="form-group" style="width: 280px;">
<label class="form-label"> </label>
<label style="margin-top:22px;display:flex;align-items:center;gap:10px;cursor:pointer;
padding:10px 14px;background:var(--bg);border:1px solid var(--border);
border-radius:8px;transition:border-color .15s;"
onmouseover="this.style.borderColor='var(--accent)'"
onmouseout="this.style.borderColor='var(--border)'">
<input type="checkbox" name="lanzadestellos" value="1"
{% if vehiculo and vehiculo['lanzadestellos'] == 1 %}checked{% endif %}
style="width:16px;height:16px;accent-color:var(--accent);cursor:pointer;">
<span style="font-size:13px;color:var(--text)">Lanzadestellos</span>
<span style="font-size:11px;color:var(--muted);margin-left:4px;">
<div style="display:flex; align-items:center; gap:12px;">
({{ '✓ Con lanzadestellos' if vehiculo and vehiculo['lanzadestellos'] == 1 else '✗ Sin lanzadestellos' }})
</span>
<!-- Caja del checkbox -->
<div class="form-group">
<label class="form-label"></label>
<label style="width: 160px; margin-top:22px; display:flex; align-items:center;
gap:10px;
cursor:pointer;
padding:10px 14px;
background:var(--bg);
border:1px solid var(--border);
border-radius:8px;
transition:border-color .15s;"
onmouseover="this.style.borderColor='var(--accent)'"
onmouseout="this.style.borderColor='var(--border)'">
<input type="checkbox" name="lanzadestellos" id="lanzadestellos" value="1"
{% if vehiculo and vehiculo['lanzadestellos'] == 1 %}checked{% endif %}
style=" width:16px; height:16px; accent-color:var(--accent); cursor:pointer;">
Lanzadestellos
<span
style="font-size:11px;color:var(--muted);margin-left:4px;"
id="texto-lanzadestellos">
<!-- {% if vehiculo and vehiculo['lanzadestellos'] == 1 %}
✓ Con lanzadestellos
{% else %}
✗ Sin lanzadestellos
{% endif %} -->
</span>
</label>
</div>
</div>
<i id="icono-lanzadestellos" class="bi bi-brightness-alt-high-fill" style="font-size:2rem;color:cornflowerblue; {% if not (vehiculo and vehiculo['lanzadestellos'] == 1) %}display:none;{% endif %}"></i>
</div>
</div>
{% if vehiculo %}
{% set sit = vehiculo['situacion'] or '' %}
{% if sit | upper == 'BAJA' %}
@@ -329,12 +329,15 @@
</div>
{% endif %}
<div class="form-actions" style="margin-top:28px">
<button type="submit" class="btn btn-primary">
{{ '💾 GUARDAR CAMBIOS' if vehiculo else '✚ Registrar vehículo' }}
<button type="submit" class="btn btn-primary"> <i class="bi bi-floppy" style="font-size: 1rem;"></i>
{{ 'GUARDAR CAMBIOS' if vehiculo else '✚ Registrar vehículo' }}
</button>
{# Cancelar #}
{% if origen == 'viat' %}
<a href="/viat?q={{ q_viat }}" class="btn btn-ghost">Cancelar</a>
{% elif desde or hasta or q_itv %}
@@ -346,11 +349,7 @@
<!-- <div style="display:flex;justify-content:space-between;align-items:center;margin-top:12px;"> -->
{% if vehiculo %}
<button type="button" class="btn btn-ghost" onclick="imprimirFicha()" title="Imprimir ficha del vehículo">
<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>
<i class="bi bi-printer"></i>
Ficha - Imprimir/PDF
</button>
{% endif %}
@@ -511,10 +510,175 @@
</div>
{% endif %}
{% if vehiculo %}
{# ── DOCUMENTOS PDF ── #}
<div class="section-title" style="margin-top:32px">Documentos PDF</div>
{# Listado de documentos existentes #}
{% if documentos %}
<div class="table-wrap" style="margin-bottom:16px;">
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Fecha</th>
<th style="text-align:right">Acciones</th>
</tr>
</thead>
<tbody>
{% for doc in documentos %}
<tr>
<td>
<div style="display:flex;align-items:center;gap:8px;">
<i class="bi bi-file-earmark-text" style="color: salmon;"></i>
<strong style="font-size:13px;">{{ doc['nombre'] }}</strong>
</div>
</td>
<td style="font-size:12px;color:var(--muted);">{{ doc['fecha'] }}</td>
<td style="text-align:right;">
<div style="display:flex;gap:6px;justify-content:flex-end;">
<a href="{{ url_for('documentos_ver', doc_id=doc['id']) }}"
target="_blank" class="btn btn-ghost btn-sm" title="Ver">
<i class="bi bi-eye"></i>
Ver
</a>
<a href="{{ url_for('documentos_descargar', doc_id=doc['id']) }}"
class="btn btn-ghost btn-sm" title="Descargar">
<i class="bi bi-download"></i>
Descargar
</a>
<form method="POST"
action="{{ url_for('documentos_eliminar', doc_id=doc['id']) }}">
<input type="hidden" name="vid" value="{{ vehiculo['id'] }}">
<input type="hidden" name="_q" value="{{ q|default('') }}">
<input type="hidden" name="_incl_bajas" value="{{ incl_bajas|default('0') }}">
<button type="button" class="btn btn-danger btn-sm"
onclick="if(confirm('¿Eliminar documento «{{ doc['nombre'] }}»?')) this.closest('form').submit()">
Eliminar
</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p style="color:var(--muted);font-size:13px;margin-bottom:16px;">
No hay documentos adjuntos para este vehículo.
</p>
{% endif %}
{# Formulario subir nuevo documento #}
<div style="background:var(--bg);border:1px solid var(--border);
border-radius:10px;padding:20px;">
<p style="font-size:11px;font-weight:600;letter-spacing:.7px;
text-transform:uppercase;color:var(--muted);margin-bottom:14px;">
Añadir documento <5Mb
</p>
<form method="POST"
action="{{ url_for('documentos_upload', id=vehiculo['id']) }}"
enctype="multipart/form-data">
<input type="hidden" name="_q" value="{{ q|default('') }}">
<input type="hidden" name="_incl_bajas" value="{{ incl_bajas|default('0') }}">
<input type="hidden" name="_desde" value="{{ desde|default('') }}">
<input type="hidden" name="_hasta" value="{{ hasta|default('') }}">
<input type="hidden" name="_q_itv" value="{{ q_itv|default('') }}">
<input type="hidden" name="_origen" value="{{ origen|default('') }}">
<input type="hidden" name="_q_viat" value="{{ q_viat|default('') }}">
<div class="form-grid" style="margin-bottom:12px;">
<div class="form-group" style="margin-bottom:0; width: 300px">
<label class="form-label">Nombre del documento</label>
<input class="form-input" style="background-color: white;" type="text" name="nombre_doc"
placeholder="Ej. Permiso circulación, Seguro, etc…">
</div>
<div class="form-group" style="margin-bottom:0;margin-left: 80px;" >
<label class="form-label">Archivo PDF</label>
<input class="form-control" type="file" name="pdf" id="pdfInput" accept=".pdf"
onchange="mostrarArchivo(this)"
style="font-size:13px;color:var(--text);padding:8px 0;">
</div>
</div>
<!-- style="font-size:13px;color:var(--text);padding:8px 0;" -->
<div id="pdfInfo" style="display:none;margin-bottom:12px;font-size:12px;
color:var(--accent);padding:8px 12px;
background:rgba(92,110,248,.08);border-radius:6px;">
</div>
<button type="submit" id="btnSubir" class="btn btn-primary btn-sm"
style="display:none;">
<i class="bi bi-upload"></i>
Subir documento
</button>
</form>
</div>
{% endif %}
</div> {# ← cierre de form-card #}
<script>
function mostrarArchivo(input) {
var info = document.getElementById('pdfInfo');
var btnSubir = document.getElementById('btnSubir');
if (input.files && input.files[0]) {
var f = input.files[0];
var size = (f.size / 1024).toFixed(1) + ' KB';
info.textContent = '📄 ' + f.name + ' (' + size + ')';
info.style.display = 'block';
btnSubir.style.display = 'inline-flex';
}
}
// mostrar archivo solo unico documento ficha tecnica.pdf
// function mostrarArchivo(input) {
// var nombre = document.getElementById('pdfNombre');
// var btnSubir = document.getElementById('btnSubir');
// if (input.files && input.files[0]) {
// nombre.textContent = '📄 ' + input.files[0].name;
// nombre.style.display = 'block';
// if (btnSubir) btnSubir.style.display = 'inline-flex';
// }
// }
function handleDrop(e) {
e.preventDefault();
document.getElementById('dropZone').style.borderColor = 'var(--border)';
var files = e.dataTransfer.files;
if (files.length && files[0].type === 'application/pdf') {
var input = document.getElementById('pdfInput');
input.files = files;
mostrarArchivo(input);
} else {
alert('Solo se permiten archivos PDF.');
}
}
</script>
<script>
const checkbox = document.getElementById('lanzadestellos');
const icono = document.getElementById('icono-lanzadestellos');
const texto = document.getElementById('texto-lanzadestellos');
checkbox.addEventListener('change', function () {
if (this.checked) {
icono.style.display = 'inline';
// texto.textContent = '✓ Con lanzadestellos';
} else {
icono.style.display = 'none';
// texto.textContent = '✗ Sin lanzadestellos';
}
});
</script>
<script>
@@ -524,6 +688,7 @@
}
}
function formatFecha(f) {
if (!f) return '';
// Si ya está en formato DD-MM-YYYY lo devuelve tal cual
@@ -613,7 +778,7 @@ function imprimirFicha() {
var v = {
id: '{{ vehiculo["id"] }}',
m_oficial: '{{ vehiculo["m_oficial"] or "" }}',
m_civil: '{{ vehiculo["m_civil"] or "" }}',
m_reservada: '{{ vehiculo["m_reservada"] or "" }}',
m_propia: '{{ vehiculo["m_propìa"] or "" }}',
denominacion: '{{ vehiculo["denominacion"] or "" }}',
marca: '{{ vehiculo["marca"] or "" }}',
@@ -740,7 +905,7 @@ function imprimirFicha() {
<div class="section-title">Identificación</div>
<table class="data-table">
${fila('M. oficial', v.m_oficial)}
${fila('M. civil', v.m_civil)}
${fila('M. reservada', v.m_reservada)}
${fila('M. propia', v.m_propia)}
${fila('VIN', v.VIN)}
${fila('Denominación', v.denominacion)}
@@ -803,3 +968,117 @@ function imprimirFicha() {
{% endblock %}
<!-- OPCION CARGAR SOLO PDF FICHA TECNICA -->
<!-- {% if vehiculo %}
{# ── DOCUMENTO PDF ── #}
<div class="section-title" style="margin-top:32px;">Ficha técnica PDF</div>
<div style="background:var(--surface);border:1px solid var(--border);
border-radius:var(--radius);padding:24px;margin-bottom:20px;">
{% if vehiculo['pdf'] %}
{# PDF existente #}
<div style="display:flex;align-items:center;justify-content:space-between;
flex-wrap:wrap;gap:12px;">
<div style="display:flex;align-items:center;gap:12px;">
<i class="bi bi-file-earmark-text" style="font-size: 2rem; color: salmon;"></i>
<div>
<div style="font-size:13px;font-weight:600;color:var(--text)">
{{ vehiculo['pdf'] }}
</div>
<div style="font-size:11px;color:var(--muted);margin-top:2px;">
Documento adjunto
</div>
</div>
</div>
<div style="display:flex;gap:8px;">
<a href="{{ url_for('vehiculos_pdf', id=vehiculo['id']) }}"
target="_blank" class="btn btn-ghost btn-sm">
<i class="bi bi-eye"></i>
Ver PDF
</a>
<a href="{{ url_for('vehiculos_pdf', id=vehiculo['id']) }}"
download class="btn btn-ghost btn-sm">
<i class="bi bi-download"></i>
Descargar
</a>
<form method="POST"
action="{{ url_for('vehiculos_pdf_eliminar', id=vehiculo['id']) }}">
<input type="hidden" name="_q" value="{{ q|default('') }}">
<input type="hidden" name="_incl_bajas" value="{{ incl_bajas|default('0') }}">
<button type="button" class="btn btn-danger btn-sm"
onclick="if(confirm('¿Eliminar el documento PDF?')) this.closest('form').submit()">
Eliminar PDF
</button>
</form>
</div>
</div>
{# Reemplazar PDF existente #}
<div style="margin-top:16px;padding-top:16px;border-top:1px solid var(--border);">
<p style="font-size:11px;color:var(--muted);margin-bottom:10px;">
Reemplazar documento actual:
</p>
<form method="POST"
action="{{ url_for('vehiculos_upload', id=vehiculo['id']) }}"
enctype="multipart/form-data"
style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
<input type="hidden" name="_q" value="{{ q|default('') }}">
<input type="hidden" name="_incl_bajas" value="{{ incl_bajas|default('0') }}">
<input type="hidden" name="_desde" value="{{ desde|default('') }}">
<input type="hidden" name="_hasta" value="{{ hasta|default('') }}">
<input type="hidden" name="_q_itv" value="{{ q_itv|default('') }}">
<input type="hidden" name="_origen" value="{{ origen|default('') }}">
<input type="hidden" name="_q_viat" value="{{ q_viat|default('') }}">
<input type="file" name="pdf" accept=".pdf"
style="font-size:13px;color:var(--text);">
<button type="submit" class="btn btn-ghost btn-sm">
<i class="bi bi-upload"></i>
Reemplazar
</button>
</form>
</div>
{% else %}
{# Sin PDF - formulario de subida #}
<form method="POST"
action="{{ url_for('vehiculos_upload', id=vehiculo['id']) }}"
enctype="multipart/form-data">
<input type="hidden" name="_q" value="{{ q|default('') }}">
<input type="hidden" name="_incl_bajas" value="{{ incl_bajas|default('0') }}">
<input type="hidden" name="_desde" value="{{ desde|default('') }}">
<input type="hidden" name="_hasta" value="{{ hasta|default('') }}">
<input type="hidden" name="_q_itv" value="{{ q_itv|default('') }}">
<input type="hidden" name="_origen" value="{{ origen|default('') }}">
<input type="hidden" name="_q_viat" value="{{ q_viat|default('') }}">
<div style="border:2px dashed var(--border);border-radius:10px;padding:32px;
text-align:center;transition:border-color .2s;"
id="dropZone"
ondragover="event.preventDefault();this.style.borderColor='var(--accent)'"
ondragleave="this.style.borderColor='var(--border)'"
ondrop="handleDrop(event)">
<i class="bi bi-file-earmark-arrow-down" style="font-size: 2rem; color: gray;"></i>
<p style="font-size:13px;color:var(--muted);margin-bottom:12px;">
Arrastra un PDF aquí o pulsa para seleccionar
</p>
<input type="file" name="pdf" id="pdfInput" accept=".pdf"
style="display:none;" onchange="mostrarArchivo(this)">
<label for="pdfInput" class="btn btn-ghost" style="cursor:pointer;">
Seleccionar PDF
</label>
<p id="pdfNombre" style="margin-top:10px;font-size:12px;color:var(--accent);display:none;"></p>
</div>
<button type="submit" id="btnSubir" class="btn btn-primary"
style="margin-top:14px;display:none;">
<i class="bi bi-upload"></i>
Subir PDF
</button>
</form>
{% endif %}
</div>
{% endif %} -->
+4 -4
View File
@@ -6,8 +6,8 @@
<div class="page-header">
<div class="page-eyebrow">Base de datos</div>
<div class="page-title">Vehículos</div>
<div class="page-subtitle">Gestión del parque móvil. Búsqueda por m.oficial, m.civil, VIN, marca, modelo, poblacion, unidad, denominación, tipo, operacion, situacion.</div>
<div class="page-title"><i class="bi bi-taxi-front"></i> Vehículos</div>
<div class="page-subtitle">Gestión del parque móvil. Búsqueda por m.oficial, m.reservada, VIN, marca, modelo, poblacion, unidad, denominación, tipo, operacion, situacion, lanzadestellos.</div>
</div>
<div class="crud-toolbar">
@@ -100,7 +100,7 @@
<tr>
<!-- <th hidden>ID</th> -->
<th>M. Oficial</th>
<th>M. Civil</th>
<th>M. Reservada</th>
<th>Marca</th>
<th>Modelo</th>
<th>Poblacion</th>
@@ -249,7 +249,7 @@
var btn = this;
// Leer filas EN EL MOMENTO del click, no antes
var heads = ['M. Oficial','M. Civil','Marca','Modelo','Poblacion','Unidad','Grupo','Denominación','Situación'];
var heads = ['M. Oficial','M. Reservada','Marca','Modelo','Poblacion','Unidad','Grupo','Denominación','Situación'];
var lineas = [heads.join('\t')];
document.querySelectorAll('#tbody tr[data-id]').forEach(function(tr) {
+7 -7
View File
@@ -7,7 +7,7 @@
onmouseout="this.style.background=''">
<!-- <td hidden class="td-id">#{{ r['id'] }}</td> -->
<td>{{ r['m_oficial'] or '—' }}</td>
<td>{{ r['m_civil'] or '—' }}</td>
<td>{{ r['m_reservada'] or '—' }}</td>
<!-- <td>{{ r['vin'] or '—' }}</td> -->
<td><strong>{{ r['marca'] or '—' }}</strong></td>
<td>{{ r['modelo'] or '—' }}</td>
@@ -25,17 +25,17 @@
{{ r['situacion'] }}
</span>
{% else %}—{% endif %}
{% if r['lanzadestellos'] == 1 %}
<i class="bi bi-brightness-alt-high-fill" style="margin-left: 10px; font-size: 1rem; color: cornflowerblue;"></i>
{% endif %}
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="10" rowspan="7">
<div class="empty-state" style="color: rgb(202, 145, 145);">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M5 17H3a2 2 0 01-2-2V9a2 2 0 012-2h11l4 4v4a2 2 0 01-2 2h-1"/>
<circle cx="7" cy="17" r="2"/><circle cx="17" cy="17" r="2"/>
</svg>
<div class="empty-state" style="color: rgb(194, 191, 191);">
<i class="bi bi-car-front" style="font-size: 2rem;"></i>
<br>
{% if q %}
No se encontraron resultados para <strong>"{{ q }}"</strong>.
@@ -57,7 +57,7 @@
<tr data-id="{{ r['id'] }}">
<td class="td-id">#{{ r['id'] }}</td>
<td>{{ r['m_oficial'] or '—' }}</td>
<td>{{ r['m_civil'] or '—' }}</td>
<td>{{ r['m_reservada'] or '—' }}</td>
<td><strong>{{ r['marca'] or '—' }}</strong></td>
<td>{{ r['modelo'] or '—' }}</td>
<td>{{ r['tipo'] or '—' }}</td>
+5 -5
View File
@@ -4,8 +4,8 @@
{% block content %}
<div class="page-header">
<div class="page-eyebrow">Gestión</div>
<div class="page-title">Via-T</div>
<div class="page-subtitle">Listado de vehículos con dispositivo Via-T. Búsqueda por Via-T, m.oficial, m.civil, marca, modelo, poblacion, unidad tipo.</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">
@@ -67,7 +67,7 @@
<tr>
<th>Via-T</th>
<th>M. Oficial</th>
<th>M. Civil</th>
<th>M. Reservada</th>
<th>Marca</th>
<th>Modelo</th>
<th>Población</th>
@@ -129,7 +129,7 @@
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. Civil','Marca','Modelo','Población','Unidad','Tipo'];
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>';
@@ -160,7 +160,7 @@
/* ── COPIAR ── */
document.getElementById('btnCopiar').addEventListener('click', function() {
var btn = this;
var heads = ['Via-T','M. Oficial','M. Civil','Marca','Modelo','Población','Unidad','Tipo'];
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 = [];
+1 -1
View File
@@ -7,7 +7,7 @@
onmouseout="this.style.background=''">
<td><strong>{{ r['viat'] or '—' }}</strong></td>
<td>{{ r['m_oficial'] or '—' }}</td>
<td>{{ r['m_civil'] or '—' }}</td>
<td>{{ r['m_reservada'] or '—' }}</td>
<td>{{ r['marca'] or '—' }}</td>
<td>{{ r['modelo'] or '—' }}</td>
<td>{{ r['poblacion'] or '—' }}</td>