Merge branch 'develop/frontend/open-street-map' into 'master/frontend'
se cambia google map por open street map See merge request m3f_usm/T_digital_Transporte_USM!1master/frontend
commit
4bb0b87d2c
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
|
@ -58,6 +58,11 @@
|
||||||
<span class="align-middle">Personas</span>
|
<span class="align-middle">Personas</span>
|
||||||
</SideLink>
|
</SideLink>
|
||||||
|
|
||||||
|
<SideLink to="/roles">
|
||||||
|
<i class="align-middle bi bi-people fs-4" />
|
||||||
|
<span class="align-middle">Roles</span>
|
||||||
|
</SideLink>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
<script>
|
||||||
|
// imagenes
|
||||||
|
import IconParada from '$/assets/parada-de-autobus.png'
|
||||||
|
// servicios
|
||||||
|
import { getParadero, updateParadero, getParaderoImagenes, createParaderoImagen, deleteParaderoImagen } from '$/services/paraderos'
|
||||||
|
// libs
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
export let parada = null
|
||||||
|
let canvas = null
|
||||||
|
let form = {}
|
||||||
|
let imagenes = []
|
||||||
|
|
||||||
|
$: init(!!parada)
|
||||||
|
|
||||||
|
async function init(show) {
|
||||||
|
if (!canvas) return;
|
||||||
|
!show && canvas.classList.remove('show')
|
||||||
|
show && canvas.classList.add('show')
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (parada) {
|
||||||
|
form = await getParadero(parada.id_paradero)
|
||||||
|
imagenes = await getParaderoImagenes(parada.id_paradero)
|
||||||
|
}
|
||||||
|
} catch(error) {
|
||||||
|
alert(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSave() {
|
||||||
|
try {
|
||||||
|
await updateParadero(form)
|
||||||
|
alert('Información guardada')
|
||||||
|
} catch(error) {
|
||||||
|
alert(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSaveImagen({ target: form }) {
|
||||||
|
try {
|
||||||
|
const [ file = null ] = form.file1.files;
|
||||||
|
await createParaderoImagen(parada.id_paradero, file)
|
||||||
|
imagenes = await getParaderoImagenes(parada.id_paradero)
|
||||||
|
form.file1.value = ''
|
||||||
|
} catch (error) {
|
||||||
|
alert(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onDeleteImagen({ id_paradero_imagen }) {
|
||||||
|
try {
|
||||||
|
if (!confirm('Estás seguro de eliminar la imagen?')) return;
|
||||||
|
await deleteParaderoImagen(id_paradero_imagen)
|
||||||
|
imagenes = imagenes.filter(imagen => imagen.id_paradero_imagen !== id_paradero_imagen)
|
||||||
|
} catch (error) {
|
||||||
|
alert(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="offcanvas offcanvas-end" tabindex="-1" bind:this={canvas} aria-labelledby="offcanvasParaderoLabel" style="visibility: inherit;">
|
||||||
|
<div class="offcanvas-header">
|
||||||
|
<h5 class="offcanvas-title text-info" id="offcanvasParaderoLabel">
|
||||||
|
<img src={IconParada} alt="Icono de Paradero" width="64">
|
||||||
|
Paradero
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn-close" aria-label="Close" on:click|preventDefault={() => dispatch('close')}></button>
|
||||||
|
</div>
|
||||||
|
<div class="offcanvas-body">
|
||||||
|
Coordenadas:
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-text">Latitud</div>
|
||||||
|
<div class="form-control">{form.stop_lat}</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-text">Longitud</div>
|
||||||
|
<div class="form-control">{form.stop_lon}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Propiedades:
|
||||||
|
<form on:submit|preventDefault={onSave}>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-text">Nombre</div>
|
||||||
|
<input type="text" bind:value={form.stop_name} class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-text">Descripción</div>
|
||||||
|
<input type="text" bind:value={form.stop_desc} class="form-control">
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="text-center">
|
||||||
|
<button type="submit" class="btn btn-primary"><i class="bi bi-save"></i> Guardar</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="my-3"></div>
|
||||||
|
|
||||||
|
<!-- imagenes -->
|
||||||
|
{#each imagenes as imagen}
|
||||||
|
<div class="seccion-imagen mb-3">
|
||||||
|
<img src={imagen.url} alt="imagen paradero" class="img-fluid">
|
||||||
|
<a href={"#"} class="btn btn-danger" on:click|preventDefault={() => onDeleteImagen(imagen)}>
|
||||||
|
<i class="bi bi-trash"></i> Eliminar
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<!-- agregar imagen -->
|
||||||
|
<form action="" on:submit|preventDefault={onSaveImagen}>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="form-control" style="overflow: hidden">
|
||||||
|
<input type="file" name="file1" accept="*.png,*.jpg,*.jpeg">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-center">
|
||||||
|
<button class="btn btn-primary" type="submit">
|
||||||
|
<i class="bi bi-plus-lg"></i> Agregar imagen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if parada}
|
||||||
|
<div class="offcanvas-backdrop fade show" on:click|preventDefault={() => dispatch('close')}></div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
{#if parada}
|
||||||
|
<style>
|
||||||
|
html { overflow-y: hidden; }
|
||||||
|
.seccion-imagen { position: relative; }
|
||||||
|
.seccion-imagen > a { position: absolute; bottom: .5rem; right: .5rem; }
|
||||||
|
</style>
|
||||||
|
{/if}
|
|
@ -1,38 +1,70 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
// components
|
||||||
import PageTitle from "$/components/PageTitle.svelte";
|
import PageTitle from "$/components/PageTitle.svelte"
|
||||||
import { getMarcasParaderos } from "$/services/mapas";
|
import IconLoading from "$/components/IconLoading.svelte"
|
||||||
import { storeParaderos } from "$/stores/global";
|
import FormParadero from "./FormParadero.svelte"
|
||||||
import GoogleMap from '$/components/MyMap.svelte'
|
|
||||||
import IconLoading from "$/components/IconLoading.svelte";
|
|
||||||
import ModalParadero from "./ModalParadero.svelte";
|
|
||||||
import { getRegiones } from "$/services/regiones";
|
|
||||||
import { getComunas } from "$/services/comunas";
|
|
||||||
|
|
||||||
let google_api_key = null
|
// services
|
||||||
let google_marks = []
|
import { getRegiones } from "$/services/regiones"
|
||||||
let google_window = null
|
import { getComunas } from "$/services/comunas"
|
||||||
let google_map = null
|
import { getMarcasParaderos } from "$/services/mapas"
|
||||||
let data_map = null
|
|
||||||
let paradero = null
|
// libs
|
||||||
let id_comuna = ''
|
import { onMount } from 'svelte'
|
||||||
let id_region = ''
|
import { storeParaderos } from "$/stores/global"
|
||||||
|
import imagenParada from '$/assets/parada-de-autobus.png'
|
||||||
|
|
||||||
|
let myMap = null
|
||||||
|
let elMap = null
|
||||||
|
let loading = false
|
||||||
let regiones = []
|
let regiones = []
|
||||||
let comunas = []
|
let comunas = []
|
||||||
let comunas_region = []
|
let comunas_x_region = []
|
||||||
let search = ''
|
let L = null
|
||||||
let search_time = null;
|
let iconParada = null
|
||||||
let search_paraderos = []
|
let markers = []
|
||||||
let loading = false
|
let form = {}
|
||||||
|
let parada = null
|
||||||
$: onSearch(search)
|
|
||||||
|
|
||||||
cargar_regiones_comunas()
|
cargar_regiones_comunas()
|
||||||
|
|
||||||
|
$: myMap && crear_marcadores_por_criterio()
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
if(globalThis.L) create_map();
|
||||||
cargar_paraderos_todos($storeParaderos)
|
cargar_paraderos_todos($storeParaderos)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function create_map() {
|
||||||
|
if (!elMap) return;
|
||||||
|
if (!L) L = globalThis.L;
|
||||||
|
if (!iconParada) {
|
||||||
|
iconParada = L.icon({
|
||||||
|
iconUrl: imagenParada,
|
||||||
|
iconSize: [64, 64],
|
||||||
|
iconAnchor: [32, 64],
|
||||||
|
popupAnchor: [0, -32]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!myMap) {
|
||||||
|
myMap = L.map(elMap)
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||||
|
{ attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }
|
||||||
|
).addTo(myMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// obtener coordenadas actuales
|
||||||
|
// centrar mapa en coordenadas del navegador
|
||||||
|
navigator.geolocation.getCurrentPosition(
|
||||||
|
({ coords }) => {
|
||||||
|
const { latitude, longitude } = coords;
|
||||||
|
myMap.setView([latitude, longitude], 16)
|
||||||
|
},
|
||||||
|
(error) => console.log({ error })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function cargar_regiones_comunas() {
|
async function cargar_regiones_comunas() {
|
||||||
try {
|
try {
|
||||||
regiones = await getRegiones()
|
regiones = await getRegiones()
|
||||||
|
@ -46,10 +78,7 @@
|
||||||
try {
|
try {
|
||||||
loading = true
|
loading = true
|
||||||
const paraderos = data_default || await getMarcasParaderos()
|
const paraderos = data_default || await getMarcasParaderos()
|
||||||
storeParaderos.set(paraderos);
|
storeParaderos.set(paraderos)
|
||||||
data_map = paraderos;
|
|
||||||
google_api_key = paraderos.google_api_key;
|
|
||||||
runSearch()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert(error)
|
alert(error)
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -57,134 +86,113 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChangeRegion() {
|
function crear_marcadores_por_criterio() {
|
||||||
comunas_region = comunas.filter(el => el.id_region === Number(id_region))
|
if (form.id_region) {
|
||||||
runSearch()
|
comunas_x_region = comunas.filter(comuna => comuna.id_region === form.id_region)
|
||||||
}
|
}
|
||||||
|
if (!$storeParaderos?.marks) {
|
||||||
|
setTimeout(crear_marcadores_por_criterio, 1000)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 1. eliminar los marcadores anteriores
|
||||||
|
markers.forEach(marker => marker.remove())
|
||||||
|
|
||||||
function onSearch(search) {
|
// 2. filtrar resultado de marcadores
|
||||||
if (!data_map) return;
|
const paraderos = $storeParaderos.marks.filter(m => {
|
||||||
search_time && clearTimeout(search_time)
|
// filtrar por comunas de la region
|
||||||
search_time = setTimeout(runSearch, 1000)
|
if (form.id_region && !form.id_comuna) {
|
||||||
}
|
const existe = comunas_x_region.findIndex(com => com.id_comuna === m.id_comuna) !== -1
|
||||||
|
if (!existe) return false
|
||||||
function runSearch() {
|
|
||||||
console.log('runSearch()')
|
|
||||||
search_paraderos = data_map.marks.filter(el => {
|
|
||||||
if (id_region && !id_comuna) {
|
|
||||||
const existe = comunas_region.findIndex(com => com.id_comuna === el.id_comuna) !== -1;
|
|
||||||
if (!existe) return false;
|
|
||||||
}
|
}
|
||||||
if (id_comuna && el.id_comuna !== Number(id_comuna)) return false;
|
|
||||||
return !search || el.location.toUpperCase().indexOf(search.toUpperCase()) !== -1;
|
// filtrar por comuna seleccionada
|
||||||
|
if (form.id_comuna && m.id_comuna !== form.id_comuna) return false
|
||||||
|
|
||||||
|
// filtrar por texto de busqueda
|
||||||
|
if (form.search && m.location.toUpperCase().indexOf(form.search.toUpperCase()) === -1) return false
|
||||||
|
|
||||||
|
// filtro coincide a criterio
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
onMostrarParaderos(google_map)
|
// 3. crear marcadores
|
||||||
}
|
for (let mark of paraderos) {
|
||||||
|
const { lat, lng } = mark.position
|
||||||
|
const marker = L.marker([lat, lng], { icon: iconParada }).addTo(myMap)
|
||||||
|
const { title, location } = mark;
|
||||||
|
const html = `${title}<br>${location}`
|
||||||
|
|
||||||
|
marker.bindTooltip(html)
|
||||||
|
|
||||||
function onMostrarParaderos(map) {
|
marker.on('click', function() {
|
||||||
const google = globalThis.google;
|
parada = { ...mark }
|
||||||
if (!google) return;
|
})
|
||||||
|
markers.push(marker)
|
||||||
console.log('onMostrarParaderos', map)
|
|
||||||
if (!google_map) google_map = map;
|
|
||||||
|
|
||||||
// elimina las marcas anteriores en el mapa
|
|
||||||
google_marks.forEach(mark => mark.setMap(null))
|
|
||||||
|
|
||||||
if (!google_window) google_window = new google.maps.InfoWindow();
|
|
||||||
|
|
||||||
google_marks = [];
|
|
||||||
for (const nuevaMarca of search_paraderos) {
|
|
||||||
const { position, title, location, url_image, id_paradero } = nuevaMarca;
|
|
||||||
const marker = new google.maps.Marker({ position, map: map, title });
|
|
||||||
|
|
||||||
marker.addListener("click", () => {
|
|
||||||
google_window.close();
|
|
||||||
const imagen = url_image ? `<img alt="Imagen" src="${url_image}" width="200">` : ''
|
|
||||||
const html = `
|
|
||||||
<h1>${title}</h1>
|
|
||||||
<p>${location}</p>
|
|
||||||
<button class="btn btn-primary" onclick="onEditaParada(${id_paradero})">Editar</button>
|
|
||||||
<hr>
|
|
||||||
${imagen}
|
|
||||||
`
|
|
||||||
google_window.setContent(html);
|
|
||||||
google_window.open(marker.getMap(), marker);
|
|
||||||
});
|
|
||||||
|
|
||||||
google_marks.push(marker)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
map && map.setCenter(search_paraderos[0].position)
|
// 4. centrar mapa
|
||||||
|
if (myMap && paraderos.length) {
|
||||||
// aplicar zoom en base a las coordenadas
|
const { lat, lng } = paraderos[0].position
|
||||||
const bounds = new google.maps.LatLngBounds();
|
myMap.setView([ lat, lng ],16)
|
||||||
search_paraderos.forEach(el => bounds.extend(el.position))
|
const bounds = myMap.getBounds()
|
||||||
google_map && google_map.fitBounds(bounds);
|
paraderos.forEach(el => bounds.extend(el.position))
|
||||||
|
myMap.fitBounds(bounds)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
globalThis.onEditaParada = function(id_paradero) {
|
|
||||||
paradero = { id_paradero }
|
function run_search_text() {
|
||||||
|
form.time_search && clearTimeout(form.time_search)
|
||||||
|
form.time_search = setTimeout(() => crear_marcadores_por_criterio(), 1000)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<PageTitle>Paraderos</PageTitle>
|
<svelte:head>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" on:load={create_map}></script>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<div class="card">
|
<div class="row">
|
||||||
<div class="card-header">
|
<div class="col-md">
|
||||||
<div class="row">
|
<PageTitle>
|
||||||
<div class="col-md-auto">
|
{#if loading}<IconLoading />{/if}
|
||||||
<button class="btn btn-outline-secondary" on:click|preventDefault={() => cargar_paraderos_todos(null)}><i class="bi bi-arrow-repeat"></i> Refrescar</button>
|
Paraderos
|
||||||
</div>
|
</PageTitle>
|
||||||
<div class="col-md-auto">
|
</div>
|
||||||
<div class="input-group">
|
<div class="col-md-auto">
|
||||||
<div class="input-group-text">Región</div>
|
<div class="input-group mb-sm-3">
|
||||||
<select bind:value={id_region} class="form-select" on:change={onChangeRegion}>
|
<div class="input-group-text">Región</div>
|
||||||
<option value=""></option>
|
<select bind:value={form.id_region} class="form-select" on:change={crear_marcadores_por_criterio}>
|
||||||
{#each regiones as region}
|
<option value=""></option>
|
||||||
<option value={region.id_region}>{region.nombre_region}</option>
|
{#each regiones as r}
|
||||||
{/each}
|
<option value={r.id_region}>{r.nombre_region}</option>
|
||||||
</select>
|
{/each}
|
||||||
</div>
|
</select>
|
||||||
</div>
|
|
||||||
<div class="col-md-auto">
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-text">Comuna</div>
|
|
||||||
<select bind:value={id_comuna} class="form-select" on:change={runSearch}>
|
|
||||||
<option value=""></option>
|
|
||||||
{#each comunas_region as comuna}
|
|
||||||
<option value={comuna.id_comuna}>{comuna.nombre_comuna}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-auto">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="search" bind:value={search} placeholder="busqueda..." class="form-control">
|
|
||||||
<div class="input-group-text"><i class="bi bi-search"></i></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md">
|
|
||||||
{#if loading}
|
|
||||||
<IconLoading />
|
|
||||||
<span>Cargando ubicaciones...</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="col-md-auto">
|
||||||
{#if google_api_key}
|
<div class="input-group mb-sm-3">
|
||||||
<GoogleMap
|
<div class="input-group-text">Comuna</div>
|
||||||
{google_api_key}
|
<select bind:value={form.id_comuna} class="form-select" on:change={crear_marcadores_por_criterio}>
|
||||||
on:start={ev => onMostrarParaderos(ev.detail)}
|
<option value=""></option>
|
||||||
/>
|
{#each comunas_x_region as c}
|
||||||
{/if}
|
<option value={c.id_comuna}>{c.nombre_comuna}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-auto">
|
||||||
|
<div class="input-group mb-sm-3">
|
||||||
|
<input type="search" bind:value={form.search} class="form-control" on:input={run_search_text}>
|
||||||
|
<div class="input-group-text"><i class="bi bi-search"></i></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div bind:this={elMap} style="height: 100vh;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if paradero}
|
<FormParadero {parada} on:close={() => parada = null} />
|
||||||
<ModalParadero {paradero} on:close={() => paradero = null} />
|
|
||||||
{/if}
|
|
|
@ -1,142 +1,150 @@
|
||||||
<script>
|
<script>
|
||||||
|
// componentes
|
||||||
import PageTitle from "$/components/PageTitle.svelte";
|
import PageTitle from "$/components/PageTitle.svelte";
|
||||||
import GoogleMap from '$/components/MyMap.svelte'
|
|
||||||
import IconLoading from "$/components/IconLoading.svelte";
|
import IconLoading from "$/components/IconLoading.svelte";
|
||||||
|
import imagenPartida from '$/assets/partida.png'
|
||||||
|
import imagenTermino from '$/assets/termino.png'
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
// servicios
|
||||||
import { getOperadores } from "$/services/operadores";
|
import { getOperadores } from "$/services/operadores";
|
||||||
import { getLineas } from "$/services/lineas";
|
import { getLineas } from "$/services/lineas";
|
||||||
import { getRutas } from "$/services/mapas";
|
import { getRutas } from "$/services/mapas";
|
||||||
|
|
||||||
|
let myMap = null
|
||||||
|
let elMap = null
|
||||||
let operadores = []
|
let operadores = []
|
||||||
let lineas = []
|
let lineas = []
|
||||||
let positions = []
|
let id_operador = ''
|
||||||
let id_operador = '';
|
let id_linea = ''
|
||||||
let id_linea = '';
|
let loading = false
|
||||||
let loading = false;
|
let polyline = null
|
||||||
let google_api_key = null;
|
let iconPartida = null
|
||||||
let google_map = null;
|
let iconTermino = null
|
||||||
let polyline = null;
|
let L = null
|
||||||
|
let marker1 = null
|
||||||
|
let marker2 = null
|
||||||
|
|
||||||
|
getOperadores()
|
||||||
|
.then(data => data.sort((a,b) => a.nombre_operador < b.nombre_operador? -1 : 1))
|
||||||
|
.then(data => operadores = data)
|
||||||
|
.catch(error => alert(error))
|
||||||
|
getLineas()
|
||||||
|
.then(data => data.sort((a,b) => a.nombre_linea < b.nombre_linea? -1 : 1))
|
||||||
|
.then(data => lineas = data)
|
||||||
|
.catch(error => alert(error))
|
||||||
|
|
||||||
$: cargar_coordenadas(id_operador, id_linea)
|
$: cargar_coordenadas(id_operador, id_linea)
|
||||||
|
|
||||||
Promise.all([ getOperadores(), getLineas() ])
|
onMount(() => {
|
||||||
.then(res => {
|
create_map()
|
||||||
operadores = res[0]
|
})
|
||||||
lineas = res[1]
|
|
||||||
})
|
function create_map() {
|
||||||
.catch(error => alert(error))
|
if (!elMap) return;
|
||||||
|
if (!L) L = globalThis.L;
|
||||||
|
if (!iconPartida) {
|
||||||
|
iconPartida = L.icon({
|
||||||
|
iconUrl: imagenPartida,
|
||||||
|
iconSize: [64, 64],
|
||||||
|
iconAnchor: [32, 64],
|
||||||
|
popupAnchor: [0, -32]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!iconTermino) {
|
||||||
|
iconTermino = L.icon({
|
||||||
|
iconUrl: imagenTermino,
|
||||||
|
iconSize: [64, 64],
|
||||||
|
iconAnchor: [32, 64],
|
||||||
|
popupAnchor: [0, -32]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!myMap) {
|
||||||
|
myMap = L.map(elMap)
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||||
|
{ attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }
|
||||||
|
).addTo(myMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// obtener coordenadas actuales
|
||||||
|
// centrar mapa en coordenadas del navegador
|
||||||
|
navigator.geolocation.getCurrentPosition(
|
||||||
|
({ coords }) => {
|
||||||
|
const { latitude, longitude } = coords;
|
||||||
|
myMap.setView([latitude, longitude], 16);
|
||||||
|
},
|
||||||
|
(error) => console.log({ error })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function cargar_coordenadas(id_operador, id_linea) {
|
async function cargar_coordenadas(id_operador, id_linea) {
|
||||||
try {
|
try {
|
||||||
loading = true
|
loading = true
|
||||||
polyline && polyline.setMap(null);
|
polyline && polyline.remove()
|
||||||
polyline = null;
|
marker1 && marker1.remove()
|
||||||
|
marker2 && marker2.remove()
|
||||||
|
|
||||||
if (!id_operador || !id_linea) return;
|
if (!id_operador || !id_linea) return;
|
||||||
const data = await getRutas({ id_linea})
|
const data = await getRutas({ id_linea })
|
||||||
if (!google_api_key) google_api_key = data.google_api_key;
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
if (coordenadas) {
|
||||||
|
marker1 = L.marker(coordenadas[0], { icon: iconPartida }).addTo(myMap)
|
||||||
|
marker2 = L.marker(coordenadas[coordenadas.length -1], { icon: iconTermino }).addTo(myMap)
|
||||||
|
}
|
||||||
|
|
||||||
positions = data.positions
|
|
||||||
// .sort((a,b) => a.shape_pt_sequence < b.shape_pt_sequence? -1 : 1)
|
|
||||||
.map(el => ({ lat: el.shape_pt_lat, lng: el.shape_pt_lon }))
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert(error)
|
alert(error)
|
||||||
} finally {
|
} finally {
|
||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPolyline(map) {
|
|
||||||
google_map = map;
|
|
||||||
map.setZoom(0)
|
|
||||||
onMostrarCoordenadas(positions)
|
|
||||||
}
|
|
||||||
|
|
||||||
$: onMostrarCoordenadas(positions)
|
|
||||||
|
|
||||||
function onMostrarCoordenadas(positions) {
|
|
||||||
if (!google_map) return;
|
|
||||||
const google = globalThis.google;
|
|
||||||
|
|
||||||
const minmax = positions.reduce((acum, el) => {
|
|
||||||
if (acum.min_lat === 0 || el.lat < acum.min_lat) acum.min_lat = el.lat;
|
|
||||||
if (acum.max_lat === 0 || el.lat > acum.max_lat) acum.max_lat = el.lat;
|
|
||||||
if (acum.min_lng === 0 || el.lng < acum.min_lng) acum.min_lng = el.lng;
|
|
||||||
if (acum.max_lng === 0 || el.lng > acum.max_lng) acum.max_lng = el.lng;
|
|
||||||
return { ...acum }
|
|
||||||
}, { min_lat: 0, max_lat: 0, min_lng: 0, max_lng: 0 })
|
|
||||||
|
|
||||||
// centrar el mapa
|
|
||||||
const pos_center = {
|
|
||||||
lat: minmax.min_lat + (minmax.max_lat - minmax.min_lat) / 2,
|
|
||||||
lng: minmax.min_lng + (minmax.max_lng - minmax.min_lng) / 2,
|
|
||||||
}
|
|
||||||
google_map.setCenter(pos_center)
|
|
||||||
|
|
||||||
// aplicar zoom en base a las coordenadas
|
|
||||||
const bounds = new google.maps.LatLngBounds();
|
|
||||||
positions.forEach(el => bounds.extend(el))
|
|
||||||
google_map.fitBounds(bounds);
|
|
||||||
|
|
||||||
polyline && polyline.setMap(null);
|
|
||||||
polyline = null;
|
|
||||||
|
|
||||||
polyline = new google.maps.Polyline({
|
|
||||||
path: positions,
|
|
||||||
geodesic: true,
|
|
||||||
strokeColor: "#FF0000",
|
|
||||||
strokeOpacity: 1.0,
|
|
||||||
strokeWeight: 2,
|
|
||||||
// editable: true,
|
|
||||||
// draggable: true
|
|
||||||
});
|
|
||||||
polyline.setMap(google_map);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<PageTitle>Paraderos</PageTitle>
|
<svelte:head>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" on:load={create_map}></script>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
<div class="row">
|
||||||
<div class="row">
|
<div class="col-md">
|
||||||
<div class="col-md-auto">
|
<PageTitle>
|
||||||
<div class="input-group">
|
{#if loading}<IconLoading />{/if}
|
||||||
<div class="input-group-text">Operador</div>
|
Rutas
|
||||||
<select bind:value={id_operador} class="form-select" on:change={() => id_linea = ''}>
|
</PageTitle>
|
||||||
<option value=""></option>
|
</div>
|
||||||
{#each operadores as operador}
|
<div class="col-md-auto">
|
||||||
<option value={operador.id_operador}>{operador.nombre_operador}</option>
|
<div class="input-group">
|
||||||
{/each}
|
<div class="input-group-text">Operador</div>
|
||||||
</select>
|
<select bind:value={id_operador} class="form-select" on:change={() => id_linea = ''}>
|
||||||
</div>
|
<option value=""></option>
|
||||||
</div>
|
{#each operadores as operador}
|
||||||
<div class="col-md-auto">
|
<option value={operador.id_operador}>{operador.nombre_operador}</option>
|
||||||
<div class="input-group">
|
{/each}
|
||||||
<div class="input-group-text">Linea</div>
|
</select>
|
||||||
<select bind:value={id_linea} class="form-select">
|
|
||||||
<option value=""></option>
|
|
||||||
{#each lineas.filter(el => el.id_operador === id_operador) as linea}
|
|
||||||
<option value={linea.id_linea}>{linea.route_short_name}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md">
|
|
||||||
{#if loading}
|
|
||||||
<IconLoading />
|
|
||||||
<span>Cargando rutas...</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="col-md-auto">
|
||||||
{#if google_api_key}
|
<div class="input-group">
|
||||||
<div class={id_linea ? "d-block" : "d-none"}>
|
<div class="input-group-text">Linea</div>
|
||||||
<GoogleMap
|
<select bind:value={id_linea} class="form-select">
|
||||||
{google_api_key}
|
<option value=""></option>
|
||||||
on:start={ev => onPolyline(ev.detail)}
|
{#each lineas.filter(el => el.id_operador === id_operador) as linea}
|
||||||
/>
|
<option value={linea.id_linea}>{linea.route_short_name}</option>
|
||||||
</div>
|
{/each}
|
||||||
{/if}
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div bind:this={elMap} style="height: 100vh;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
<script>
|
||||||
|
import Paginate from "$/components/Paginate.svelte";
|
||||||
|
import { getRoles } from "$/services/roles";
|
||||||
|
import PageTitle from "$/components/PageTitle.svelte";
|
||||||
|
import ModalRol from "./ModalRol.svelte";
|
||||||
|
|
||||||
|
const limit = 15;
|
||||||
|
let page = 1;
|
||||||
|
let offset = 0;
|
||||||
|
let count = 0;
|
||||||
|
let ordering = 'id_rol'
|
||||||
|
let roles = []
|
||||||
|
let rol = null
|
||||||
|
let loading = false;
|
||||||
|
|
||||||
|
$: onPage(page)
|
||||||
|
|
||||||
|
async function onPage(p) {
|
||||||
|
try {
|
||||||
|
loading = true
|
||||||
|
offset = (p - 1) * limit;
|
||||||
|
const data = await getRoles({ offset, limit, ordering })
|
||||||
|
roles = data.results;
|
||||||
|
count = data.count;
|
||||||
|
} catch (error) {
|
||||||
|
alert(error)
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEdita(item) {
|
||||||
|
rol = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNuevo() {
|
||||||
|
rol = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOrderBy(field) {
|
||||||
|
ordering = ordering === field ? '-' + field : field;
|
||||||
|
onPage(page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<PageTitle {loading}>Roles</PageTitle>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<button class="btn btn-primary" on:click|preventDefault={onNuevo}>
|
||||||
|
<i class="bi bi-plus-lg"></i> Nuevo
|
||||||
|
</button>
|
||||||
|
</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_rol')}>ID</a>
|
||||||
|
{#if ordering === 'id_rol'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||||
|
{#if ordering === '-id_rol'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href={"#"} on:click|preventDefault={() => onOrderBy('nombre_rol')}>Nombre</a>
|
||||||
|
{#if ordering === 'nombre_rol'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||||
|
{#if ordering === '-nombre_rol'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each roles as app, index}
|
||||||
|
<tr>
|
||||||
|
<td class="table-light">{offset + index + 1}</td>
|
||||||
|
<td>{app.id_rol}</td>
|
||||||
|
<td><a href={"#"} on:click|preventDefault={() => onEdita(app)}>{app.nombre_rol}</a></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
|
||||||
|
{offset}
|
||||||
|
{limit}
|
||||||
|
{count}
|
||||||
|
on:page={ev => page = ev.detail}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if rol}
|
||||||
|
<ModalRol
|
||||||
|
{rol}
|
||||||
|
on:close={() => rol = null}
|
||||||
|
on:refresh={() => onPage(page)}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.table-responsive {
|
||||||
|
max-height: calc(100vh - 300px);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,275 @@
|
||||||
|
<script>
|
||||||
|
import Modal from "../../components/Modal.svelte";
|
||||||
|
import { getRol, createRol, updateRol, deleteRol } from "$/services/roles";
|
||||||
|
import { getAplicaciones } from "$/services/aplicaciones";
|
||||||
|
import { getRolesyaplicaciones , createRolyaplicacion , updateRolyaplicacion , deleteRolyaplicacion } from "$/services/rolesyaplicaciones";
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
import { getRolesaplicaciones } from "../../services/rolesyaplicaciones";
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
|
||||||
|
export let rol = {}
|
||||||
|
let form = {}
|
||||||
|
let loading = false;
|
||||||
|
let form2 = []
|
||||||
|
let form3 = []
|
||||||
|
let rolaplicaciones = []
|
||||||
|
let form_inicio = []
|
||||||
|
|
||||||
|
|
||||||
|
$: begin(rol.id_rol)
|
||||||
|
$: begin2(rol.id_rol)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function begin(id) {
|
||||||
|
try {
|
||||||
|
if (!id) return;
|
||||||
|
form = await getRol(id) || {}
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.detail || error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function begin2(id) {
|
||||||
|
try {
|
||||||
|
form2 = await getAplicaciones() || []
|
||||||
|
if (!id) {
|
||||||
|
rolaplicaciones = []
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
rolaplicaciones = await getRolesyaplicaciones(id)
|
||||||
|
|
||||||
|
}
|
||||||
|
form3 = combinarArreglos(form2,rolaplicaciones)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(const obj1 of form3){
|
||||||
|
const nuevoItem = {
|
||||||
|
id_rol_app: obj1.id_rol_app,
|
||||||
|
id_aplicacion: obj1.id_aplicacion,
|
||||||
|
nombre_app: obj1.nombre_app,
|
||||||
|
vigente: obj1.vigente,
|
||||||
|
id_rol:obj1.id_rol,
|
||||||
|
solo_visualizar:obj1.solo_visualizar,
|
||||||
|
acceso:obj1.acceso
|
||||||
|
};
|
||||||
|
form_inicio.push(nuevoItem)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.detail || error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSave() {
|
||||||
|
try {
|
||||||
|
loading = true;
|
||||||
|
|
||||||
|
if (rol.id_rol) {
|
||||||
|
|
||||||
|
|
||||||
|
form = await updateRol(form)
|
||||||
|
|
||||||
|
form3 = CrearEditarEliminar(rol.id_rol,form_inicio,form3)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(const obj1 of form3){
|
||||||
|
|
||||||
|
if (obj1.opcion == "Crear"){
|
||||||
|
await createRolyaplicacion(obj1)
|
||||||
|
}
|
||||||
|
else if (obj1.opcion == "Editar"){
|
||||||
|
await updateRolyaplicacion(obj1)
|
||||||
|
}
|
||||||
|
else if (obj1.opcion == "Eliminar"){
|
||||||
|
await deleteRolyaplicacion(obj1.id_rol_app)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
form = await createRol(form)
|
||||||
|
form3 = Arreglo_crear(form3,form.id_rol)
|
||||||
|
for(const obj1 of form3){
|
||||||
|
await createRolyaplicacion(obj1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alert('Se ha guardado la aplicación')
|
||||||
|
dispatch('refresh')
|
||||||
|
dispatch('close')
|
||||||
|
} catch (error) {
|
||||||
|
if (error.detail) {
|
||||||
|
alert(error.detail)
|
||||||
|
} else {
|
||||||
|
alert(JSON.stringify(error))
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onDelete() {
|
||||||
|
try {
|
||||||
|
if (!confirm('Eliminará el registro?')) return;
|
||||||
|
loading = true;
|
||||||
|
for(const obj1 of form_inicio){
|
||||||
|
if (obj1.id_rol_app != false){
|
||||||
|
if(obj1.acceso = true)
|
||||||
|
await deleteRolyaplicacion(obj1.id_rol_app)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await deleteRol(form.id_rol)
|
||||||
|
alert('Se ha eliminado la aplicación')
|
||||||
|
dispatch('refresh')
|
||||||
|
dispatch('close')
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.detail || error)
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function combinarArreglos(arreglo1, arreglo2) {
|
||||||
|
const resultado = [];
|
||||||
|
|
||||||
|
for (const obj1 of arreglo1) {
|
||||||
|
const objResultado = { ...obj1 };
|
||||||
|
|
||||||
|
const obj2 = arreglo2.find(item => item.id_aplicacion === obj1.id_aplicacion);
|
||||||
|
|
||||||
|
if (obj2) {
|
||||||
|
objResultado.id_rol = obj2.id_rol !== undefined ? obj2.id_rol : null;
|
||||||
|
objResultado.solo_visualizar = obj2.solo_visualizar !== undefined ? obj2.solo_visualizar : null;
|
||||||
|
objResultado.id_rol_app = obj2.id_rol_app !== undefined ? obj2.id_rol_app : null;
|
||||||
|
objResultado.acceso = true;
|
||||||
|
} else {
|
||||||
|
objResultado.id_rol = null;
|
||||||
|
objResultado.solo_visualizar = null;
|
||||||
|
objResultado.acceso = false;
|
||||||
|
objResultado.id_rol_app = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultado.push(objResultado);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultado;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Arreglo_crear(input_data,id) {
|
||||||
|
const result = [];
|
||||||
|
for (const item of input_data) {
|
||||||
|
if (item.acceso) {
|
||||||
|
result.push({
|
||||||
|
"id_aplicacion": item.id_aplicacion,
|
||||||
|
"id_rol": id,
|
||||||
|
"solo_visualizar": item.solo_visualizar
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CrearEditarEliminar(id, arregloInicial, arregloFinal) {
|
||||||
|
const resultado = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < arregloInicial.length; i++) {
|
||||||
|
const inicio = arregloInicial[i];
|
||||||
|
const fin = arregloFinal[i];
|
||||||
|
|
||||||
|
const nuevoItem = {
|
||||||
|
id_aplicacion: inicio.id_aplicacion,
|
||||||
|
id_rol: id,
|
||||||
|
solo_visualizar: fin.solo_visualizar,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (inicio.acceso !== fin.acceso) {
|
||||||
|
nuevoItem.opcion = inicio.acceso ? "Eliminar" : "Crear";
|
||||||
|
if(inicio.acceso == true){
|
||||||
|
nuevoItem.id_rol_app = inicio.id_rol_app
|
||||||
|
}
|
||||||
|
} else if (inicio.solo_visualizar !== fin.solo_visualizar) {
|
||||||
|
nuevoItem.opcion = "Editar";
|
||||||
|
nuevoItem.id_rol_app = inicio.id_rol_app
|
||||||
|
} else {
|
||||||
|
nuevoItem.opcion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultado.push(nuevoItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultado;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form action="" on:submit|preventDefault={onSave}>
|
||||||
|
<Modal title={'rol #' + (rol.id_rol || 'Nuevo')}
|
||||||
|
size="lg"
|
||||||
|
on:close={() => dispatch('close')}>
|
||||||
|
<div class="form">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3">Rol ID</div>
|
||||||
|
<div class="col-md">
|
||||||
|
{#if rol.id_rol}
|
||||||
|
<input type="number" value={form.id_rol} disabled class="form-control">
|
||||||
|
{:else}
|
||||||
|
<input type="number" bind:value={form.id_rol} required class="form-control">
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3">Nombre Rol</div>
|
||||||
|
<div class="col-md">
|
||||||
|
<input type="text" bind:value={form.nombre_rol} required class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h4 class="h4 mb-3">Permiso a aplicaciones </h4>
|
||||||
|
<pre>
|
||||||
|
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<th>Aplicaciones</th>
|
||||||
|
<th>Acceso</th>
|
||||||
|
<th>Solo visualizar</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each form3 as aplicacion}
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td>{aplicacion.nombre_app}</td>
|
||||||
|
<td><input type="checkbox" bind:checked={aplicacion.acceso} ></td>
|
||||||
|
|
||||||
|
<td><input type="checkbox" bind:checked={aplicacion.solo_visualizar} disabled = {!aplicacion.acceso} ></td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<svelte:fragment slot="buttons">
|
||||||
|
<button class="btn btn-primary"type="submit" disabled={loading}>Guardar</button>
|
||||||
|
<button class="btn btn-danger" on:click|preventDefault={onDelete} disabled={loading}>Eliminar</button>
|
||||||
|
</svelte:fragment>
|
||||||
|
</Modal>
|
||||||
|
</form>
|
|
@ -0,0 +1,121 @@
|
||||||
|
<script>
|
||||||
|
import Paginate from "$/components/Paginate.svelte";
|
||||||
|
import { getRolesaplicaciones , getRolesyaplicaciones } from "$/services/rolesyaplicaciones";
|
||||||
|
import PageTitle from "$/components/PageTitle.svelte";
|
||||||
|
import ModalRol from "./ModalRolaplicacion.svelte";
|
||||||
|
|
||||||
|
const limit = 15;
|
||||||
|
let page = 1;
|
||||||
|
let offset = 0;
|
||||||
|
let count = 0;
|
||||||
|
let ordering = 'id_rol'
|
||||||
|
let roles = []
|
||||||
|
let rol = null
|
||||||
|
let loading = false;
|
||||||
|
let data2 = []
|
||||||
|
|
||||||
|
$: onPage(page)
|
||||||
|
|
||||||
|
async function onPage(p) {
|
||||||
|
try {
|
||||||
|
loading = true
|
||||||
|
offset = (p - 1) * limit;
|
||||||
|
const data = await getRolesaplicaciones({ offset, limit, ordering })
|
||||||
|
data2= await getRolesyaplicaciones(1)
|
||||||
|
roles = data.results;
|
||||||
|
count = data.count;
|
||||||
|
} catch (error) {
|
||||||
|
alert(error)
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEdita(item) {
|
||||||
|
rol = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNuevo() {
|
||||||
|
rol = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOrderBy(field) {
|
||||||
|
ordering = ordering === field ? '-' + field : field;
|
||||||
|
onPage(page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<PageTitle {loading}>rolesyaplicaciones</PageTitle>
|
||||||
|
<div>
|
||||||
|
{data2}
|
||||||
|
{#each data2 as rol}
|
||||||
|
{rol.id_rol}
|
||||||
|
{rol.id_aplicacion}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<button class="btn btn-primary" on:click|preventDefault={onNuevo}>
|
||||||
|
<i class="bi bi-plus-lg"></i> Nuevo
|
||||||
|
</button>
|
||||||
|
</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_aplicacion')}>ID rol</a>
|
||||||
|
{#if ordering === 'id_aplicacion'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||||
|
{#if ordering === '-id_aplicacion'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a href={"#"} on:click|preventDefault={() => onOrderBy('id_rol')}>Id app</a>
|
||||||
|
{#if ordering === 'id_rol'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||||
|
{#if ordering === '-id_rol'}<i class="bi bi-caret-down-fill"></i>{/if}
|
||||||
|
</th>
|
||||||
|
<th style="width:5%">Solo Visualizar</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each roles as app, index}
|
||||||
|
<tr>
|
||||||
|
<td class="table-light">{offset + index + 1}</td>
|
||||||
|
<td>{app.id_rol}</td>
|
||||||
|
<td><a href={"#"} on:click|preventDefault={() => onEdita(app)}>{app.id_aplicacion}</a></td>
|
||||||
|
<td>{app.solo_visualizar}</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
|
||||||
|
{offset}
|
||||||
|
{limit}
|
||||||
|
{count}
|
||||||
|
on:page={ev => page = ev.detail}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if rol}
|
||||||
|
<ModalRol
|
||||||
|
{rol}
|
||||||
|
on:close={() => rol = null}
|
||||||
|
on:refresh={() => onPage(page)}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.table-responsive {
|
||||||
|
max-height: calc(100vh - 300px);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,95 @@
|
||||||
|
<script>
|
||||||
|
import Modal from "../../components/Modal.svelte";
|
||||||
|
import { getRolyaplicacion, createRolyaplicacion, updateRolyaplicacion, deleteRolyaplicacion } from "$/services/rolesyaplicaciones";
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
export let rol = {}
|
||||||
|
let form = {}
|
||||||
|
let loading = false;
|
||||||
|
|
||||||
|
$: begin(rol.id_aplicacion)
|
||||||
|
|
||||||
|
async function begin(id) {
|
||||||
|
try {
|
||||||
|
if (!id) return;
|
||||||
|
form = await getRolyaplicacion(id) || {}
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.detail || error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSave() {
|
||||||
|
try {
|
||||||
|
loading = true;
|
||||||
|
if (rol.id_aplicacion) {
|
||||||
|
form = await updateRolyaplicacion(form)
|
||||||
|
} else {
|
||||||
|
form = await createRolyaplicacion(form)
|
||||||
|
}
|
||||||
|
alert('Se ha guardado la aplicación')
|
||||||
|
dispatch('refresh')
|
||||||
|
dispatch('close')
|
||||||
|
} catch (error) {
|
||||||
|
if (error.detail) {
|
||||||
|
alert(error.detail)
|
||||||
|
} else {
|
||||||
|
alert(JSON.stringify(error))
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onDelete() {
|
||||||
|
try {
|
||||||
|
if (!confirm('Eliminará el registro?')) return;
|
||||||
|
loading = true;
|
||||||
|
await deleteRolyaplicacion(form.id_aplicacion)
|
||||||
|
alert('Se ha eliminado la aplicación')
|
||||||
|
dispatch('refresh')
|
||||||
|
dispatch('close')
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.detail || error)
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form action="" on:submit|preventDefault={onSave}>
|
||||||
|
<Modal title={'Aplicacion #' + (rol.id_aplicacion || 'Nuevo')}
|
||||||
|
size="lg"
|
||||||
|
on:close={() => dispatch('close')}>
|
||||||
|
<div class="form">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3">ID app</div>
|
||||||
|
<div class="col-md">
|
||||||
|
{#if rol.id_aplicacion}
|
||||||
|
<input type="number" value={form.id_aplicacion} disabled class="form-control">
|
||||||
|
{:else}
|
||||||
|
<input type="number" bind:value={form.id_aplicacion} required class="form-control">
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3">ID rol</div>
|
||||||
|
<div class="col-md">
|
||||||
|
<input type="text" bind:value={form.id_rol} required class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" bind:checked={form.solo_visualizar} role="switch" id="vigente">
|
||||||
|
<label class="form-check-label" for="vigente">solo_visualizar</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<svelte:fragment slot="buttons">
|
||||||
|
<button class="btn btn-primary"type="submit" disabled={loading}>Guardar</button>
|
||||||
|
<button class="btn btn-danger" on:click|preventDefault={onDelete} disabled={loading}>Eliminar</button>
|
||||||
|
</svelte:fragment>
|
||||||
|
</Modal>
|
||||||
|
</form>
|
|
@ -11,6 +11,8 @@ import PagePersonaCreate from '$/pages/personas/Persona.svelte'
|
||||||
import PagePersonaModifica from '$/pages/personas/Persona.svelte'
|
import PagePersonaModifica from '$/pages/personas/Persona.svelte'
|
||||||
import PageMapaParaderos from '$/pages/mapas/Paraderos.svelte'
|
import PageMapaParaderos from '$/pages/mapas/Paraderos.svelte'
|
||||||
import PageMapaRutas from '$/pages/mapas/Rutas.svelte'
|
import PageMapaRutas from '$/pages/mapas/Rutas.svelte'
|
||||||
|
import PageRoles from '$/pages/roles/Admin.svelte'
|
||||||
|
import PageRolesyAplicaciones from '$/pages/rolesaplicaciones/Admin.svelte'
|
||||||
|
|
||||||
export const routes = [
|
export const routes = [
|
||||||
{ path: '/', component: PageHome },
|
{ path: '/', component: PageHome },
|
||||||
|
@ -19,6 +21,8 @@ export const routes = [
|
||||||
{ path: '/usuarios', component: PageUsuarios },
|
{ path: '/usuarios', component: PageUsuarios },
|
||||||
{ path: '/usuarios/nuevo', component: PageUsuarioCreate },
|
{ path: '/usuarios/nuevo', component: PageUsuarioCreate },
|
||||||
{ path: '/usuarios/:login', component: PageUsuarioModifica },
|
{ path: '/usuarios/:login', component: PageUsuarioModifica },
|
||||||
|
{ path: '/roles', component: PageRoles },
|
||||||
|
{ path: '/rolesaplicaciones', component: PageRolesyAplicaciones },
|
||||||
{ path: '/comunas', component: PageComunas },
|
{ path: '/comunas', component: PageComunas },
|
||||||
{ path: '/personas', component: PagePersonas },
|
{ path: '/personas', component: PagePersonas },
|
||||||
{ path: '/personas/nuevo', component: PagePersonaCreate },
|
{ path: '/personas/nuevo', component: PagePersonaCreate },
|
||||||
|
@ -26,4 +30,5 @@ export const routes = [
|
||||||
{ path: '/mapas/paraderos', component: PageMapaParaderos },
|
{ path: '/mapas/paraderos', component: PageMapaParaderos },
|
||||||
{ path: '/mapas/rutas', component: PageMapaRutas },
|
{ path: '/mapas/rutas', component: PageMapaRutas },
|
||||||
{ path: '*', component: PageError },
|
{ path: '*', component: PageError },
|
||||||
|
|
||||||
]
|
]
|
|
@ -2,7 +2,7 @@
|
||||||
import { base, getToken } from './_config'
|
import { base, getToken } from './_config'
|
||||||
|
|
||||||
export function getUrlImagen(id_paradero) {
|
export function getUrlImagen(id_paradero) {
|
||||||
return `${base}/paraderos/image/${id_paradero}`
|
return `${base}/paraderos-image/${id_paradero}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getParaderos(params) {
|
export async function getParaderos(params) {
|
||||||
|
@ -51,11 +51,21 @@ export async function deleteParadero(id) {
|
||||||
return res.json()
|
return res.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveImageParadero(id = null, file) {
|
export async function getParaderoImagenes(id_paradero) {
|
||||||
const form = new FormData()
|
const res = await fetch(`${base}/paraderos-image/?id_paradero=${id_paradero}`, {
|
||||||
form.append('imagen', file)
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.text()
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
const res = await fetch(`${base}/paraderos/image/${id}/`, {
|
export async function createParaderoImagen(id_paradero, file) {
|
||||||
|
const form = new FormData()
|
||||||
|
form.append('id_paradero', id_paradero)
|
||||||
|
form.append('imagen', file)
|
||||||
|
form.append('content_type', file.type)
|
||||||
|
|
||||||
|
const res = await fetch(`${base}/paraderos-image/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: form,
|
body: form,
|
||||||
headers: { "Authorization": `Bearer ${getToken()}` }
|
headers: { "Authorization": `Bearer ${getToken()}` }
|
||||||
|
@ -63,3 +73,12 @@ export async function saveImageParadero(id = null, file) {
|
||||||
if (!res.ok) throw await res.text()
|
if (!res.ok) throw await res.text()
|
||||||
return res.json()
|
return res.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteParaderoImagen(id_paradero_imagen) {
|
||||||
|
const res = await fetch(`${base}/paraderos-image/${id_paradero_imagen}/`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}` }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.text()
|
||||||
|
return res.text()
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { base, getToken } from './_config'
|
||||||
|
|
||||||
|
export async function getRoles(params) {
|
||||||
|
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||||
|
const res = await fetch(`${base}/roles/${query}`, {
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.json()
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRol(id) {
|
||||||
|
const res = await fetch(`${base}/roles/${id}/`, {
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.json()
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createRol(data) {
|
||||||
|
const res = await fetch(`${base}/roles/`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.json()
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateRol({ id_rol: id, ...data }) {
|
||||||
|
const res = await fetch(`${base}/roles/${id}/`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.json()
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteRol(id) {
|
||||||
|
const res = await fetch(`${base}/roles/${id}/`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.json()
|
||||||
|
return res.text()
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { base, getToken } from './_config'
|
||||||
|
|
||||||
|
export async function getRolesyaplicaciones(idRol) {
|
||||||
|
const params = idRol ? { id_rol: idRol } : null; // Preparar los parámetros de la consulta
|
||||||
|
const query = params ? '?' + new URLSearchParams(params).toString() : '';
|
||||||
|
const res = await fetch(`${base}/rolyaplicacion/${query}`, {
|
||||||
|
headers: {"Authorization": `Bearer ${getToken()}`,"Content-Type": "application/json"}
|
||||||
|
});
|
||||||
|
if (!res.ok) throw await res.json();
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRolesaplicaciones(params) {
|
||||||
|
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||||
|
const res = await fetch(`${base}/rolyaplicacion/${query}`, {
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.json()
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function getRolyaplicacion(id) {
|
||||||
|
const res = await fetch(`${base}/rolyaplicacion/${id}/`, {
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.json()
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createRolyaplicacion(data) {
|
||||||
|
const res = await fetch(`${base}/rolyaplicacion/`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.json()
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateRolyaplicacion({ id_rol_app: id, ...data }) {
|
||||||
|
const res = await fetch(`${base}/rolyaplicacion/${id}/`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.json()
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteRolyaplicacion(id) {
|
||||||
|
const res = await fetch(`${base}/rolyaplicacion/${id}/`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||||
|
})
|
||||||
|
if (!res.ok) throw await res.json()
|
||||||
|
return res.text()
|
||||||
|
}
|
Loading…
Reference in New Issue