forked from TDTP/admin_transporte_frontend
Compare commits
22 Commits
merge/fran
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
5af531837f | |
|
|
ad2f83cf3b | |
|
|
9a69c66b13 | |
|
|
2f51d94ea2 | |
|
|
c24acb7a75 | |
|
|
a334053420 | |
|
|
3407f855c1 | |
|
|
7abf1b3ab4 | |
|
|
9c1d9bf372 | |
|
|
a2687861a2 | |
|
|
cc109373b3 | |
|
|
78956d4e01 | |
|
|
181806faf2 | |
|
|
9486b40ecc | |
|
|
8e7168fbe4 | |
|
|
4e2d3c10c9 | |
|
|
07031c2a9e | |
|
|
d379ff956a | |
|
|
e947951f1c | |
|
|
fac33c2d8a | |
|
|
76a28aa8fa | |
|
|
08ce21218e |
|
|
@ -11,6 +11,8 @@
|
|||
"@adminkit/core": "^3.4.0",
|
||||
"bootstrap": "^5.3.0",
|
||||
"bootstrap-icons": "^1.10.5",
|
||||
"chart.js": "^3.9.1",
|
||||
"chartjs-plugin-datalabels": "^2.2.0",
|
||||
"history": "^5.3.0",
|
||||
"svelte-navigator": "^3.2.2",
|
||||
"svelte-pagination": "^0.0.1",
|
||||
|
|
@ -37,6 +39,15 @@
|
|||
"simplebar": "5.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@adminkit/core/node_modules/chart.js": {
|
||||
"version": "2.9.4",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz",
|
||||
"integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==",
|
||||
"dependencies": {
|
||||
"chartjs-color": "^2.1.0",
|
||||
"moment": "^2.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/polyfill": {
|
||||
"version": "7.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.12.1.tgz",
|
||||
|
|
@ -508,13 +519,9 @@
|
|||
"integrity": "sha512-ceOhN1DL7Y4O6M0j9ICgmTYziV89WMd96SvSl0REd8PMgrY0B/WBOPoed5S1KUmJqXgUXh8gzSe6E3ae27upsQ=="
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "2.9.4",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz",
|
||||
"integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==",
|
||||
"dependencies": {
|
||||
"chartjs-color": "^2.1.0",
|
||||
"moment": "^2.10.2"
|
||||
}
|
||||
"version": "3.9.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
|
||||
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w=="
|
||||
},
|
||||
"node_modules/chartjs-color": {
|
||||
"version": "2.4.1",
|
||||
|
|
@ -533,6 +540,14 @@
|
|||
"color-name": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chartjs-plugin-datalabels": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz",
|
||||
"integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==",
|
||||
"peerDependencies": {
|
||||
"chart.js": ">=3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/classnames": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
|
||||
|
|
@ -727,9 +742,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
"@adminkit/core": "^3.4.0",
|
||||
"bootstrap": "^5.3.0",
|
||||
"bootstrap-icons": "^1.10.5",
|
||||
"chart.js": "^3.9.1",
|
||||
"chartjs-plugin-datalabels": "^2.2.0",
|
||||
"history": "^5.3.0",
|
||||
"svelte-navigator": "^3.2.2",
|
||||
"svelte-pagination": "^0.0.1",
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
<script>
|
||||
import Paginate from "$/components/Paginate.svelte";
|
||||
import { getOperadores } from "$/services/operadores";
|
||||
import PageTitle from "$/components/PageTitle.svelte";
|
||||
import ModalOperador from "./ModalOperador.svelte";
|
||||
import { useLocation } from "svelte-navigator";
|
||||
import { getPermisosPath } from "$/services/usuarios";
|
||||
|
||||
const limit = 15;
|
||||
let page = 1;
|
||||
let offset = 0;
|
||||
let count = 0;
|
||||
let ordering = 'id_operador'
|
||||
let operadores = []
|
||||
let operador = null
|
||||
let verLineas =null
|
||||
let loading = false;
|
||||
let escritura = false;
|
||||
let location = useLocation()
|
||||
|
||||
getPermisosPath($location.pathname)
|
||||
.then(data => escritura = data.escritura)
|
||||
.catch(error => console.log({ error }))
|
||||
|
||||
$: onPage(page)
|
||||
async function onPage(p) {
|
||||
try {
|
||||
loading = true
|
||||
offset = (p - 1) * limit;
|
||||
const data = await getOperadores({ offset, limit, ordering })
|
||||
operadores = data.results;
|
||||
count = data.count;
|
||||
} catch (error) {
|
||||
globalThis.toast.error(error)
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onEdita(item) {
|
||||
operador = item;
|
||||
}
|
||||
|
||||
function onVerLineas(item){
|
||||
verLineas = item;
|
||||
operador = item;
|
||||
}
|
||||
|
||||
function onNuevo() {
|
||||
operador = {}
|
||||
}
|
||||
|
||||
function onOrderBy(field) {
|
||||
ordering = ordering === field ? '-' + field : field;
|
||||
onPage(page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<PageTitle {loading}>Operadores</PageTitle>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
{#if escritura}
|
||||
<button class="btn btn-primary" on:click|preventDefault={onNuevo}>
|
||||
<i class="bi bi-plus-lg"></i> Nuevo
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead>
|
||||
<tr class="table-light">
|
||||
<th style="width:5%">Nro</th>
|
||||
<th>
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('id_operador')}>ID</a>
|
||||
{#if ordering === 'id_operador'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-id_operador'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||
</th>
|
||||
<th>
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('nombre_operador')}>Nombre</a>
|
||||
{#if ordering === 'nombre_operador'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-nombre_operador'}<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}
|
||||
{#if ordering === '-vigente'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each operadores as app, index}
|
||||
<tr key={index}>
|
||||
<td class="table-light">{offset + index + 1}</td>
|
||||
<td>{app.id_operador}</td>
|
||||
<td><a href={"#"} on:click|preventDefault={() => onEdita(app)}>{app.nombre_operador}</a></td>
|
||||
<td>{app.vigente ? '✅':'🚫'}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex">
|
||||
<a href={"#"} class="btn btn-outline-secondary me-3" on:click|preventDefault={() => onPage(page)}>
|
||||
<i class="bi bi-arrow-repeat"></i>
|
||||
</a>
|
||||
<Paginate
|
||||
{limit}
|
||||
{count}
|
||||
on:page={ev => page = ev.detail}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if operador}
|
||||
<ModalOperador
|
||||
{operador}
|
||||
{escritura}
|
||||
on:close={() => operador = null}
|
||||
on:refresh={() => onPage(page)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.table-responsive {
|
||||
max-height: calc(100vh - 300px);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,26 +1,29 @@
|
|||
<script>
|
||||
import Paginate from "$/components/Paginate.svelte";
|
||||
|
||||
import { downloadGtfsArchivo, getGtfsArchivo,getGtfsArchivoId,getGtfsBase } from "$/services/gtfs_archivo";
|
||||
import PageTitle from "$/components/PageTitle.svelte";
|
||||
import PageTitle from "$/components/PageTitle.svelte";
|
||||
import { useLocation } from "svelte-navigator";
|
||||
import { getPermisosPath } from "$/services/usuarios";
|
||||
import ModalgtfsUpload from "./ModalgtfsUpload.svelte";
|
||||
import { getRedTransporte } from "$/services/red_transporte";
|
||||
import { space } from "svelte/internal";
|
||||
|
||||
let id_red;
|
||||
|
||||
let id_red;
|
||||
let escritura = false;
|
||||
|
||||
const limit = 15;
|
||||
let page = 1;
|
||||
let offset = 0;
|
||||
let count = 0;
|
||||
let ordering = 'id_archivo'
|
||||
let ordering = '-id_gtfs'
|
||||
let redes = []
|
||||
let lista_gtfs = []
|
||||
let location = useLocation()
|
||||
let loading = false;
|
||||
let showUpload = null;
|
||||
let baseUrlInforme = window.location.origin;
|
||||
|
||||
getPermisosPath($location.pathname)
|
||||
.then(data => escritura = data.escritura)
|
||||
.catch(error => console.log({ error }))
|
||||
|
|
@ -33,12 +36,12 @@
|
|||
|
||||
async function onPage(p) {
|
||||
try {
|
||||
if (!id_red) {
|
||||
if (!id_red) {
|
||||
return;
|
||||
}
|
||||
loading = true
|
||||
offset = (p - 1) * limit;
|
||||
const data = await getGtfsArchivo({id_red, offset, limit, ordering })
|
||||
const data = await getGtfsArchivo({id_red, offset, limit, ordering:'-id_gtfs'})
|
||||
lista_gtfs = data.results;
|
||||
count = data.count;
|
||||
} catch (error) {
|
||||
|
|
@ -69,7 +72,36 @@
|
|||
.finally(() => loading = false)
|
||||
}
|
||||
|
||||
|
||||
function reporteErroresCargaGTFS (id_red, id_gtfs){
|
||||
const url = new URL(`reporte/gtfs/reporte_${id_red}_${id_gtfs}.pdf`, baseUrlInforme);
|
||||
window.open(url.href, '_blank');
|
||||
}
|
||||
|
||||
function adjustDateTimeByOffset(isoString) {
|
||||
// Extraer la fecha, la hora y el desplazamiento de zona horaria
|
||||
const [datePart, timePart] = isoString.split('T');
|
||||
const [time, offset] = timePart.split(/(?=[-+])/); // Usa una expresión regular para dividir por el signo más cercano (+ o -)
|
||||
|
||||
// Convertir a fecha/hora UTC
|
||||
const dateTime = new Date(`${datePart}T${time}Z`);
|
||||
|
||||
// Extraer las horas y minutos del desplazamiento
|
||||
const offsetSign = offset[0] === '+' ? 1 : -1; // Determinar si el desplazamiento es positivo o negativo
|
||||
const [offsetHours, offsetMinutes] = offset.slice(1).split(':').map(Number);
|
||||
|
||||
// Ajustar la fecha/hora basándose en el desplazamiento
|
||||
dateTime.setUTCHours(dateTime.getUTCHours() + offsetSign * offsetHours);
|
||||
dateTime.setUTCMinutes(dateTime.getUTCMinutes() + offsetSign * offsetMinutes);
|
||||
|
||||
// Formatear y retornar la nueva fecha/hora ajustada
|
||||
const adjustedYear = dateTime.getUTCFullYear();
|
||||
const adjustedMonth = String(dateTime.getUTCMonth() + 1).padStart(2, '0'); // getUTCMonth() es 0-indexado
|
||||
const adjustedDate = String(dateTime.getUTCDate()).padStart(2, '0');
|
||||
const adjustedHours = String(dateTime.getUTCHours()).padStart(2, '0');
|
||||
const adjustedMinutes = String(dateTime.getUTCMinutes()).padStart(2, '0');
|
||||
|
||||
return `${adjustedDate}-${adjustedMonth}-${adjustedYear} ${adjustedHours}:${adjustedMinutes}`;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -106,27 +138,39 @@
|
|||
{#if ordering === '-id_gtfs'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||
</th>-->
|
||||
<th>
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('nombre_red')}>Archivo</a>
|
||||
{#if ordering === 'archivo'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-archivo'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('id_gtfs')}>ID</a>
|
||||
<!-- {#if ordering === 'id_gtfs'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-id_gtfs'}<i class="bi bi-caret-down-fill"></i>{/if}-->
|
||||
|
||||
</th>
|
||||
|
||||
<th>
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('created')}>Creado el</a>
|
||||
{#if ordering === 'created'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-created'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('archivo')}>Archivo</a>
|
||||
<!-- {#if ordering === 'archivo'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-archivo'}<i class="bi bi-caret-down-fill"></i>{/if}-->
|
||||
</th>
|
||||
|
||||
<th>
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('status')}>Estado</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}
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('created')}>Creado el</a>
|
||||
<!-- {#if ordering === 'created'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-created'}<i class="bi bi-caret-down-fill"></i>{/if}-->
|
||||
</th>
|
||||
|
||||
<th>
|
||||
<a href={"#"} on:click|preventDefault={() => onOrderBy('status')}>Estado A</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('valid_from')}>Inico Vigencia</a>
|
||||
<!-- {#if ordering === 'valid_from'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-valid_from'}<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}
|
||||
{#if ordering === '-vigente'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||
<!-- {#if ordering === 'vigente'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-vigente'}<i class="bi bi-caret-down-fill"></i>{/if} -->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -134,7 +178,7 @@
|
|||
{#each lista_gtfs as app, index}
|
||||
<tr>
|
||||
<td class="table-light">{offset + index + 1}</td>
|
||||
<!--<td>{app.id_gtfs}</td>-->
|
||||
<td>{app.id_gtfs}</td>
|
||||
<td>
|
||||
{#if !loading}
|
||||
<a href={"#"} on:click|preventDefault={() => onDownload(app)}>{app.archivo}</a>
|
||||
|
|
@ -142,8 +186,21 @@
|
|||
<span>{app.archivo}</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td>{app.created}</td>
|
||||
<td>{app.status}</td>
|
||||
<td>{adjustDateTimeByOffset(app.created)}</td>
|
||||
|
||||
<td >
|
||||
{#if app.status.toUpperCase().includes('PREP')}
|
||||
<!-- Elemento interactivo para usuarios con 'CON REPAROS' en el estado -->
|
||||
|
||||
<a href={"#"} on:click|preventDefault={() => reporteErroresCargaGTFS(id_red, app.id_gtfs)}>{app.status}</a>
|
||||
|
||||
{:else}
|
||||
<!-- Solo texto para estados sin 'CON REPAROS' -->
|
||||
<a href={"#"} on:click|preventDefault={() => reporteErroresCargaGTFS(id_red, app.id_gtfs)}>{app.status}</a>
|
||||
{/if}
|
||||
</td>
|
||||
|
||||
<td>{app.valid_from}</td>
|
||||
<td>{app.vigente ? '✅':'🚫'}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
|
|
@ -166,11 +223,11 @@
|
|||
|
||||
{#if showUpload}
|
||||
<ModalgtfsUpload on:close={() => showUpload = false} {id_red} on:refresh={() => onPage(page)} />
|
||||
|
||||
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.table-responsive {
|
||||
max-height: calc(100vh - 300px);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -7,12 +7,15 @@
|
|||
// services
|
||||
import { getRegiones } from "$/services/regiones"
|
||||
import { getComunas } from "$/services/comunas"
|
||||
import { getMarcasParaderos } from "$/services/mapas"
|
||||
import { getMarcasParaderos, getCoordenadasIniciales } from "$/services/mapas"
|
||||
|
||||
// libs
|
||||
import { storeParaderos } from "$/stores/global"
|
||||
import imagenParada from '$/assets/parada.png'
|
||||
import imagenParadaAzul from '$/assets/paradaAzul.png'
|
||||
import { onMount } from "svelte";
|
||||
import { getDispositivos } from "../../services/dispositivos";
|
||||
|
||||
|
||||
let myMap = null
|
||||
let elMap = null
|
||||
|
|
@ -22,6 +25,7 @@
|
|||
let comunas_x_region = []
|
||||
let L = null
|
||||
let iconParada = null
|
||||
let iconParadaAzul = null
|
||||
let markers = []
|
||||
let form = {}
|
||||
let parada = null
|
||||
|
|
@ -30,10 +34,22 @@
|
|||
|
||||
$: myMap && crear_marcadores_por_criterio()
|
||||
|
||||
|
||||
let center = { lat: 0, lng: 0 };
|
||||
let dispositivosConTotem
|
||||
//dispositivosConTotem = totemAsignado();
|
||||
|
||||
onMount(() => {
|
||||
if(globalThis.L) create_map();
|
||||
cargar_paraderos_todos($storeParaderos)
|
||||
})
|
||||
obtieneCorrdenadas();
|
||||
// No es necesario llamar a create_map() , la lógica se maneja a través de la reactividad de center
|
||||
});
|
||||
|
||||
// Reacciona a los cambios en center
|
||||
$: if(center && globalThis.L) {
|
||||
create_map();
|
||||
cargar_paraderos_todos($storeParaderos);
|
||||
|
||||
};
|
||||
|
||||
function create_map() {
|
||||
if (!elMap || !globalThis.L) return;
|
||||
|
|
@ -46,6 +62,14 @@
|
|||
popupAnchor: [0, -16]
|
||||
})
|
||||
}
|
||||
if (!iconParadaAzul) {
|
||||
iconParadaAzul = L.icon({
|
||||
iconUrl: imagenParadaAzul,
|
||||
iconSize: [32, 32],
|
||||
iconAnchor: [16, 32],
|
||||
popupAnchor: [0, -16]
|
||||
})
|
||||
}
|
||||
if (!myMap) {
|
||||
myMap = L.map(elMap)
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
|
|
@ -55,13 +79,19 @@
|
|||
|
||||
// obtener coordenadas actuales
|
||||
// centrar mapa en coordenadas del navegador
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
/* navigator.geolocation.getCurrentPosition(
|
||||
({ coords }) => {
|
||||
const { latitude, longitude } = coords;
|
||||
myMap.setView([latitude, longitude], 16)
|
||||
},
|
||||
(error) => console.log({ error })
|
||||
)
|
||||
)*/
|
||||
|
||||
|
||||
|
||||
// Configurar la vista del mapa en la ubicación de Concepción con un nivel de zoom, por ejemplo, 16
|
||||
myMap.setView(center, 15);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -74,6 +104,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
async function obtieneCorrdenadas () {
|
||||
try {
|
||||
const data = await getCoordenadasIniciales('?');
|
||||
center.lat = parseFloat(data.initialLat);
|
||||
center.lng = parseFloat(data.initialLng);
|
||||
console.log("Coordenadas de inicio:", data);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error al obtener las coordenadas iniciales :', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function cargar_paraderos_todos(data_default) {
|
||||
try {
|
||||
loading = true
|
||||
|
|
@ -118,11 +162,18 @@
|
|||
// filtro coincide a criterio
|
||||
return true
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 3. crear marcadores
|
||||
for (let mark of paraderos) {
|
||||
|
||||
const tieneTotem = false // dispositivosConTotem.some(dispositivo => dispositivo.id_paradero === mark.id_paradero);
|
||||
|
||||
const { lat, lng } = mark.position
|
||||
const marker = L.marker([lat, lng], { icon: iconParada }).addTo(myMap)
|
||||
const marker = L.marker([lat, lng], {icon: tieneTotem ? iconParadaAzul : iconParada }).addTo(myMap)
|
||||
const { title, location } = mark;
|
||||
const html = `${title}<br>${location}`
|
||||
|
||||
|
|
@ -149,6 +200,22 @@
|
|||
form.time_search && clearTimeout(form.time_search)
|
||||
form.time_search = setTimeout(() => crear_marcadores_por_criterio(), 1000)
|
||||
}
|
||||
|
||||
|
||||
async function totemAsignado( ) {
|
||||
try {
|
||||
|
||||
const dispTotem = await getDispositivos({ id_tipo_dispositivo: 1 });
|
||||
return dispTotem
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import { onMount } from "svelte";
|
||||
|
||||
// servicios
|
||||
import { getRutas } from "$/services/mapas";
|
||||
import { getRutas,getCoordenadasIniciales } from "$/services/mapas";
|
||||
import ModalLetreroLUR from "./ModalLetreroLUR.svelte";
|
||||
import FiltroRutas from "./FiltroRutas.svelte";
|
||||
import { getBusesLinea, getParaderosLinea } from "$/services/lineas";
|
||||
|
|
@ -39,15 +39,22 @@
|
|||
let timeInterval = null
|
||||
let fileproto = null
|
||||
let loading_proto = false
|
||||
let center = { lat: 0, lng: 0 };
|
||||
|
||||
|
||||
onMount(() => {
|
||||
create_map()
|
||||
|
||||
//create_map()
|
||||
obtieneCorrdenadas();
|
||||
return () => {
|
||||
timeInterval && globalThis.clearInterval(timeInterval)
|
||||
}
|
||||
})
|
||||
|
||||
// Reacciona a los cambios en center
|
||||
$: if(center && globalThis.L) {
|
||||
create_map();
|
||||
};
|
||||
|
||||
function create_map() {
|
||||
if (!elMap || !globalThis.L) return;
|
||||
if (!L) L = globalThis.L;
|
||||
|
|
@ -85,22 +92,38 @@
|
|||
}
|
||||
if (!myMap) {
|
||||
myMap = L.map(elMap)
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
{ attribution: '© <a target="_blank" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }
|
||||
L.tileLayer(
|
||||
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
{
|
||||
maxZoom: 19,
|
||||
attribution: '© <a target="_blank" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}
|
||||
).addTo(myMap);
|
||||
}
|
||||
|
||||
// obtener coordenadas actuales
|
||||
// centrar mapa en coordenadas del navegador
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
/*navigator.geolocation.getCurrentPosition(
|
||||
({ coords }) => {
|
||||
const { latitude, longitude } = coords;
|
||||
myMap.setView([latitude, longitude], 16);
|
||||
},
|
||||
(error) => console.log({ error })
|
||||
)
|
||||
}
|
||||
)*/
|
||||
myMap.setView(center, 14);
|
||||
|
||||
}
|
||||
async function obtieneCorrdenadas () {
|
||||
try {
|
||||
const data = await getCoordenadasIniciales('?');
|
||||
center.lat = parseFloat(data.initialLat);
|
||||
center.lng = parseFloat(data.initialLng);
|
||||
console.log("Coordenadas de inicio:", data);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error al obtener las coordenadas iniciales :', error);
|
||||
}
|
||||
}
|
||||
async function onMostrarRuta(id_operador, id_linea) {
|
||||
try {
|
||||
loading = true
|
||||
|
|
@ -114,7 +137,11 @@
|
|||
|
||||
const coordenadas = data.positions.map(({ shape_pt_lat, shape_pt_lon}) => [shape_pt_lat, shape_pt_lon])
|
||||
polyline = L.polyline(coordenadas, { color: 'red' }).addTo(myMap)
|
||||
myMap.fitBounds(polyline.getBounds());
|
||||
|
||||
const bound = polyline.getBounds()
|
||||
if (bound._northEast && bound._southWset) {
|
||||
myMap.fitBounds(polyline.getBounds());
|
||||
}
|
||||
|
||||
if (coordenadas) {
|
||||
marker1 = L.marker(coordenadas[0], { icon: iconPartida }).addTo(myMap)
|
||||
|
|
@ -124,6 +151,7 @@
|
|||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log({ error })
|
||||
globalThis.toast.error(error)
|
||||
} finally {
|
||||
loading = false;
|
||||
|
|
|
|||
|
|
@ -71,19 +71,20 @@
|
|||
form = await updateOperador(form);
|
||||
} else {
|
||||
|
||||
formRol.nombre_rol=form.nombre_operador;
|
||||
formRol = await createRol(formRol);
|
||||
form.id_rol = formRol.id_rol
|
||||
/*formRol.nombre_rol=form.nombre_operador;
|
||||
formRol = await createRol(formRol);
|
||||
form.id_rol = formRol.id_rol */
|
||||
|
||||
form = await createOperador(form);
|
||||
formRol= await createRolOperador(form);
|
||||
/*formRol= await createRolOperador(form);*/
|
||||
|
||||
formRol={};
|
||||
/*formRol={};
|
||||
formRol.id_rol = form.id_rol;
|
||||
formRol.id_aplicacion=1;
|
||||
formRol = await createRolyaplicacion(formRol);
|
||||
|
||||
formRol.id_aplicacion=2;
|
||||
formRol = await createRolyaplicacion(formRol);
|
||||
formRol = await createRolyaplicacion(formRol);*/
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
<script>
|
||||
import { getOperadores } from "$/services/operadores";
|
||||
|
||||
export let id_operador;
|
||||
export let loading = false;
|
||||
|
||||
let operadores = [];
|
||||
let baseUrlInforme = window.location.origin;
|
||||
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 = new URL(`reporte/itinerarios/${id_operador}.pdf`, baseUrlInforme);
|
||||
window.open(url.href, '_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`;
|
||||
const url = new URL(`reporte/expediciones/${id_operador}_${fecha}.pdf`, baseUrlInforme);
|
||||
window.open(url.href, '_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>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { getCountByComuna } from '$/services/paraderos';
|
||||
import Chart from 'chart.js/auto';
|
||||
import ChartDataLabels from 'chartjs-plugin-datalabels';
|
||||
|
||||
let comunasCounts = [];
|
||||
let chart = null; // Referencia al gráfico
|
||||
|
||||
onMount(() => {
|
||||
getCountByComuna({ vigente: 1 })
|
||||
.then(data => {
|
||||
comunasCounts = data.count_by_comuna.sort((a, b) => a.id_comuna__nombre_comuna.localeCompare(b.id_comuna__nombre_comuna));
|
||||
updateChart(); // Actualizar el gráfico cuando los datos estén listos
|
||||
})
|
||||
.catch(error => console.error("Failed to load comunas counts:", error));
|
||||
});
|
||||
|
||||
function updateChart() {
|
||||
const ctx = document.getElementById('comunasChart').getContext('2d');
|
||||
if (chart) {
|
||||
chart.destroy(); // Destruir el gráfico existente antes de crear uno nuevo
|
||||
}
|
||||
chart = new Chart(ctx, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: comunasCounts.map(c => c.id_comuna__nombre_comuna),
|
||||
datasets: [{
|
||||
label: 'Total Paraderos',
|
||||
data: comunasCounts.map(c => c.total),
|
||||
backgroundColor: comunasCounts.map(() => `hsla(${Math.random() * 360}, 100%, 75%, 0.8)`), // Colores aleatorios con opacidad
|
||||
hoverOffset: 4
|
||||
}]
|
||||
},
|
||||
plugins: [ChartDataLabels],
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
datalabels: {
|
||||
color: '#ffffff',
|
||||
anchor: 'end',
|
||||
align: 'start',
|
||||
font: {
|
||||
size: 14,
|
||||
weight: 'bold',
|
||||
family: 'Arial'
|
||||
},
|
||||
formatter: (value, ctx) => {
|
||||
let sum = ctx.dataset.data.reduce((acc, curr) => acc + curr, 0);
|
||||
let percentage = (value * 100 / sum).toFixed(2) + "%";
|
||||
return ` (${percentage})`;
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(tooltipItem) {
|
||||
let label = tooltipItem.label || '';
|
||||
let value = tooltipItem.raw;
|
||||
let total = tooltipItem.dataset.data.reduce((acc, curr) => acc + curr, 0);
|
||||
let percentage = ((value / total) * 100).toFixed(2) + '%';
|
||||
return `${label}: (${percentage})`;
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<table class="table table-striped table-hover table-responsive">
|
||||
<tr>
|
||||
<th>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<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>
|
||||
<table class="table table-striped table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Comuna</th>
|
||||
<th>Total Paraderos</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each comunasCounts as { id_comuna__nombre_comuna, total }}
|
||||
<tr>
|
||||
<td>{id_comuna__nombre_comuna}</td>
|
||||
<td>{total}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<canvas id="comunasChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<script>
|
||||
import { onMount, createEventDispatcher } from "svelte";
|
||||
import Chart from 'chart.js/auto';
|
||||
import ChartDataLabels from 'chartjs-plugin-datalabels';
|
||||
import { getCountRutasBuses } from "$/services/lineas";
|
||||
import { getCount } from "$/services/lineas";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
let rutasConGPS = [];
|
||||
let rutasSinGPS = 0;
|
||||
let chart = null;
|
||||
|
||||
onMount(() => {
|
||||
dispatch('loading', true);
|
||||
|
||||
Promise.all([
|
||||
getCountRutasBuses(),
|
||||
getCount({ vigente: 1 })
|
||||
]).then(([dataRutasConGPS, dataTotalRutas]) => {
|
||||
rutasConGPS = dataRutasConGPS;
|
||||
rutasSinGPS = parseInt (dataTotalRutas.count) - parseInt (dataRutasConGPS.count);
|
||||
updateChart();
|
||||
}).catch(error => {
|
||||
console.error('Error fetching counts:', error);
|
||||
}).finally(() => {
|
||||
dispatch('loading', false);
|
||||
});
|
||||
});
|
||||
|
||||
function updateChart() {
|
||||
const ctx = document.getElementById('barChartR').getContext('2d');
|
||||
if (chart) {
|
||||
chart.destroy();
|
||||
}
|
||||
chart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['Rutas con información', 'Rutas sin información'],
|
||||
datasets: [{
|
||||
label: 'Posicionamiento',
|
||||
data: [rutasConGPS.count, rutasSinGPS],
|
||||
backgroundColor: ['rgba(54, 162, 235, 0.8)', 'rgba(255, 99, 132, 0.8)']
|
||||
}]
|
||||
},
|
||||
plugins: [ChartDataLabels],
|
||||
options: {
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
responsive: true,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Rutas con Informacion de Posicionamiento</h5>
|
||||
<canvas id="barChartR"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -3,42 +3,29 @@
|
|||
import CantidadParaderos from "./CantidadParaderos.svelte";
|
||||
import CantidadLineas from "./CantidadLineas.svelte";
|
||||
import CantidadParaderosComuna from "./CantidadParaderosComuna.svelte";
|
||||
import CantidadParaderosComunaGrafico from "./CantidadParaderosComunaGrafico.svelte";
|
||||
import CantidadBusesRecorrido from "./CantidadBusesRecorrido.svelte";
|
||||
import CantidadBusesLinea from "./CantidadBusesLinea.svelte";
|
||||
import RutasCorrectas from "./rutasCorrectas.svelte";
|
||||
import CantidadRutasBuses from "./CantidadRutaBuses.svelte";
|
||||
|
||||
let loading1 = false
|
||||
let loading2 = false
|
||||
let loading3 = false
|
||||
let loading4 = false
|
||||
let loading5 = false
|
||||
let loading6 = false
|
||||
</script>
|
||||
|
||||
<PageTitle
|
||||
loading={loading1 || loading2 || loading3 || loading4 || loading5 }
|
||||
><strong>Análisis</strong> de datos</PageTitle>
|
||||
<PageTitle loading={loading1 || loading2 || loading3 || loading4 || loading5 }><strong>Resumen</strong> de datos</PageTitle>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-7">
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<CantidadLineas on:loading={ev => loading1 = ev.detail} />
|
||||
</div>
|
||||
<div class="col">
|
||||
<CantidadParaderos on:loading={ev => loading2 = ev.detail} />
|
||||
</div>
|
||||
<div class="col"><CantidadParaderos on:loading={ev => loading2 = ev.detail} /></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<CantidadParaderosComuna on:loading={ev => loading3 = ev.detail} />
|
||||
</div>
|
||||
<div class="col">
|
||||
<CantidadBusesRecorrido on:loading={ev => loading4 = ev.detail} />
|
||||
</div>
|
||||
<div class="col"><CantidadParaderosComunaGrafico on:loading={ev => loading3 = ev.detail} /> </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-md-5">
|
||||
<CantidadBusesLinea on:loading={ev => loading5 = ev.detail} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import Chart from 'chart.js/auto';
|
||||
import ChartDataLabels from 'chartjs-plugin-datalabels';
|
||||
import { getCountLineasCorrectas } from "$/services/lineas";
|
||||
|
||||
let rutas = [];
|
||||
let chart = null;
|
||||
|
||||
onMount(() => {
|
||||
getCountLineasCorrectas().then(data => {
|
||||
rutas = data;
|
||||
updateChart();
|
||||
}).catch(error => console.error('Error fetching line counts:', error));
|
||||
});
|
||||
|
||||
function updateChart() {
|
||||
const ctx = document.getElementById('barChart').getContext('2d');
|
||||
if (chart) {
|
||||
chart.destroy();
|
||||
}
|
||||
|
||||
// Generando colores aleatorios para cada barra
|
||||
const backgroundColors = rutas.map(() => `hsla(${Math.random() * 360}, 70%, 70%, 0.8)`);
|
||||
const borderColors = rutas.map(() => `hsla(${Math.random() * 360}, 70%, 50%, 1)`);
|
||||
|
||||
chart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: rutas.map(ruta => ruta.titulo),
|
||||
datasets: [{
|
||||
label: 'Rutas',
|
||||
data: rutas.map(ruta => ruta.cantidad),
|
||||
backgroundColor: backgroundColors,
|
||||
borderColor: borderColors,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
plugins: [ChartDataLabels],
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
datalabels: {
|
||||
color: '#444',
|
||||
font: {
|
||||
weight: 'bold'
|
||||
},
|
||||
formatter: (value, ctx) => {
|
||||
let sum = ctx.dataset.data.reduce((a, b) => a + b, 0);
|
||||
let percentage = (value * 100 / sum).toFixed(2) + "%";
|
||||
return value + " (" + percentage + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Rutas Definidas</h5>
|
||||
<canvas id="barChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -16,8 +16,8 @@
|
|||
<img class="img-fluid" width="100%" alt="" src={avatar} />
|
||||
<section class="section">
|
||||
<div class="section-body">
|
||||
<p>Twitter del perfil: <br />
|
||||
@twitter
|
||||
<p> <br />
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -34,13 +34,10 @@
|
|||
</div>
|
||||
<div class="section-body">
|
||||
<p>
|
||||
Vestibulum
|
||||
volutpat lacus ac magna ullamcorper, id semper sem aliquam. Donec
|
||||
vestibulum turpis mi, sed ullamcorper lorem feugiat sed. Praesent
|
||||
ut fringilla dolor. Sed viverra posuere felis eu ullamcorper.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -53,12 +50,13 @@
|
|||
<div class="section-header">
|
||||
<h6 class="pb-2 border-bottom border-accent">Información de contacto:</h6>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<!-- <div class="section-body">
|
||||
<p class="mb-1">Sitio Web: <a href="http://wwww.example.com">http://wwww.example.com</a></p>
|
||||
<p class="mb-1">Twitter: @twitter</p>
|
||||
<p class="mb-2">Facebook: facebook/ejemplo</p><a class="btn btn-primary btn-block" href={"#"}>Ir al
|
||||
sitio</a>
|
||||
</div>
|
||||
-->
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getUsuarios(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/usuarios/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getUsuario(id) {
|
||||
const res = await fetch(`${base}/usuarios/${id}/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getPermisosPath(path) {
|
||||
const res = await fetch(`${base}/usuarios/permisos/`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ path }),
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getPermisosApp() {
|
||||
const res = await fetch(`${base}/usuarios/permisos/`, {
|
||||
method: 'POST',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function createUsuario(data) {
|
||||
const res = await fetch(`${base}/usuarios/`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function updateUsuario({ login: id = null, ...data }) {
|
||||
const res = await fetch(`${base}/usuarios/${id}/`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(data),
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
|
||||
export async function deleteUsuario(id) {
|
||||
const res = await fetch(`${base}/usuarios/${id}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.text()
|
||||
}
|
||||
|
|
@ -86,6 +86,16 @@ export async function getCountBuses(params) {
|
|||
}
|
||||
|
||||
|
||||
export async function getCountLineasCorrectas(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/lineas/count_lineas_correctas/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
|
||||
export async function getCountBusesRecorridos(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/lineas/count_buses_recorridos/${query}`, {
|
||||
|
|
@ -93,4 +103,14 @@ export async function getCountBusesRecorridos(params) {
|
|||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function getCountRutasBuses(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/lineas/count_rutas_buses/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,4 +17,13 @@ export async function getRutas(params) {
|
|||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getCoordenadasIniciales(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/mapas/coordenadas/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text( )
|
||||
return res.json()
|
||||
}
|
||||
|
|
@ -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