avance mejora parada

develop/frontend-parada
Francisco Sandoval 2023-10-24 14:38:01 -03:00
parent ea07d6c579
commit 9958a53ebe
9 changed files with 473 additions and 208 deletions

View File

@ -0,0 +1,110 @@
<script>
// imagenes
import IconParada from "$/assets/parada-de-autobus.png";
// libs
import { createEventDispatcher } from "svelte";
import FormParadero from "./FormParadero.svelte";
import FormParaderoDispositivos from "./FormParaderoDispositivos.svelte";
import FormParaderoDetalle from "./FormParaderoDetalle.svelte";
import FormParaderoServicios from "./FormParaderoServicios.svelte";
const dispatch = createEventDispatcher();
export let parada = null;
let canvas = null;
let tab = 0;
$: init(!!parada);
function init(show) {
if (!canvas) return;
if (show) {
canvas.classList.add("show");
} else {
canvas.classList.remove("show");
tab = 0;
}
}
</script>
<div
class="offcanvas offcanvas-end"
tabindex="-1"
bind:this={canvas}
aria-labelledby="offcanvasParaderoLabel"
style="visibility: inherit; --bs-offcanvas-width: 500px;"
>
<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")}
/>
</div>
<div class="offcanvas-body">
<nav class="mb-3">
<ul class="nav nav-tabs">
<li class="nav-item">
<a class={"nav-link" + (tab === 0 ? ' active' : '')} href={"#"}
on:click|preventDefault={() => tab = 0}
>General</a>
</li>
<li class="nav-item">
<a class={"nav-link" + (tab === 1 ? ' active' : '')} href={"#"}
on:click|preventDefault={() => tab = 1}
>Dispositivos</a>
</li>
<li class="nav-item">
<a class={"nav-link" + (tab === 2 ? ' active' : '')} href={"#"}
on:click|preventDefault={() => tab = 2}
>Detalle</a>
</li>
<li class="nav-item">
<a class={"nav-link" + (tab === 3 ? ' active' : '')} href={"#"}
on:click|preventDefault={() => tab = 3}
>Servicios</a>
</li>
</ul>
</nav>
{#if tab === 0}
<FormParadero {parada} />
{/if}
{#if tab === 1}
<FormParaderoDispositivos {parada} />
{/if}
{#if tab === 2}
<FormParaderoDetalle {parada} />
{/if}
{#if tab === 3}
<FormParaderoServicios {parada} />
{/if}
</div>
</div>
{#if parada}
<div
class="offcanvas-backdrop fade show"
on:click|preventDefault={() => dispatch("close")}
on:keydown={() => {}}
/>
{/if}
{#if parada}
<style>
html {
overflow-y: hidden;
}
.seccion-imagen {
position: relative;
}
.seccion-imagen > a {
position: absolute;
bottom: 0.5rem;
right: 0.5rem;
}
</style>
{/if}

View File

@ -1,74 +1,66 @@
<script> <script>
// imagenes // imagenes
import IconParada from '$/assets/parada-de-autobus.png' import IconParada from "$/assets/parada-de-autobus.png";
// servicios // servicios
import { getParadero, updateParadero, getParaderoImagenes, createParaderoImagen, deleteParaderoImagen } from '$/services/paraderos' import {
// libs getParadero,
import { createEventDispatcher } from "svelte" updateParadero,
const dispatch = createEventDispatcher() getParaderoImagenes,
createParaderoImagen,
deleteParaderoImagen,
} from "$/services/paraderos";
export let parada = null export let parada = null;
let canvas = null let form = {};
let form = {} let imagenes = [];
let imagenes = []
$: init(!!parada) $: init(!!parada);
async function init(show) { async function init(show) {
if (!canvas) return;
!show && canvas.classList.remove('show')
show && canvas.classList.add('show')
try { try {
if (parada) { if (parada) {
form = await getParadero(parada.id_paradero) form = await getParadero(parada.id_paradero);
imagenes = await getParaderoImagenes(parada.id_paradero) imagenes = await getParaderoImagenes(parada.id_paradero);
} }
} catch(error) { } catch (error) {
alert(error) alert(error);
} }
} }
async function onSave() { async function onSave() {
try { try {
await updateParadero(form) await updateParadero(form);
alert('Información guardada') alert("Información guardada");
} catch(error) { } catch (error) {
alert(error) alert(error);
} }
} }
async function onSaveImagen({ target: form }) { async function onSaveImagen({ target: form }) {
try { try {
const [ file = null ] = form.file1.files; const [file = null] = form.file1.files;
await createParaderoImagen(parada.id_paradero, file) await createParaderoImagen(parada.id_paradero, file);
imagenes = await getParaderoImagenes(parada.id_paradero) imagenes = await getParaderoImagenes(parada.id_paradero);
form.file1.value = '' form.file1.value = "";
} catch (error) { } catch (error) {
alert(error) alert(error);
} }
} }
async function onDeleteImagen({ id_paradero_imagen }) { async function onDeleteImagen({ id_paradero_imagen }) {
try { try {
if (!confirm('Estás seguro de eliminar la imagen?')) return; if (!confirm("Estás seguro de eliminar la imagen?")) return;
await deleteParaderoImagen(id_paradero_imagen) await deleteParaderoImagen(id_paradero_imagen);
imagenes = imagenes.filter(imagen => imagen.id_paradero_imagen !== id_paradero_imagen) imagenes = imagenes.filter(
(imagen) => imagen.id_paradero_imagen !== id_paradero_imagen
);
} catch (error) { } catch (error) {
alert(error) alert(error);
} }
} }
</script> </script>
<div class="offcanvas offcanvas-end" tabindex="-1" bind:this={canvas} aria-labelledby="offcanvasParaderoLabel" style="visibility: inherit;"> <div>
<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: Coordenadas:
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-text">Latitud</div> <div class="input-group-text">Latitud</div>
@ -83,25 +75,39 @@
<form on:submit|preventDefault={onSave}> <form on:submit|preventDefault={onSave}>
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-text">Nombre</div> <div class="input-group-text">Nombre</div>
<input type="text" bind:value={form.stop_name} class="form-control"> <input
type="text"
bind:value={form.stop_name}
class="form-control"
/>
</div> </div>
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-text">Descripción</div> <div class="input-group-text">Descripción</div>
<input type="text" bind:value={form.stop_desc} class="form-control"> <input
type="text"
bind:value={form.stop_desc}
class="form-control"
/>
</div> </div>
<hr> <hr />
<div class="text-center"> <div class="text-center">
<button type="submit" class="btn btn-primary"><i class="bi bi-save"></i> Guardar</button> <button type="submit" class="btn btn-primary"
><i class="bi bi-save" /> Guardar</button
>
</div> </div>
</form> </form>
<div class="my-3"></div> <div class="my-3" />
<!-- imagenes --> <!-- imagenes -->
{#each imagenes as imagen} {#each imagenes as imagen}
<div class="seccion-imagen mb-3"> <div class="seccion-imagen mb-3">
<img src={imagen.url} alt="imagen paradero" class="img-fluid"> <img src={imagen.url} alt="imagen paradero" class="img-fluid" />
<a href={"#"} class="btn btn-danger" on:click|preventDefault={() => onDeleteImagen(imagen)}> <a
<i class="bi bi-trash"></i> Eliminar href={"#"}
class="btn btn-danger"
on:click|preventDefault={() => onDeleteImagen(imagen)}
>
<i class="bi bi-trash" /> Eliminar
</a> </a>
</div> </div>
{/each} {/each}
@ -111,29 +117,18 @@
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="form-control" style="overflow: hidden"> <div class="form-control" style="overflow: hidden">
<input type="file" name="file1" accept="*.png,*.jpg,*.jpeg"> <input
type="file"
name="file1"
accept="*.png,*.jpg,*.jpeg"
/>
</div> </div>
</div> </div>
<div class="card-footer text-center"> <div class="card-footer text-center">
<button class="btn btn-primary" type="submit"> <button class="btn btn-primary" type="submit">
<i class="bi bi-plus-lg"></i> Agregar imagen <i class="bi bi-plus-lg" /> Agregar imagen
</button> </button>
</div> </div>
</div> </div>
</form> </form>
</div>
</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}

View File

@ -0,0 +1,22 @@
<script>
import ModalParaderoDetalle from "./ModalParaderoDetalle.svelte";
export let parada = null
let showModal = false
</script>
<div>
<h4 class="mb-3">Detalle</h4>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<div class="text-center">
<button class="btn btn-secondary"
on:click|preventDefault={() => showModal = true}
>Información</button>
</div>
</div>
{#if showModal}
<ModalParaderoDetalle {parada} on:close={() => showModal = false} />
{/if}

View File

@ -0,0 +1,32 @@
<script>
export let parada = null
</script>
<div>
<h4 class="mb-3">Dispositivos</h4>
<table class="table table-bordered">
<thead>
<tr>
<th>ID Dipositivo</th>
<th>Tipo</th>
</tr>
</thead>
<tbody>
<tr>
<td>AAAAAAAAAA</td>
<td>Panel Led</td>
</tr>
<tr>
<td>BBBBBBBBBB</td>
<td>Totem</td>
</tr>
</tbody>
</table>
<div class="text-center mb-3">
<img loading="lazy" width="300" height="300" src="https://gravatar.com/avatar/2db5ce9c15a9178854325b844485702b?s=400&d=wavatar&r=x" alt="">
</div>
<div class="text-center">
<button class="btn btn-secondary">Imprimir QR</button>
</div>
</div>

View File

@ -0,0 +1,16 @@
<script>
export let parada = null
</script>
<div>
<h4 class="mb-3">Servicios</h4>
<div class="p-3 bg-white">
<ul class="list-group">
<li class="list-group-item">Servicio 1</li>
<li class="list-group-item">Servicio 2</li>
<li class="list-group-item">Servicio 3</li>
<li class="list-group-item">Servicio 4</li>
</ul>
</div>
</div>

View File

@ -0,0 +1,14 @@
<script>
import Modal from "../../components/Modal.svelte";
import { createEventDispatcher } from "svelte";
import TablaEditaParada from "../paraderos/TablaEditaParada.svelte";
const dispatch = createEventDispatcher()
export let parada = null
</script>
<Modal title="Detalle del Paradero" size="xl"
on:close={() => dispatch('close')}>
<TablaEditaParada {parada} />
</Modal>

View File

@ -2,7 +2,7 @@
// components // components
import PageTitle from "$/components/PageTitle.svelte" import PageTitle from "$/components/PageTitle.svelte"
import IconLoading from "$/components/IconLoading.svelte" import IconLoading from "$/components/IconLoading.svelte"
import FormParadero from "./FormParadero.svelte" import CanvasParadero from "./CanvasParadero.svelte"
// services // services
import { getRegiones } from "$/services/regiones" import { getRegiones } from "$/services/regiones"
@ -195,4 +195,4 @@
</div> </div>
</div> </div>
<FormParadero {parada} on:close={() => parada = null} /> <CanvasParadero {parada} on:close={() => parada = null} />

View File

@ -1,70 +1,6 @@
<script> <script>
import "./Home.css" import "./Home.css"
import TablaEditaParada from "./TablaEditaParada.svelte";
let table = null
let data = [ {} ]
function onKeyDownCell(ev) {
if (ev.key === 'Escape') {
ev.target.blur()
return;
}
if (ev.key === 'Enter') {
ev.returnValue = false;
const {nextElementSibling: nextTd} = ev.target;
if (nextTd && nextTd.contentEditable === 'true') {
nextTd.focus()
} else if (nextTd && nextTd.querySelector('input')) {
nextTd.querySelector('input').focus()
} else {
ev.target.blur()
const { cellIndex } = ev.target
const rowTable = ev.target.parentElement
if (rowTable.children.length === cellIndex + 1) {
data = [ ...data, {}]
setTimeout(() => {
table.querySelector('tbody > tr:last-child > td').focus()
},0)
}
}
return;
}
if (ev.key === 'ArrowDown') {
const { cellIndex } = ev.target
const { rowIndex } = ev.target.parentElement
const rowTable = table.querySelectorAll('tbody tr')[rowIndex] || null
if (rowTable) {
ev.target.blur()
const cellTable = rowTable.querySelectorAll('td')[cellIndex]
cellTable.focus()
}
return;
}
if (ev.key === 'ArrowUp') {
const { cellIndex } = ev.target
const { rowIndex } = ev.target.parentElement
const rowTable = table.querySelectorAll('tbody tr')[rowIndex - 2] || null
if (rowTable) {
ev.target.blur()
const cellTable = rowTable.querySelectorAll('td')[cellIndex]
cellTable.focus()
}
return;
}
}
function onKeyDownInput(ev) {
if (ev.key === 'Enter') {
ev.returnValue = false;
const {nextElementSibling: nextTd} = ev.target.parentElement;
if (nextTd && nextTd.contentEditable === 'true') {
nextTd.focus()
} else if (nextTd.querySelector('input')) {
nextTd.querySelector('input').focus()
}
}
}
</script> </script>
@ -73,51 +9,7 @@
<button class="btn btn-primary"><i class="bi bi-save"></i> Guardar</button> <button class="btn btn-primary"><i class="bi bi-save"></i> Guardar</button>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <TablaEditaParada parada={null} />
<table class="table table-hover table-bordered table-paraderos" bind:this={table}>
<thead>
<tr>
<th>Instalación</th>
<th>Secuencia</th>
<th>Comuna</th>
<th>Eje</th>
<th>Cod. Eje</th>
<th>Desde</th>
<th>Hasta</th>
<th>Uso Parada</th>
<th>Sentido</th>
<th>Tipo Parada</th>
<th>Nombre Corredor</th>
<th>Refugio</th>
<th>Categoría</th>
<th>Nombre Parada</th>
<th>Referencia Urbana</th>
</tr>
</thead>
<tbody>
{#each data as item}
<tr>
<td contenteditable data-name="instalacion" bind:innerText={item.instalacion} on:keydown={onKeyDownCell}></td>
<td contenteditable data-name="secuencia" bind:innerText={item.secuencia} on:keydown={onKeyDownCell}></td>
<!-- <td contenteditable bind:innerText={item.comuna} on:keydown={onKeyDownCell}></td> -->
<td><input type="text" list="ex1" class="form-select" on:keydown={onKeyDownInput}></td>
<td contenteditable bind:innerText={item.eje} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.codigo_eje} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.desde} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.hasta} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.uso_parada} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.sentido} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.tipo_parada} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.nombre_corredor} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.refugio} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.categoria} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.nombre_parada} on:keydown={onKeyDownCell}></td>
<td contenteditable bind:innerText={item.referencia_urbana} on:keydown={onKeyDownCell}></td>
</tr>
{/each}
</tbody>
</table>
</div>
</div> </div>
<div class="card-footer"></div> <div class="card-footer"></div>
</div> </div>

