Merge branch 'develop/Ronald' into 'master'
Develop/ronald See merge request m3f_usm/admin_transporte/frontend!3develop/Ronald
commit
07031c2a9e
|
@ -20,6 +20,7 @@
|
|||
"history": "^5.3.0",
|
||||
"svelte-navigator": "^3.2.2",
|
||||
"svelte-pagination": "^0.0.1",
|
||||
"svelte-qrcode": "^1.0.0"
|
||||
"svelte-qrcode": "^1.0.0",
|
||||
"chart.js": "^3.7.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
<div class="row text-muted">
|
||||
<div class="col-6 text-start">
|
||||
<p class="mb-0">
|
||||
<a class="text-muted" href="https://www.empresa.com/"
|
||||
<a class="text-muted" href="https://tdt-dev.ilab.cl/"
|
||||
target="_blank" rel="noreferrer">
|
||||
<strong>.</strong>
|
||||
</a> ©
|
||||
<strong></strong>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<ul class="list-inline">
|
||||
<li class="list-inline-item">
|
||||
<a class="text-muted" href="https://www.empresa.com/support/" target="_blank" rel="noreferrer">Soporte</a>
|
||||
<a class="text-muted" href="https://tdt-dev.ilab.cl/" target="_blank" rel="noreferrer">Transformación Digital del Transporte 2024</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -48,6 +48,13 @@
|
|||
<i class="align-middle bi bi-card-list fs-4" />
|
||||
<span class="align-middle">Listado Archivos GTFS</span>
|
||||
</SideLink>
|
||||
|
||||
<li class="sidebar-header">Reportes</li>
|
||||
|
||||
<SideLink to='/reporte/itinerario'>
|
||||
<i class="align-middle bi bi-card-list fs-4" />
|
||||
<span class="align-middle">Itinerario</span>
|
||||
</SideLink>
|
||||
|
||||
<li class="sidebar-header">Mantenedores</li>
|
||||
|
||||
|
|
|
@ -123,6 +123,12 @@
|
|||
{#if ordering === '-status'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||
</th>
|
||||
|
||||
<th>
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('valid_from')}>Inico Vigencia</a>
|
||||
{#if ordering === 'status'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-status'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||
</th>
|
||||
|
||||
<th>
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('vigente')}>Vigente</a>
|
||||
{#if ordering === 'vigente'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
|
@ -144,6 +150,7 @@
|
|||
</td>
|
||||
<td>{app.created}</td>
|
||||
<td>{app.status}</td>
|
||||
<td>{app.valid_from}</td>
|
||||
<td>{app.vigente ? '✅':'🚫'}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
<script>
|
||||
import { getLineas } from "$/services/lineas";
|
||||
import { getOperadores } from "$/services/operadores";
|
||||
import { onMount } from 'svelte';
|
||||
export let id_operador;
|
||||
export let id_linea;
|
||||
export let codigo;
|
||||
export let ver_buses;
|
||||
export let ver_paraderos;
|
||||
export let loading = false;
|
||||
|
||||
let operadores = []
|
||||
let lineas = []
|
||||
let lineas_operador = []
|
||||
onMount(() => {
|
||||
getOperadores({ vigente: 1 })
|
||||
.then(data => data.sort((a,b) => a.nombre_operador < b.nombre_operador? -1 : 1))
|
||||
//.then(data => operadores = data)
|
||||
.then(data => {
|
||||
operadores = data;
|
||||
if (operadores.length > 0) {
|
||||
id_operador = operadores[0].id_operador;
|
||||
onChangeOperador();
|
||||
}
|
||||
})
|
||||
.catch(error => globalThis.toast.error(error))
|
||||
|
||||
getLineas({ vigente: 1 })
|
||||
.then(data => data.sort((a,b) => a.nombre_linea < b.nombre_linea? -1 : 1))
|
||||
.then(data => lineas = data)
|
||||
.catch(error => globalThis.toast.error(error))
|
||||
});
|
||||
|
||||
function onChangeOperador() {
|
||||
id_linea = ''
|
||||
ver_paraderos = false
|
||||
ver_buses = false
|
||||
if (!id_operador) {
|
||||
lineas_operador = []
|
||||
} else {
|
||||
const lineas_filtradas = lineas.filter(el => el.id_operador === id_operador);
|
||||
lineas_operador = lineas_filtradas.sort((a,b) => a.route_short_name < b.route_short_name ? -1 : 1);
|
||||
if (lineas_operador.length > 0) {
|
||||
id_linea = lineas_operador[0].id_linea;
|
||||
onChangeLinea();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onChangeLinea() {
|
||||
codigo = lineas.find(el => el.id_linea === id_linea)?.route_short_name || null
|
||||
ver_paraderos = false
|
||||
ver_buses = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Operador</div>
|
||||
<select bind:value={id_operador} class="form-select" on:change={onChangeOperador}>
|
||||
<option value=""></option>
|
||||
{#each operadores as operador}
|
||||
<option value={operador.id_operador}>{operador.nombre_operador}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Linea</div>
|
||||
<select bind:value={id_linea} class="form-select" style="font-family: monospace;" on:change={onChangeLinea}>
|
||||
<option value=""></option>
|
||||
{#each lineas_operador as linea}
|
||||
<option value={linea.id_linea}>{@html linea.route_short_name.padEnd(6).replace(/ /g,' ')} {linea.route_long_name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-auto">
|
||||
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="check-ver-buses-recorrido"
|
||||
disabled={!id_linea}
|
||||
bind:checked={ver_buses}>
|
||||
<label class="form-check-label" for="check-ver-buses-recorrido">Ver Buses del Recorrido</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="check-ver-paraderos"
|
||||
disabled={!id_linea}
|
||||
bind:checked={ver_paraderos}>
|
||||
<label class="form-check-label" for="check-ver-paraderos">Ver Paraderos</label>
|
||||
</div>
|
||||
|
||||
{#if loading}
|
||||
<div class="spinner-grow spinner-grow-sm text-danger" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,63 @@
|
|||
<script>
|
||||
import { getOperadores } from "$/services/operadores";
|
||||
|
||||
export let id_operador;
|
||||
export let loading = false;
|
||||
|
||||
let operadores = [];
|
||||
|
||||
getOperadores({ vigente: 1 })
|
||||
.then(data => data.sort((a, b) => a.nombre_operador < b.nombre_operador ? -1 : 1))
|
||||
.then(data => operadores = data)
|
||||
.catch(error => globalThis.toast.error(error));
|
||||
|
||||
function validarOperadorSeleccionado() {
|
||||
if (!id_operador) {
|
||||
globalThis.toast.error('Debe seleccionar un operador');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function abrirReporteItinerario() {
|
||||
if (!validarOperadorSeleccionado()) return;
|
||||
const url = `${window.location.href}/reporte_itinerarios_${id_operador}.pdf`;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
function abrirReporteExpediciones() {
|
||||
if (!validarOperadorSeleccionado()) return;
|
||||
const fecha = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
||||
const url = `${window.location.href}/reporte_expediciones_${id_operador}_${fecha}.pdf`;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Operador</div>
|
||||
<select bind:value={id_operador} class="form-select">
|
||||
{#each operadores as operador}
|
||||
<option value={operador.id_operador}>{operador.nombre_operador}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-auto">
|
||||
{#if loading}
|
||||
<div class="spinner-grow spinner-grow-sm text-danger" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group" role="group">
|
||||
<button on:click={abrirReporteItinerario} class="btn btn-primary">Reporte Itinerario</button>
|
||||
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
|
||||
<button on:click={abrirReporteExpediciones} class="btn btn-secondary">Reporte Expediciones</button>
|
||||
</div>
|
|
@ -0,0 +1,32 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import Chart from 'chart.js/auto';
|
||||
|
||||
export let comunasCounts = [];
|
||||
|
||||
onMount(() => {
|
||||
const ctx = document.getElementById('comunasChart').getContext('2d');
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: comunasCounts.map(item => item.id_comuna__nombre_comuna),
|
||||
datasets: [{
|
||||
label: '# de Paraderos',
|
||||
data: comunasCounts.map(item => item.total),
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<canvas id="comunasChart" width="400" height="400"></canvas>
|
|
@ -74,7 +74,7 @@
|
|||
|
||||
<div class="card flex-fill w-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Buses en recorrido por línea</h5>
|
||||
<h5 class="card-title mb-0">Buses en recorrido por ruta</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">Cantidad de Líneas</h5>
|
||||
<h5 class="card-title">Cantidad de Rutas</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
|
|
|
@ -1,32 +1,47 @@
|
|||
<script>
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import { getCountByComuna } from "$/services/paraderos";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
let comunasCounts = [];
|
||||
|
||||
onMount(() => {
|
||||
dispatch('loading', true);
|
||||
getCountByComuna({ vigente: 1 })
|
||||
.then(data => {
|
||||
comunasCounts = data.count_by_comuna.sort((a, b) => {
|
||||
|
||||
return a.id_comuna__nombre_comuna.localeCompare(b.id_comuna__nombre_comuna);
|
||||
});
|
||||
})
|
||||
.catch(error => console.error("Failed to load comunas counts:", error))
|
||||
.finally(() => dispatch('loading', false));
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col mt-0">
|
||||
<h5 class="card-title">Paraderos por comuna</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<div class="stat text-primary">
|
||||
<i class="bi bi-geo-alt fs-4"></i>
|
||||
</div>
|
||||
<h5 class="card-title">Paraderos por Comuna </h5>
|
||||
<div class="col-auto">
|
||||
<div class="stat text-primary">
|
||||
<i class="bi bi-map"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="mt-1 mb-3">2.382</h1>
|
||||
<table class="table mb-0">
|
||||
<table class="table table-striped table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Comuna</th>
|
||||
<th>Total Paraderos</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Chrome</td>
|
||||
<td class="text-end">4306</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Firefox</td>
|
||||
<td class="text-end">3801</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IE</td>
|
||||
<td class="text-end">1689</td>
|
||||
</tr>
|
||||
{#each comunasCounts as { id_comuna__nombre_comuna, total }}
|
||||
<tr>
|
||||
<td>{id_comuna__nombre_comuna}</td>
|
||||
<td>{total}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<PageTitle
|
||||
loading={loading1 || loading2 || loading3 || loading4 || loading5 }
|
||||
><strong>Análisis</strong> de datos</PageTitle>
|
||||
><strong>Resumen</strong> de datos</PageTitle>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-7">
|
||||
|
|
|
@ -21,6 +21,7 @@ import PageRutas from "$/pages/rutas/Home.svelte";
|
|||
import { getPermisosApp } from '$/services/usuarios'
|
||||
import { storePermisos } from '$/stores/global'
|
||||
import PageTipoCargo from '$/pages/tipo_cargo/Admin.svelte'
|
||||
import PageReporteItinerario from '$/pages/reportes/Itinerario.svelte'
|
||||
|
||||
export const routes_base = [
|
||||
{ path: '/', component: PageHome, public: true },
|
||||
|
@ -43,6 +44,7 @@ export const routes_base = [
|
|||
{ path: '/paraderos', component: PageParaderos },
|
||||
{ path: '/rutas', component: PageRutas },
|
||||
{ path: '/tipo-cargo', component: PageTipoCargo },
|
||||
{ path: '/reporte/itinerario', component: PageReporteItinerario },
|
||||
{ path: '*', component: PageError, public: true },
|
||||
];
|
||||
|
||||
|
|
|
@ -132,4 +132,14 @@ export async function getCount(params) {
|
|||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
|
||||
export async function getCountByComuna(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/paraderos/count_by_comuna/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
});
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
return res.json();
|
||||
}
|
Loading…
Reference in New Issue