View File

@ -0,0 +1,184 @@
<script>
export let parada = null;
let table = null;
let data = [{}];
function onKeyDownCell(ev) {
if (ev.key === "Escape") {
ev.target.blur();
return;
}
if (ev.key === "Enter") {
ev.returnValue = false;
const { nextElementSibling: nextTd } = ev.target;
if (nextTd && nextTd.contentEditable === "true") {
nextTd.focus();
} else if (nextTd && nextTd.querySelector("input")) {
nextTd.querySelector("input").focus();
} else {
ev.target.blur();
const { cellIndex } = ev.target;
const rowTable = ev.target.parentElement;
if (rowTable.children.length === cellIndex + 1) {
data = [...data, {}];
setTimeout(() => {
table
.querySelector("tbody > tr:last-child > td")
.focus();
}, 0);
}
}
return;
}
if (ev.key === "ArrowDown") {
const { cellIndex } = ev.target;
const { rowIndex } = ev.target.parentElement;
const rowTable =
table.querySelectorAll("tbody tr")[rowIndex] || null;
if (rowTable) {
ev.target.blur();
const cellTable = rowTable.querySelectorAll("td")[cellIndex];
cellTable.focus();
}
return;
}
if (ev.key === "ArrowUp") {
const { cellIndex } = ev.target;
const { rowIndex } = ev.target.parentElement;
const rowTable =
table.querySelectorAll("tbody tr")[rowIndex - 2] || null;
if (rowTable) {
ev.target.blur();
const cellTable = rowTable.querySelectorAll("td")[cellIndex];
cellTable.focus();
}
return;
}
}
function onKeyDownInput(ev) {
if (ev.key === "Enter") {
ev.returnValue = false;
const { nextElementSibling: nextTd } = ev.target.parentElement;
if (nextTd && nextTd.contentEditable === "true") {
nextTd.focus();
} else if (nextTd.querySelector("input")) {
nextTd.querySelector("input").focus();
}
}
}
</script>
<div class="table-responsive">
<table
class="table table-hover table-bordered table-paraderos"
bind:this={table}
>
<thead>
<tr>
<th>Instalación</th>
<th>Secuencia</th>
<th>Comuna</th>
<th>Eje</th>
<th>Cod. Eje</th>
<th>Desde</th>
<th>Hasta</th>
<th>Uso Parada</th>
<th>Sentido</th>
<th>Tipo Parada</th>
<th>Nombre Corredor</th>
<th>Refugio</th>
<th>Categoría</th>
<th>Nombre Parada</th>
<th>Referencia Urbana</th>
</tr>
</thead>
<tbody>
{#each data as item}
<tr>
<td
contenteditable
data-name="instalacion"
bind:innerText={item.instalacion}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
data-name="secuencia"
bind:innerText={item.secuencia}
on:keydown={onKeyDownCell}
/>
<!-- <td contenteditable bind:innerText={item.comuna} on:keydown={onKeyDownCell}></td> -->
<td
><input
type="text"
list="ex1"
class="form-select"
on:keydown={onKeyDownInput}
/></td
>
<td
contenteditable
bind:innerText={item.eje}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.codigo_eje}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.desde}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.hasta}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.uso_parada}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.sentido}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.tipo_parada}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.nombre_corredor}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.refugio}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.categoria}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.nombre_parada}
on:keydown={onKeyDownCell}
/>
<td
contenteditable
bind:innerText={item.referencia_urbana}
on:keydown={onKeyDownCell}
/>
</tr>
{/each}
</tbody>
</table>
</div>