commit inicial
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["svelte.svelte-vscode"]
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
# Svelte + Vite
|
||||
|
||||
This template should help get you started developing with Svelte in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
|
||||
|
||||
## Need an official Svelte framework?
|
||||
|
||||
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
|
||||
|
||||
## Technical considerations
|
||||
|
||||
**Why use this over SvelteKit?**
|
||||
|
||||
- It brings its own routing solution which might not be preferable for some users.
|
||||
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
|
||||
|
||||
This template contains as little as possible to get started with Vite + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
|
||||
|
||||
Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
|
||||
|
||||
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
|
||||
|
||||
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
|
||||
|
||||
**Why include `.vscode/extensions.json`?**
|
||||
|
||||
Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
|
||||
|
||||
**Why enable `checkJs` in the JS template?**
|
||||
|
||||
It is likely that most cases of changing variable types in runtime are likely to be accidental, rather than deliberate. This provides advanced typechecking out of the box. Should you like to take advantage of the dynamically-typed nature of JavaScript, it is trivial to change the configuration.
|
||||
|
||||
**Why is HMR not preserving my local component state?**
|
||||
|
||||
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/sveltejs/svelte-hmr/tree/master/packages/svelte-hmr#preservation-of-local-state).
|
||||
|
||||
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
|
||||
|
||||
```js
|
||||
// store.js
|
||||
// An extremely simple external store
|
||||
import { writable } from 'svelte/store'
|
||||
export default writable(0)
|
||||
```
|
|
@ -0,0 +1,20 @@
|
|||
version: "3"
|
||||
name: transporte-frontend
|
||||
|
||||
services:
|
||||
app:
|
||||
image: node:18-alpine
|
||||
volumes:
|
||||
- ../:/app
|
||||
ports:
|
||||
- 3000:3000
|
||||
environment:
|
||||
- VITE_PORT=3000
|
||||
- VITE_BACKEND=http://localhost:4000/api
|
||||
# - VITE_BACKEND=http://transporte.hz.kursor.cl/api
|
||||
working_dir: /app
|
||||
command: sh -c "
|
||||
[ ! -d node_modules ] && npm install ;
|
||||
chmod -R o+w . ;
|
||||
npm run dev
|
||||
"
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>...</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "bundler",
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
/**
|
||||
* svelte-preprocess cannot figure out whether you have
|
||||
* a value or a type, so tell TypeScript to enforce using
|
||||
* `import type` instead of `import` for Types.
|
||||
*/
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
/**
|
||||
* To have warnings / errors of the Svelte compiler at the
|
||||
* correct position, enable source maps by default.
|
||||
*/
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
/**
|
||||
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||
* Disable this if you'd like to use dynamic types.
|
||||
*/
|
||||
"checkJs": true,
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"$/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Use global.d.ts instead of compilerOptions.types
|
||||
* to avoid limiting type declarations.
|
||||
*/
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "app",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "VITE_BACKEND= vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^2.0.4",
|
||||
"svelte": "^3.58.0",
|
||||
"vite": "^4.3.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@adminkit/core": "^3.4.0",
|
||||
"bootstrap": "^5.3.0",
|
||||
"bootstrap-icons": "^1.10.5",
|
||||
"history": "^5.3.0",
|
||||
"svelte-navigator": "^3.2.2",
|
||||
"svelte-pagination": "^0.0.1",
|
||||
"svelte-qrcode": "^1.0.0"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,74 @@
|
|||
@import url("https://fonts.googleapis.com/css?family=Roboto:300,400");
|
||||
@import url("https://fonts.googleapis.com/css?family=Roboto+Slab:300,400");
|
||||
|
||||
main .container { padding-top: 2rem; }
|
||||
footer { height: auto; }
|
||||
footer>.container { padding-bottom: 3rem; }
|
||||
footer>.container>.row { display: block; }
|
||||
|
||||
:root {
|
||||
--bs-body-font-family: "Roboto Slab",serif
|
||||
}
|
||||
|
||||
.sidebar, .sidebar-content {
|
||||
background: #006FB3;
|
||||
}
|
||||
|
||||
.sidebar-link, a.sidebar-link {
|
||||
background: rgba(255,255,255,.05);
|
||||
color: rgba(255,255,255,.8);
|
||||
}
|
||||
|
||||
.sidebar-item.active .sidebar-link:hover, .sidebar-item.active>.sidebar-link {
|
||||
background: rgba(0,0,0,.05);
|
||||
border-left-color: #FE6565;
|
||||
}
|
||||
|
||||
|
||||
.navbar-bg {
|
||||
position: relative;
|
||||
background: #cce0ef;
|
||||
}
|
||||
.navbar-bg::after {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
content: ' ';
|
||||
background: linear-gradient(to right,#0f69b4 0,#0f69b4 50%,#e22c2c 50%,#e22c2c 100%);
|
||||
}
|
||||
|
||||
h3::after {
|
||||
content: ' #';
|
||||
color: #006FB3;
|
||||
}
|
||||
|
||||
footer { position: relative; }
|
||||
footer::after {
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
height: 4px;
|
||||
bottom: 0;
|
||||
left: 1rem;
|
||||
content: ' ';
|
||||
background: linear-gradient(to right,#0f69b4 0,#0f69b4 50%,#e22c2c 50%,#e22c2c 100%);
|
||||
}
|
||||
|
||||
footer.footer { background: #0A132D; }
|
||||
.text-muted { color: #fff !important; }
|
||||
|
||||
input:required:invalid,
|
||||
select:required:invalid,
|
||||
textarea:required:invalid
|
||||
{
|
||||
border-color: var(--bs-danger);
|
||||
}
|
||||
|
||||
input:required:valid,
|
||||
select:required:valid,
|
||||
textarea:required:valid
|
||||
{
|
||||
border-color: var(--bs-success);
|
||||
}
|
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,3 @@
|
|||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
|
@ -0,0 +1,43 @@
|
|||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let title = "modal title";
|
||||
export let className = "";
|
||||
export let classTitle = "";
|
||||
export let classHeader = "";
|
||||
export let classBody = "";
|
||||
export let classFooter = "";
|
||||
export let size = "";
|
||||
</script>
|
||||
|
||||
<!-- Modal -->
|
||||
<div
|
||||
class={"modal show " + className}
|
||||
tabindex="-1"
|
||||
aria-hidden="true"
|
||||
style="display: block; background-color: rgb(0 0 0 / 30%)"
|
||||
>
|
||||
<div class={"modal-dialog " + (size ? " modal-" + size : "")}>
|
||||
<div class="modal-content">
|
||||
<div class={"modal-header " + classHeader}>
|
||||
<h5 class={"modal-title " + classTitle}>{title}</h5>
|
||||
<button type="button" class="btn-close" aria-label="Close"
|
||||
on:click|preventDefault={() => dispatch("close")}>
|
||||
</button>
|
||||
</div>
|
||||
<div class={"modal-body " + classBody}>
|
||||
<slot />
|
||||
</div>
|
||||
<div class={"modal-footer " + classFooter}>
|
||||
<slot name="buttons" />
|
||||
<div class="me-auto" />
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
on:click|preventDefault={() => dispatch("close")}>Cerrar</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
<script>
|
||||
import { onMount, createEventDispatcher } from 'svelte'
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let google_api_key = ''
|
||||
let mapEl = null
|
||||
|
||||
onMount(() => {
|
||||
if (!globalThis.google) {
|
||||
const el = document.createElement('script')
|
||||
el.src = "https://maps.googleapis.com/maps/api/js?key=" + google_api_key + "&callback=initMap&v=weekly"
|
||||
document.body.appendChild(el)
|
||||
} else {
|
||||
globalThis.initMap()
|
||||
}
|
||||
})
|
||||
|
||||
globalThis.initMap = function() {
|
||||
const google = globalThis.google;
|
||||
const google_map = new google.maps.Map(mapEl);
|
||||
dispatch('start', google_map)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:this={mapEl} style="height: 800px; max-height: 90vh;">Cargando...</div>
|
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
import IconLoading from "./IconLoading.svelte";
|
||||
export let loading = false;
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<h3 class="h3 mb-3"><slot/></h3>
|
||||
</div>
|
||||
{#if loading}
|
||||
<div class="col">
|
||||
<IconLoading />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
|
@ -0,0 +1,52 @@
|
|||
<script>
|
||||
import Pagination from "svelte-pagination";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let forcePage = 1;
|
||||
export let limit = 15;
|
||||
export let count = 0;
|
||||
export let disabled = false;
|
||||
let timePage = null;
|
||||
|
||||
function handleChange(e) {
|
||||
const { selected = 1 } = e.detail;
|
||||
dispatch("page", selected)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="pagination-container">
|
||||
<Pagination
|
||||
pageCount={Math.ceil(count / limit) +1}
|
||||
marginPagesDisplayed={2}
|
||||
pageRangeDisplayed={5}
|
||||
previousLabel="«"
|
||||
nextLabel="»"
|
||||
containerClassName="pagination px-0 mb-0"
|
||||
pageClassName={"page-item" + (disabled ? " disabled" : "")}
|
||||
previousClassName={"page-item" + (disabled ? " disabled" : "")}
|
||||
nextClassName={"page-item" + (disabled ? " disabled" : "")}
|
||||
breakClassName={"page-item" + (disabled ? " disabled" : "")}
|
||||
pageLinkClassName="page-link"
|
||||
previousLinkClassName="page-link"
|
||||
nextLinkClassName="page-link"
|
||||
breakLinkClassName="page-link"
|
||||
activeClassName="active"
|
||||
activeLinkClassName="active"
|
||||
hrefBuilder={() => {}}
|
||||
ariaLabelBuilder={null}
|
||||
extraAriaContext={null}
|
||||
initialPage={forcePage || 1}
|
||||
{forcePage}
|
||||
on:change={handleChange}
|
||||
/>
|
||||
|
||||
<span class="page-link d-inline-block bg-light text-info">
|
||||
{#if count}
|
||||
{count} registros
|
||||
{:else}
|
||||
Sin registros
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-bordered">
|
||||
<slot />
|
||||
</table>
|
||||
</div>
|
|
@ -0,0 +1,21 @@
|
|||
<footer class="footer">
|
||||
<div class="container-fluid">
|
||||
<div class="row text-muted">
|
||||
<div class="col-6 text-start">
|
||||
<p class="mb-0">
|
||||
<a class="text-muted" href="https://www.empresa.com/"
|
||||
target="_blank" rel="noreferrer">
|
||||
<strong>EMPRESA S.A.</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>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
|
@ -0,0 +1,37 @@
|
|||
<svelte:head>
|
||||
<title>Transporte: Acceso</title>
|
||||
</svelte:head>
|
||||
|
||||
<script>
|
||||
import { routes } from '$/routes/guest.routes'
|
||||
import { Router, Route, createHistory } from 'svelte-navigator'
|
||||
import hashHistory from './hashHistory'
|
||||
const history = createHistory(hashHistory())
|
||||
</script>
|
||||
|
||||
<Router {history} primary={false}>
|
||||
<main class="d-flex w-100">
|
||||
<div class="container d-flex flex-column">
|
||||
<div class="row vh-100">
|
||||
<div class="col-sm-10 col-md-8 col-lg-6 mx-auto d-table h-100">
|
||||
<div class="d-table-cell align-middle">
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<h1 class="h2">Gestión de Transporte</h1>
|
||||
<p class="lead">
|
||||
Acceda a su cuenta para continuar
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#each routes as r}
|
||||
<Route path={r.path}>
|
||||
<svelte:component this={r.component} />
|
||||
</Route>
|
||||
{/each}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Router>
|
|
@ -0,0 +1,65 @@
|
|||
<svelte:head>
|
||||
<title>Transporte: Usuario</title>
|
||||
</svelte:head>
|
||||
|
||||
<script>
|
||||
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||
import '@adminkit/core/dist/css/app.css'
|
||||
import '@adminkit/core/dist/js/app.js'
|
||||
import 'bootstrap-icons/font/bootstrap-icons.css'
|
||||
import '../assets/colors.css'
|
||||
import '../assets/custom.css'
|
||||
import Navbar from './Navbar.svelte'
|
||||
import Sidebar from './Sidebar.svelte'
|
||||
import Footer from './Footer.svelte'
|
||||
import Notifications from './Notifications.svelte'
|
||||
import { routes } from '$/routes/user.routes'
|
||||
import { Router, Route, createHistory } from 'svelte-navigator'
|
||||
import hashHistory from './hashHistory'
|
||||
import { onMount } from 'svelte'
|
||||
import { getInfoToken } from '$/services/login'
|
||||
import { storeSession } from '$/stores/global'
|
||||
|
||||
|
||||
let triggerEvent = false;
|
||||
const history = createHistory(hashHistory())
|
||||
|
||||
onMount(() => {
|
||||
triggerEvent && document.dispatchEvent(new Event('DOMContentLoaded'));
|
||||
})
|
||||
|
||||
async function begin() {
|
||||
try {
|
||||
$storeSession = await getInfoToken()
|
||||
} catch (error) {
|
||||
alert(error.message || error)
|
||||
}
|
||||
}
|
||||
begin()
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<Router {history} primary={false}>
|
||||
<Sidebar />
|
||||
|
||||
<div class="main">
|
||||
<Navbar />
|
||||
|
||||
<main class="content">
|
||||
<div class="container-fluid p-0">
|
||||
|
||||
{#each routes as r}
|
||||
<Route path={r.path}>
|
||||
<svelte:component this={r.component} />
|
||||
</Route>
|
||||
{/each}
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</Router>
|
||||
</div>
|
||||
|
||||
<Notifications />
|
|
@ -0,0 +1,32 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { storeMessages } from '$/stores/global'
|
||||
let count = 0
|
||||
let classBell = '';
|
||||
storeMessages.subscribe(val => count = val.length || 0)
|
||||
|
||||
$: if (count) {
|
||||
classBell = ' bell'
|
||||
setTimeout(() => classBell = '', 6000);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
return () => {
|
||||
count = 0
|
||||
storeMessages.set([])
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-icon dropdown-toggle" href={"#"} data-bs-toggle="offcanvas" data-bs-target="#offcanvasRight" aria-controls="offcanvasRight">
|
||||
<div class="position-relative">
|
||||
{#if count}
|
||||
<i class={"align-middle bi bi-bell-fill text-danger" + classBell}></i>
|
||||
<span class="indicator">{count}</span>
|
||||
{:else}
|
||||
<i class="align-middle bi bi-bell"></i>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
|
@ -0,0 +1,35 @@
|
|||
<script>
|
||||
import { storeSession } from '$/stores/global'
|
||||
import { Link } from 'svelte-navigator'
|
||||
|
||||
let avatar = null;
|
||||
let persona = {}
|
||||
|
||||
$: persona = $storeSession
|
||||
$: avatar = $storeSession?.avatar_img || '/avatars/avatar2.png';
|
||||
|
||||
function onLogout() {
|
||||
sessionStorage.clear();
|
||||
document.location.href = '/';
|
||||
}
|
||||
</script>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-icon dropdown-toggle d-inline-block d-sm-none" href={"#"} data-bs-toggle="dropdown">
|
||||
<i class="align-middle" data-feather="settings"></i>
|
||||
</a>
|
||||
|
||||
<a class="nav-link dropdown-toggle d-none d-sm-inline-block" href={"#"} data-bs-toggle="dropdown">
|
||||
<img src={avatar} class="avatar img-fluid rounded me-1" alt={persona.nombres} />
|
||||
<span class="text-dark">{$storeSession.login}</span>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<Link class="dropdown-item" to="/perfil">
|
||||
<i class="align-middle me-1" data-feather="user"></i> Perfil
|
||||
</Link>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger" href={"#"} on:click|preventDefault={onLogout}>
|
||||
Salir
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
|
@ -0,0 +1,17 @@
|
|||
<script>
|
||||
import NavUser from './NavUser.svelte'
|
||||
import NavNotices from './NavNotices.svelte'
|
||||
</script>
|
||||
|
||||
<nav class="navbar navbar-expand navbar-light navbar-bg">
|
||||
<a class="sidebar-toggle js-sidebar-toggle" href={null}>
|
||||
<i class="hamburger align-self-center"></i>
|
||||
</a>
|
||||
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="navbar-nav navbar-align">
|
||||
<NavNotices />
|
||||
<NavUser />
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
|
@ -0,0 +1,53 @@
|
|||
<script>
|
||||
import { deleteNotificacion } from '$/services/notificaciones'
|
||||
import { storeMessages } from '$/stores/global'
|
||||
let messages = []
|
||||
let hoy = (new Date()).toISOString().substring(0,10);
|
||||
|
||||
storeMessages.subscribe(val => messages = val)
|
||||
|
||||
async function onDelete(id) {
|
||||
try {
|
||||
await deleteNotificacion(id)
|
||||
storeMessages.update(val => val.filter(el => el.id !== id))
|
||||
} catch(error) {
|
||||
globalThis.toast.error(error.message || error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function formatTime(time) {
|
||||
if (!time) return '--:--';
|
||||
const myDate = new Date(time);
|
||||
|
||||
if (myDate.toISOString() < hoy) {
|
||||
return myDate.toLocaleString()
|
||||
} else {
|
||||
return myDate.toLocaleTimeString()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasRight" aria-labelledby="offcanvasRightLabel" style="visibility: inherit;">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title text-info" id="offcanvasRightLabel"><i class="bi bi-bell-fill text-info"></i> Alertas</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
{#each messages as m, index}
|
||||
<div class={"alert d-flex mb-3" + (index === 0 ? ' alert-primary shadow' : ' alert-info shadow-sm')}>
|
||||
<i class="bi bi-info-circle fs-1 me-2"></i>
|
||||
<div>
|
||||
<strong>{formatTime(m.fecha_hora)}</strong>
|
||||
<div>{m.mensaje}</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close ms-auto" aria-label="Close" on:click|preventDefault={() => onDelete(m.id)}></button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.shadow { box-shadow: 0 .5rem 1rem var(--bs-primary)!important; }
|
||||
.shadow-sm { box-shadow: 0 .125rem .25rem var(--bs-info)!important; }
|
||||
</style>
|
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
import { Link, useMatch } from 'svelte-navigator'
|
||||
|
||||
export let to = '#'
|
||||
const absoluteMatch = useMatch(to)
|
||||
|
||||
let match;
|
||||
$: match = $absoluteMatch?.fullPath === to;
|
||||
</script>
|
||||
|
||||
<li class={"sidebar-item" + (match ? ' active' : '')}>
|
||||
<Link class="sidebar-link" {to}>
|
||||
<slot />
|
||||
</Link>
|
||||
</li>
|
|
@ -0,0 +1,80 @@
|
|||
<script>
|
||||
import { Link } from 'svelte-navigator'
|
||||
import SideLink from './SideLink.svelte'
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar js-sidebar">
|
||||
<div class="sidebar-content js-simplebar">
|
||||
<Link class="sidebar-brand" to="/">
|
||||
<span class="fs-1">🚌</span>
|
||||
<span class="align-middle">
|
||||
Transporte
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<ul class="sidebar-nav">
|
||||
<li class="sidebar-header">Pages</li>
|
||||
|
||||
<SideLink to="/">
|
||||
<i class="align-middle bi bi-house fs-4"></i>
|
||||
<span class="align-middle">Inicio</span>
|
||||
</SideLink>
|
||||
|
||||
<SideLink to="/perfil">
|
||||
<i class="align-middle bi bi-person-lines-fill fs-4"></i>
|
||||
<span class="align-middle">Perfil</span>
|
||||
</SideLink>
|
||||
|
||||
<li class="sidebar-header">Mapas</li>
|
||||
|
||||
<SideLink to="/mapas/paraderos">
|
||||
<i class="align-middle bi bi-map fs-4" />
|
||||
<span class="align-middle">Paraderos</span>
|
||||
</SideLink>
|
||||
|
||||
<SideLink to="/mapas/rutas">
|
||||
<i class="align-middle bi bi-map fs-4" />
|
||||
<span class="align-middle">Rutas</span>
|
||||
</SideLink>
|
||||
|
||||
<li class="sidebar-header">Mantenedores</li>
|
||||
|
||||
<SideLink to="/paraderos">
|
||||
<i class="align-middle bi bi-bus-front fs-4" />
|
||||
<span class="align-middle">Paraderos</span>
|
||||
</SideLink>
|
||||
|
||||
<SideLink to="/aplicaciones">
|
||||
<i class="align-middle bi bi-terminal fs-4" />
|
||||
<span class="align-middle">Aplicaciones</span>
|
||||
</SideLink>
|
||||
|
||||
<SideLink to="/rutas">
|
||||
<i class="align-middle bi bi-sign-turn-right fs-4"></i>
|
||||
<span class="align-middle">Servicios de Buses</span>
|
||||
</SideLink>
|
||||
|
||||
<SideLink to="/comunas">
|
||||
<i class="align-middle bi bi-geo-alt fs-4" />
|
||||
<span class="align-middle">Comunas</span>
|
||||
</SideLink>
|
||||
|
||||
<SideLink to="/usuarios">
|
||||
<i class="align-middle bi bi-people fs-4" />
|
||||
<span class="align-middle">Usuarios</span>
|
||||
</SideLink>
|
||||
|
||||
<SideLink to="/personas">
|
||||
<i class="align-middle bi bi-people fs-4" />
|
||||
<span class="align-middle">Personas</span>
|
||||
</SideLink>
|
||||
|
||||
<SideLink to="/roles">
|
||||
<i class="align-middle bi bi-person-badge fs-4" />
|
||||
<span class="align-middle">Roles</span>
|
||||
</SideLink>
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</nav>
|
|
@ -0,0 +1,43 @@
|
|||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { createHashHistory } from "history";
|
||||
|
||||
function createHashSource() {
|
||||
const history = createHashHistory();
|
||||
let listeners = [];
|
||||
|
||||
history.listen(location => {
|
||||
if (history.action === "POP") {
|
||||
listeners.forEach(listener => listener(location));
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
get location() {
|
||||
return history.location;
|
||||
},
|
||||
addEventListener(name, handler) {
|
||||
if (name !== "popstate") return;
|
||||
listeners.push(handler);
|
||||
},
|
||||
removeEventListener(name, handler) {
|
||||
if (name !== "popstate") return;
|
||||
listeners = listeners.filter(fn => fn !== handler);
|
||||
},
|
||||
history: {
|
||||
get state() {
|
||||
return history.location.state;
|
||||
},
|
||||
pushState(state, title, uri) {
|
||||
history.push(uri, state);
|
||||
},
|
||||
replaceState(state, title, uri) {
|
||||
history.replace(uri, state);
|
||||
},
|
||||
go(to) {
|
||||
history.go(to);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default createHashSource;
|
|
@ -0,0 +1,13 @@
|
|||
import AppGuest from '$/layouts/LayoutGuest.svelte'
|
||||
import AppUser from '$/layouts/LayoutUser.svelte'
|
||||
import { getToken } from '$/services/_config'
|
||||
|
||||
let app = null;
|
||||
|
||||
if (getToken()) {
|
||||
app = new AppUser({ target: document.getElementById('app') })
|
||||
} else {
|
||||
app = new AppGuest({ target: document.getElementById('app') })
|
||||
}
|
||||
|
||||
export default app
|
|
@ -0,0 +1,116 @@
|
|||
<script>
|
||||
import Paginate from "$/components/Paginate.svelte";
|
||||
import { getAplicaciones } from "$/services/aplicaciones";
|
||||
import PageTitle from "$/components/PageTitle.svelte";
|
||||
import ModalAplicacion from "./ModalAplicacion.svelte";
|
||||
|
||||
const limit = 15;
|
||||
let page = 1;
|
||||
let offset = 0;
|
||||
let count = 0;
|
||||
let ordering = 'id_aplicacion'
|
||||
let aplicaciones = []
|
||||
let aplicacion = null
|
||||
let loading = false;
|
||||
|
||||
$: onPage(page)
|
||||
|
||||
async function onPage(p) {
|
||||
try {
|
||||
loading = true
|
||||
offset = (p - 1) * limit;
|
||||
const data = await getAplicaciones({ offset, limit, ordering })
|
||||
aplicaciones = data.results;
|
||||
count = data.count;
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onEdita(item) {
|
||||
aplicacion = item;
|
||||
}
|
||||
|
||||
function onNuevo() {
|
||||
aplicacion = {}
|
||||
}
|
||||
|
||||
function onOrderBy(field) {
|
||||
ordering = ordering === field ? '-' + field : field;
|
||||
onPage(page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<PageTitle {loading}>Aplicaciones</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_aplicacion')}>ID</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('nombre_app')}>Nombre</a>
|
||||
{#if ordering === 'nombre_app'}<i class="bi bi-caret-up-fill"></i>{/if}
|
||||
{#if ordering === '-nombre_app'}<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 aplicaciones as app, index}
|
||||
<tr>
|
||||
<td class="table-light">{offset + index + 1}</td>
|
||||
<td>{app.id_aplicacion}</td>
|
||||
<td><a href={"#"} on:click|preventDefault={() => onEdita(app)}>{app.nombre_app}</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
|
||||
{offset}
|
||||
{limit}
|
||||
{count}
|
||||
on:page={ev => page = ev.detail}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{#if aplicacion}
|
||||
<ModalAplicacion
|
||||
{aplicacion}
|
||||
on:close={() => aplicacion = null}
|
||||
on:refresh={() => onPage(page)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.table-responsive {
|
||||
max-height: calc(100vh - 300px);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,93 @@
|
|||
<script>
|
||||
import Modal from "../../components/Modal.svelte";
|
||||
import { getAplicacion, createAplicacion, updateAplicacion, deleteAplicacion } from "$/services/aplicaciones";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let aplicacion = {}
|
||||
let form = {}
|
||||
let loading = false;
|
||||
|
||||
$: begin(aplicacion.id_aplicacion)
|
||||
|
||||
async function begin(id) {
|
||||
try {
|
||||
if (!id) return;
|
||||
form = await getAplicacion(id) || {}
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
}
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
try {
|
||||
loading = true;
|
||||
if (aplicacion.id_aplicacion) {
|
||||
form = await updateAplicacion(form)
|
||||
} else {
|
||||
form = await createAplicacion(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 deleteAplicacion(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 #' + (aplicacion.id_aplicacion || 'Nuevo')}
|
||||
size="lg"
|
||||
on:close={() => dispatch('close')}>
|
||||
<div class="form">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">ID</div>
|
||||
<div class="col-md">
|
||||
{#if aplicacion.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">Nombre</div>
|
||||
<div class="col-md">
|
||||
<input type="text" bind:value={form.nombre_app} 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.vigente} role="switch" id="vigente">
|
||||
<label class="form-check-label" for="vigente">Vigente</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>
|
|
@ -0,0 +1,146 @@
|
|||
<script>
|
||||
import { Link } from 'svelte-navigator'
|
||||
import Paginate from '$/components/Paginate.svelte'
|
||||
import PageTitle from '$/components/PageTitle.svelte'
|
||||
import { getComunas, getComuna, createComuna, updateComuna, deleteComuna } from '$/services/comunas'
|
||||
import Modal from '$/components/Modal.svelte';
|
||||
import FormComuna from './Form.svelte';
|
||||
|
||||
let comuna = null
|
||||
let es_nuevo = true
|
||||
let comunas = { count: 0, results: [] }
|
||||
let page = 1
|
||||
let loading = false;
|
||||
const limit = 15
|
||||
|
||||
$: onPage(page)
|
||||
|
||||
async function onPage(page) {
|
||||
try {
|
||||
loading = true
|
||||
const offset = (page - 1) * limit;
|
||||
comunas = await getComunas({ offset, limit, ordering: 'id_region' })
|
||||
} catch(error) {
|
||||
alert(error.detail || error)
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
function onNuevo() {
|
||||
comuna = {}
|
||||
es_nuevo = true
|
||||
}
|
||||
|
||||
async function onEditar({ id_comuna }) {
|
||||
try {
|
||||
loading = true
|
||||
comuna = await getComuna(id_comuna)
|
||||
es_nuevo = false
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
try {
|
||||
if (es_nuevo) {
|
||||
await createComuna(comuna)
|
||||
} else {
|
||||
await updateComuna(comuna)
|
||||
}
|
||||
alert('Comuna guardada con exito')
|
||||
comuna = null; // se cierra el modal
|
||||
onPage(page) // refresco la tabla de registros
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
}
|
||||
}
|
||||
|
||||
async function onDelete() {
|
||||
try {
|
||||
if (!confirm(`Desea eliminar la comuna [${comuna.nombre_comuna}]?`)) return;
|
||||
await deleteComuna(comuna.id_comuna)
|
||||
alert('Comuna eliminada con exito')
|
||||
comuna = null; // se cierra el modal
|
||||
onPage(page) // refresco la tabla de registros
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<PageTitle {loading}>Comunas</PageTitle>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="mb-3 d-flex">
|
||||
<button class="btn btn-primary me-3"
|
||||
on:click|preventDefault={onNuevo}>
|
||||
<i class="bi bi-plus-lg"></i> Nuevo
|
||||
</button>
|
||||
<div class="m-auto"></div>
|
||||
<Link to='/' class="btn btn-outline-secondary">Volver</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr class="table-light">
|
||||
<th>ID</th>
|
||||
<th>Nombre</th>
|
||||
<th>Región</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each comunas.results as row}
|
||||
<tr>
|
||||
<td><a class="d-block" href={"#"} on:click|preventDefault={() => onEditar(row)}>{row.id_comuna}</a></td>
|
||||
<td>{row.nombre_comuna}</td>
|
||||
<td>{row.id_region}</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={(page - 1) * limit}
|
||||
{limit}
|
||||
count={comunas.count}
|
||||
on:page={ev => page = ev.detail}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{#if !!comuna}
|
||||
<form action="" on:submit|preventDefault={onSave}>
|
||||
<Modal title="Comuna"
|
||||
on:close={() => comuna = null}
|
||||
>
|
||||
<FormComuna bind:form={comuna} {es_nuevo} />
|
||||
|
||||
<svelte:fragment slot="buttons">
|
||||
<button type="submit" class="btn btn-primary">Guardar</button>
|
||||
{#if !es_nuevo}
|
||||
<button class="btn btn-danger" on:click|preventDefault={onDelete}>Eliminar</button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.table-responsive {
|
||||
max-height: calc(100vh - 300px);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,37 @@
|
|||
<script>
|
||||
import { getRegiones } from '$/services/regiones'
|
||||
export let form = {}
|
||||
export let es_nuevo = true
|
||||
|
||||
let regiones = []
|
||||
|
||||
$: begin()
|
||||
|
||||
async function begin() {
|
||||
try {
|
||||
regiones = await getRegiones()
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">ID</div>
|
||||
<input type="number" bind:value={form.id_comuna} class="form-control" disabled>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Comuna</div>
|
||||
<input type="text" bind:value={form.nombre_comuna} class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Región</div>
|
||||
<select bind:value={form.id_region} class="form-select" required>
|
||||
<option value=""></option>
|
||||
{#each regiones as region}
|
||||
<option value={region.id_region}>{region.nombre_region}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
|
@ -0,0 +1,86 @@
|
|||
<script>
|
||||
import { getLineas } from "$/services/lineas";
|
||||
import { getOperadores } from "$/services/operadores";
|
||||
|
||||
export let id_operador;
|
||||
export let id_linea;
|
||||
export let codigo;
|
||||
export let ver_buses;
|
||||
export let ver_paraderos;
|
||||
|
||||
let operadores = []
|
||||
let lineas = []
|
||||
let lineas_operador = []
|
||||
|
||||
getOperadores({ vigente: 1 })
|
||||
.then(data => data.sort((a,b) => a.nombre_operador < b.nombre_operador? -1 : 1))
|
||||
.then(data => operadores = data)
|
||||
.catch(error => alert(error))
|
||||
|
||||
getLineas({ vigente: 1 })
|
||||
.then(data => data.sort((a,b) => a.nombre_linea < b.nombre_linea? -1 : 1))
|
||||
.then(data => lineas = data)
|
||||
.catch(error => alert(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);
|
||||
const lineas_ordenadas = lineas_filtradas.sort((a,b) => a.route_short_name < b.route_short_name ? -1 : 1);
|
||||
lineas_operador = lineas_ordenadas;
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,161 @@
|
|||
<script>
|
||||
// servicios
|
||||
import {
|
||||
getParadero,
|
||||
updateParadero,
|
||||
getParaderoImagenes,
|
||||
createParaderoImagen,
|
||||
deleteParaderoImagen,
|
||||
} from "$/services/paraderos";
|
||||
import IconLoading from "../../components/IconLoading.svelte";
|
||||
|
||||
export let parada = null;
|
||||
let form = {};
|
||||
let imagenes = [];
|
||||
let loading = false;
|
||||
|
||||
$: init(!!parada);
|
||||
|
||||
async function init(show) {
|
||||
try {
|
||||
if (parada) {
|
||||
loading = true;
|
||||
form = await getParadero(parada.id_paradero);
|
||||
imagenes = await getParaderoImagenes(parada.id_paradero);
|
||||
}
|
||||
} catch (error) {
|
||||
alert(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
{#if loading}
|
||||
<IconLoading />
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
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">Identificador</div>
|
||||
<input
|
||||
type="text"
|
||||
disabled
|
||||
value={form.id_paradero || '...'}
|
||||
class="form-control"
|
||||
/>
|
||||
</div>
|
||||
<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" /> Guardar</button
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
<div class="my-3" />
|
||||
|
||||
<!-- 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" /> 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" /> Agregar imagen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.seccion-imagen {
|
||||
position: relative;
|
||||
}
|
||||
.seccion-imagen > a {
|
||||
position: absolute;
|
||||
bottom: 0.5rem;
|
||||
right: 0.5rem;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,142 @@
|
|||
<script>
|
||||
import QrCode from 'svelte-qrcode'
|
||||
import IconLoading from "../../components/IconLoading.svelte";
|
||||
import { createDispositivo, deleteDispositivo, getDispositivos } from "../../services/dispositivos";
|
||||
import { getTiposDispositivo } from "../../services/tipos_dispositivo";
|
||||
import { url_base } from "../../services/_config";
|
||||
import { getInfoPublic } from '../../services/paraderos';
|
||||
|
||||
export let parada = null;
|
||||
let loading = false;
|
||||
let tipos_dispositivos = [];
|
||||
let dispositivos = [];
|
||||
let url_qrcode = null;
|
||||
let nuevoDispositivo = null;
|
||||
|
||||
async function fetchDispositivos({ id_paradero = null }) {
|
||||
try {
|
||||
if (!id_paradero) return;
|
||||
loading = true
|
||||
dispositivos = await getDispositivos({ id_paradero })
|
||||
url_qrcode = `${url_base}/public/infoStop?codigoParadero=${id_paradero}`;
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchInfoPublico({ id_paradero = null }) {
|
||||
try {
|
||||
if (!id_paradero) return;
|
||||
const info = await getInfoPublic(id_paradero)
|
||||
url_qrcode = info.url_public
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchTiposDispositivos() {
|
||||
try {
|
||||
tipos_dispositivos = await getTiposDispositivo()
|
||||
} catch (error) {
|
||||
console.log({ error })
|
||||
}
|
||||
}
|
||||
|
||||
$: Promise.all([fetchDispositivos(parada), fetchInfoPublico(parada), fetchTiposDispositivos()])
|
||||
|
||||
async function onSave() {
|
||||
try {
|
||||
loading = true
|
||||
await createDispositivo(nuevoDispositivo)
|
||||
const tipo_dispositivo = tipos_dispositivos.find(el => el.id_tipo_dispositivo === nuevoDispositivo.id_tipo_dispositivo) || {}
|
||||
dispositivos = [ ...dispositivos, { ...nuevoDispositivo, tipo_dispositivo }]
|
||||
nuevoDispositivo = null
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onDelete({ id_dispositivo }) {
|
||||
try {
|
||||
if (!confirm(`Desea eliminar el dispositivo ${id_dispositivo} ?`)) return;
|
||||
loading = true
|
||||
await deleteDispositivo(id_dispositivo)
|
||||
dispositivos = dispositivos.filter(el => el.id_dispositivo !== id_dispositivo)
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h4 class="mb-3">Dispositivos</h4>
|
||||
{#if !nuevoDispositivo}
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID Dipositivo</th>
|
||||
<th>Tipo</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each dispositivos as disp}
|
||||
<tr>
|
||||
<td>{disp.id_dispositivo}</td>
|
||||
<td>{disp.tipo_dispositivo?.descripcion || '--'}</td>
|
||||
<td><a href={"#"} on:click|preventDefault={() => onDelete(disp)}><i class="bi bi-trash text-danger"></i></a></td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="my-3 text-center">
|
||||
<button class="btn btn-outline-primary" on:click|preventDefault={() => nuevoDispositivo = {}}>Nuevo Dispositivo</button>
|
||||
</div>
|
||||
{:else}
|
||||
<form action="" on:submit|preventDefault={onSave}>
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">ID Dispositivo</div>
|
||||
<input type="text" class="form-control" bind:value={nuevoDispositivo.id_dispositivo}>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">Tipo Dispositivo</div>
|
||||
<select class="form-select" bind:value={nuevoDispositivo.id_tipo_dispositivo}>
|
||||
{#each tipos_dispositivos as tipo}
|
||||
<option value={tipo.id_tipo_dispositivo}>{ tipo.descripcion }</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between my-3">
|
||||
<button type="submit" class="btn btn-primary">Guardar</button>
|
||||
<button class="btn btn-outline-secondary" on:click|preventDefault={() => nuevoDispositivo = null}>Cancelar</button>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
{#if loading}
|
||||
<div>
|
||||
<IconLoading />
|
||||
Cargando información
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="m-6"></div>
|
||||
|
||||
<div class="text-center mb-3">
|
||||
{#if url_qrcode}
|
||||
<QrCode value={url_qrcode} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button class="btn btn-secondary">Imprimir QR</button>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,71 @@
|
|||
<script>
|
||||
import IconLoading from "../../components/IconLoading.svelte";
|
||||
import { getLinea } from "../../services/lineas";
|
||||
import { getOperador } from "../../services/operadores";
|
||||
import { getLineasParadero } from "../../services/paraderos";
|
||||
|
||||
export let parada = null;
|
||||
let servicios = [];
|
||||
let loading = false;
|
||||
|
||||
async function fetchData({ id_paradero = null }) {
|
||||
try {
|
||||
if (!id_paradero) return;
|
||||
loading = true;
|
||||
const resultado = await getLineasParadero(id_paradero);
|
||||
|
||||
const lineas = await Promise.all(
|
||||
resultado.map(({ id_linea }) => getLinea(id_linea)),
|
||||
);
|
||||
const id_operadores = [
|
||||
...new Set(lineas.map((linea) => linea.id_operador)),
|
||||
];
|
||||
|
||||
const operadores = await Promise.all(
|
||||
id_operadores.map((id_operador) => getOperador(id_operador)),
|
||||
);
|
||||
|
||||
servicios = resultado.map((el) => {
|
||||
const { id_paradero_linea, id_linea } = el;
|
||||
const linea =
|
||||
lineas.find((el) => id_linea === el.id_linea) || {};
|
||||
const { nombre_operador } =
|
||||
operadores.find(
|
||||
(op) => op.id_operador === linea.id_operador,
|
||||
) || {};
|
||||
return { id_paradero_linea, ...linea, nombre_operador };
|
||||
});
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
$: fetchData(parada);
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h4 class="mb-3">Servicios</h4>
|
||||
|
||||
<div class="p-3 bg-white">
|
||||
{#if loading}
|
||||
<div>
|
||||
<IconLoading />
|
||||
Cargando información
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<ul class="list-group">
|
||||
{#each servicios as servicio}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">{servicio.nombre_operador}</div>
|
||||
Destino: {servicio.route_long_name}
|
||||
</div>
|
||||
<span class="badge bg-primary rounded-pill">{servicio.route_short_name}</span>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,75 @@
|
|||
<script>
|
||||
import Modal from "../../components/Modal.svelte";
|
||||
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { getLetreroLUR } from "../../services/letreros_lur";
|
||||
import IconLoading from "../../components/IconLoading.svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let codigo = null;
|
||||
|
||||
let letrero = {};
|
||||
let codigo_dividido = []
|
||||
let loading = false
|
||||
|
||||
$: fetchLetreroLUR(codigo)
|
||||
|
||||
async function fetchLetreroLUR(codigo) {
|
||||
try {
|
||||
loading = true
|
||||
letrero = await getLetreroLUR(codigo) || {}
|
||||
codigo_dividido = letrero.codigo.match(/\d+|[a-z]+/gi)
|
||||
} catch (error) {
|
||||
console.log({ error })
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<Modal title="Letrero" classBody="bg-white" on:close={() => dispatch('close')}>
|
||||
{#if loading}
|
||||
<IconLoading />
|
||||
{/if}
|
||||
|
||||
{#if !loading && letrero.codigo}
|
||||
<table class="m-auto border border-secondary" cellpadding="10" cellspacing="0"
|
||||
style="--bgcolor1: {letrero.bgcolor1}; --color1: {letrero.color1}; --bgcolor2: {letrero.bgcolor2}; --color2: {letrero.color2}"
|
||||
>
|
||||
<tbody class="color1">
|
||||
<tr>
|
||||
<td rowspan="4" width="80" class="color2 text-center">
|
||||
<h1 class="m-0">{codigo_dividido[0] || ''}</h1>
|
||||
<h1 class="m-0">{codigo_dividido[1] || ''}</h1>
|
||||
</td>
|
||||
<td width="220">{letrero.linea1 || ''}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{letrero.linea2 || ''}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{letrero.linea3 || ''}</td>
|
||||
</tr>
|
||||
<tr class="color2">
|
||||
<td>{letrero.linea4 || ''}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{:else}
|
||||
{#if !loading}
|
||||
<p class="alert alert-warning p-3">Información del Letrero no registrada.</p>
|
||||
{/if}
|
||||
{/if}
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.color1 {
|
||||
background-color: var(--bgcolor1);
|
||||
color: var(--color1);
|
||||
}
|
||||
.color2 {
|
||||
background-color: var(--bgcolor2);
|
||||
color: var(--color2);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,92 @@
|
|||
<script>
|
||||
import Modal from "$/components/Modal.svelte";
|
||||
import { getParadero, getUrlImagen, createParadero, updateParadero, deleteParadero, saveImageParadero } from "$/services/paraderos";
|
||||
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let paradero = { id_paradero: null };
|
||||
let form = {};
|
||||
let inputfile = null;
|
||||
let imagenEl = null;
|
||||
|
||||
$: infoParadero(paradero)
|
||||
|
||||
async function infoParadero({ id_paradero }) {
|
||||
try {
|
||||
if (!id_paradero) return;
|
||||
form = await getParadero(id_paradero)
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function onSaveParadero() {
|
||||
try {
|
||||
form = form.id_paradero ? await updateParadero(form) : await createParadero(form)
|
||||
alert('Información guardada')
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function onDeleteParadero() {
|
||||
try {
|
||||
await deleteParadero(form.id_paradero)
|
||||
alert('Paradero eliminado')
|
||||
dispatch('delete', form)
|
||||
dispatch('close')
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function onChangeImage() {
|
||||
try {
|
||||
const { url_image } = await saveImageParadero(paradero.id_paradero, inputfile.files[0])
|
||||
imagenEl.src = url_image + '?nocache=' + (new Date()).valueOf()
|
||||
alert('Imagen cambiada')
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={onSaveParadero}>
|
||||
<Modal size="lg"
|
||||
title={'Paradero #' + paradero.id_paradero}
|
||||
on:close={() => dispatch('close')}>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Dirección</div>
|
||||
<input type="text" bind:value={form.stop_name} class="form-control">
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Latitud</div>
|
||||
<input type="number" step="any" disabled value={form.stop_lat} class="form-control">
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Longitud</div>
|
||||
<input type="number" step="any" disabled value={form.stop_lon} class="form-control">
|
||||
</div>
|
||||
|
||||
{#if form.id_paradero}
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text"><i class="bi bi-image"></i></div>
|
||||
<div class="form-control"><input type="file" bind:this={inputfile}></div>
|
||||
<button class="btn btn-primary" on:click|preventDefault={onChangeImage}>Cambiar Imagen</button>
|
||||
</div>
|
||||
|
||||
<img alt="Imagen Paradero" class="img-fluid" bind:this={imagenEl} src={getUrlImagen(form.id_paradero)}>
|
||||
{/if}
|
||||
|
||||
<svelte:fragment slot="buttons">
|
||||
<button type="submit" class="btn btn-primary me-2">
|
||||
<i class="bi bi-save"></i> Guardar
|
||||
</button>
|
||||
<button class="btn btn-danger me-2" on:click|preventDefault={onDeleteParadero}>
|
||||
<i class="bi bi-trash"></i> Eliminar
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
</form>
|
|
@ -0,0 +1,120 @@
|
|||
<script>
|
||||
// imagenes
|
||||
import IconParada from "$/assets/parada.png";
|
||||
// libs
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import FormParadero from "./FormParadero.svelte";
|
||||
import FormParaderoDispositivos from "./FormParaderoDispositivos.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;"
|
||||
>
|
||||
<div class="offcanvas-header d-block pb-0">
|
||||
<div class="d-flex justify-content-between">
|
||||
<h5 class="offcanvas-title text-info" id="offcanvasParaderoLabel">
|
||||
<img src={IconParada} alt="Icono de Paradero" width="64" />
|
||||
Paradero #{parada?.id_paradero} {parada?.location}
|
||||
</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
aria-label="Close"
|
||||
on:click|preventDefault={() => dispatch("close")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<nav class="mb-0" style="--bs-nav-tabs-link-active-border-color: var(--bs-dark)">
|
||||
<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)}>
|
||||
Servicios
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="offcanvas-body bg-white py-3">
|
||||
{#if tab === 0}
|
||||
<FormParadero {parada} />
|
||||
{/if}
|
||||
{#if tab === 1}
|
||||
<FormParaderoDispositivos {parada} />
|
||||
{/if}
|
||||
{#if tab === 2}
|
||||
<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;
|
||||
}
|
||||
|
||||
.offcanvas {
|
||||
--bs-offcanvas-width: 500px;
|
||||
--bs-offcanvas-bg: var(--bs-gray-200);
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
--bs-nav-tabs-border-color: var(--bs-white);
|
||||
--bs-nav-tabs-border-width: 0;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
--bs-nav-tabs-link-active-bg: var(--bs-white);
|
||||
--bs-nav-tabs-link-active-color: var(--bs-secondary);
|
||||
--bs-nav-tabs-link-active-border-color: var(--bs-white);
|
||||
}
|
||||
</style>
|
||||
{/if}
|
|
@ -0,0 +1,198 @@
|
|||
<script>
|
||||
// components
|
||||
import PageTitle from "$/components/PageTitle.svelte"
|
||||
import IconLoading from "$/components/IconLoading.svelte"
|
||||
import OffcanvasParadero from "./OffcanvasParadero.svelte"
|
||||
|
||||
// services
|
||||
import { getRegiones } from "$/services/regiones"
|
||||
import { getComunas } from "$/services/comunas"
|
||||
import { getMarcasParaderos } from "$/services/mapas"
|
||||
|
||||
// libs
|
||||
import { onMount } from 'svelte'
|
||||
import { storeParaderos } from "$/stores/global"
|
||||
import imagenParada from '$/assets/parada.png'
|
||||
|
||||
let myMap = null
|
||||
let elMap = null
|
||||
let loading = false
|
||||
let regiones = []
|
||||
let comunas = []
|
||||
let comunas_x_region = []
|
||||
let L = null
|
||||
let iconParada = null
|
||||
let markers = []
|
||||
let form = {}
|
||||
let parada = null
|
||||
|
||||
cargar_regiones_comunas()
|
||||
|
||||
$: myMap && crear_marcadores_por_criterio()
|
||||
|
||||
onMount(() => {
|
||||
if(globalThis.L) create_map();
|
||||
cargar_paraderos_todos($storeParaderos)
|
||||
})
|
||||
|
||||
function create_map() {
|
||||
if (!elMap || !globalThis.L) return;
|
||||
if (!L) L = globalThis.L;
|
||||
if (!iconParada) {
|
||||
iconParada = L.icon({
|
||||
iconUrl: imagenParada,
|
||||
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',
|
||||
{ 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() {
|
||||
try {
|
||||
regiones = await getRegiones()
|
||||
comunas = await getComunas()
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function cargar_paraderos_todos(data_default) {
|
||||
try {
|
||||
loading = true
|
||||
const paraderos = data_default || await getMarcasParaderos()
|
||||
storeParaderos.set(paraderos)
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
function crear_marcadores_por_criterio() {
|
||||
if (form.id_region) {
|
||||
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())
|
||||
|
||||
// 2. filtrar resultado de marcadores
|
||||
const paraderos = $storeParaderos.marks.filter(m => {
|
||||
// filtrar por comunas de la region
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
})
|
||||
|
||||
// 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)
|
||||
|
||||
marker.on('click', function() {
|
||||
parada = { ...mark }
|
||||
})
|
||||
markers.push(marker)
|
||||
}
|
||||
|
||||
// 4. centrar mapa
|
||||
if (myMap && paraderos.length) {
|
||||
const { lat, lng } = paraderos[0].position
|
||||
myMap.setView([ lat, lng ],16)
|
||||
const bounds = myMap.getBounds()
|
||||
paraderos.forEach(el => bounds.extend(el.position))
|
||||
myMap.fitBounds(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function run_search_text() {
|
||||
form.time_search && clearTimeout(form.time_search)
|
||||
form.time_search = setTimeout(() => crear_marcadores_por_criterio(), 1000)
|
||||
}
|
||||
</script>
|
||||
|
||||
<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="row">
|
||||
<div class="col-md">
|
||||
<PageTitle>
|
||||
{#if loading}<IconLoading />{/if}
|
||||
Paraderos
|
||||
</PageTitle>
|
||||
</div>
|
||||
<div class="col-md-auto">
|
||||
<div class="input-group mb-sm-3">
|
||||
<div class="input-group-text">Región</div>
|
||||
<select bind:value={form.id_region} class="form-select" on:change={crear_marcadores_por_criterio}>
|
||||
<option value=""></option>
|
||||
{#each regiones as r}
|
||||
<option value={r.id_region}>{r.nombre_region}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-auto">
|
||||
<div class="input-group mb-sm-3">
|
||||
<div class="input-group-text">Comuna</div>
|
||||
<select bind:value={form.id_comuna} class="form-select" on:change={crear_marcadores_por_criterio}>
|
||||
<option value=""></option>
|
||||
{#each comunas_x_region as c}
|
||||
<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 class="card">
|
||||
<div class="card-body">
|
||||
<div bind:this={elMap} style="height: 100vh;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<OffcanvasParadero {parada} on:close={() => parada = null} />
|
|
@ -0,0 +1,212 @@
|
|||
<script>
|
||||
// componentes
|
||||
import PageTitle from "$/components/PageTitle.svelte";
|
||||
|
||||
import imagenPartida from '$/assets/partida.png'
|
||||
import imagenTermino from '$/assets/termino.png'
|
||||
import imagenParada from '$/assets/parada.png'
|
||||
import imagenAutobus from '$/assets/autobus.png'
|
||||
import { onMount } from "svelte";
|
||||
|
||||
// servicios
|
||||
import { getRutas } from "$/services/mapas";
|
||||
import ModalLetreroLUR from "./ModalLetreroLUR.svelte";
|
||||
import FiltroRutas from "./FiltroRutas.svelte";
|
||||
import { getBusesLinea, getParaderosLinea } from "$/services/lineas";
|
||||
|
||||
let myMap = null
|
||||
let elMap = null
|
||||
|
||||
let loading = false
|
||||
let polyline = null
|
||||
let iconPartida = null
|
||||
let iconTermino = null
|
||||
let iconParada = null
|
||||
let iconAutobus = null
|
||||
let L = null // leaflet.js
|
||||
let marker1 = null
|
||||
let marker2 = null
|
||||
|
||||
let showLetrero = false
|
||||
|
||||
let id_operador = ''
|
||||
let id_linea = ''
|
||||
let codigo = null
|
||||
let ver_buses = false
|
||||
let ver_paraderos = false
|
||||
let markers_paraderos = []
|
||||
let markers_buses = []
|
||||
|
||||
onMount(() => { create_map() })
|
||||
|
||||
function create_map() {
|
||||
if (!elMap || !globalThis.L) return;
|
||||
if (!L) L = globalThis.L;
|
||||
if (!iconPartida) {
|
||||
iconPartida = L.icon({
|
||||
iconUrl: imagenPartida,
|
||||
iconSize: [40, 40],
|
||||
iconAnchor: [20, 40],
|
||||
popupAnchor: [0, -20]
|
||||
})
|
||||
}
|
||||
if (!iconTermino) {
|
||||
iconTermino = L.icon({
|
||||
iconUrl: imagenTermino,
|
||||
iconSize: [40, 40],
|
||||
iconAnchor: [20, 40],
|
||||
popupAnchor: [0, -20]
|
||||
})
|
||||
}
|
||||
if (!iconParada) {
|
||||
iconParada = L.icon({
|
||||
iconUrl: imagenParada,
|
||||
iconSize: [32, 32],
|
||||
iconAnchor: [16, 32],
|
||||
popupAnchor: [0, -16]
|
||||
})
|
||||
}
|
||||
if (!iconAutobus) {
|
||||
iconAutobus = L.icon({
|
||||
iconUrl: imagenAutobus,
|
||||
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',
|
||||
{ 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(
|
||||
({ coords }) => {
|
||||
const { latitude, longitude } = coords;
|
||||
myMap.setView([latitude, longitude], 16);
|
||||
},
|
||||
(error) => console.log({ error })
|
||||
)
|
||||
}
|
||||
|
||||
async function onMostrarRuta(id_operador, id_linea) {
|
||||
try {
|
||||
loading = true
|
||||
polyline && polyline.remove()
|
||||
marker1 && marker1.remove()
|
||||
marker2 && marker2.remove()
|
||||
|
||||
if (!id_operador || !id_linea) return;
|
||||
const data = await getRutas({ id_linea })
|
||||
|
||||
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)
|
||||
marker1.bindTooltip('Inicio')
|
||||
marker2 = L.marker(coordenadas[coordenadas.length -1], { icon: iconTermino }).addTo(myMap)
|
||||
marker2.bindTooltip('Termino')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function onMostrarParaderos(ver_paraderos) {
|
||||
// 1. eliminar marcadores anteriores
|
||||
markers_paraderos.forEach(marker => marker.remove())
|
||||
|
||||
if (!ver_paraderos || !id_linea) return;
|
||||
|
||||
const paraderos = await getParaderosLinea(id_linea)
|
||||
|
||||
// 2. crear marcadores
|
||||
for (let mark of paraderos) {
|
||||
const { stop_lat: lat, stop_lon: lng } = mark
|
||||
const marker = L.marker([lat, lng], { icon: iconParada }).addTo(myMap)
|
||||
const { id_paradero: title, stop_name: location } = mark;
|
||||
const html = `${title}<br>${location}`
|
||||
|
||||
marker.bindTooltip(html)
|
||||
markers_paraderos.push(marker)
|
||||
}
|
||||
}
|
||||
|
||||
async function onMostrarBuses(ver_buses) {
|
||||
// 1. eliminar marcadores anteriores
|
||||
markers_buses.forEach(marker => marker.remove())
|
||||
|
||||
if (!ver_buses || !id_linea) return;
|
||||
|
||||
const buses = await getBusesLinea(id_linea)
|
||||
console.log({ buses })
|
||||
|
||||
// 2. crear marcadores
|
||||
for (let mark of buses) {
|
||||
const { latitude: lat, longitude: lng } = mark
|
||||
const marker = L.marker([lat, lng], { icon: iconAutobus }).addTo(myMap)
|
||||
const html = `${mark.Patente_vehiculo}<br>Velocidad: ${mark.speed}`
|
||||
|
||||
marker.bindTooltip(html)
|
||||
markers_buses.push(marker)
|
||||
console.log({ marker })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$: onMostrarRuta(id_operador, id_linea)
|
||||
$: onMostrarParaderos(ver_paraderos)
|
||||
$: onMostrarBuses(ver_buses)
|
||||
</script>
|
||||
|
||||
<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="row">
|
||||
<div class="col-md-auto">
|
||||
<PageTitle {loading}>
|
||||
Rutas
|
||||
</PageTitle>
|
||||
</div>
|
||||
|
||||
<div class="col-md">
|
||||
<FiltroRutas
|
||||
bind:id_operador={id_operador}
|
||||
bind:id_linea={id_linea}
|
||||
bind:codigo={codigo}
|
||||
bind:ver_buses={ver_buses}
|
||||
bind:ver_paraderos={ver_paraderos}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if id_operador && codigo}
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-secondary" on:click|preventDefault={() => showLetrero = true}>
|
||||
Ver Letrero
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div bind:this={elMap} style="height: 100vh;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if showLetrero}
|
||||
<ModalLetreroLUR {codigo} on:close={() => showLetrero = false} />
|
||||
{/if}
|
|
@ -0,0 +1,23 @@
|
|||
.table-paraderos td {
|
||||
min-width: 150px;
|
||||
--margin-input: -.75rem;
|
||||
}
|
||||
.table-paraderos td:focus {
|
||||
background: #80ffa6;
|
||||
}
|
||||
|
||||
.table-paraderos td:has(input) {
|
||||
padding: 0;
|
||||
--margin-input: 0;
|
||||
}
|
||||
|
||||
.table-paraderos td > input {
|
||||
border: none;
|
||||
margin: var(--margin-input) 0;
|
||||
background-color: transparent;
|
||||
padding: .75rem;
|
||||
}
|
||||
|
||||
.table-paraderos td > input:focus {
|
||||
background-color: #80ffa6;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<script>
|
||||
import "./Home.css"
|
||||
import TablaEditaParada from "./TablaEditaParada.svelte";
|
||||
</script>
|
||||
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<button class="btn btn-primary"><i class="bi bi-save"></i> Guardar</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<TablaEditaParada parada={null} />
|
||||
</div>
|
||||
<div class="card-footer"></div>
|
||||
</div>
|
||||
|
||||
<datalist id="ex1">
|
||||
<option value="prueba1"></option>
|
||||
<option value="prueba2"></option>
|
||||
<option value="prueba3"></option>
|
||||
</datalist>
|
|
@ -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>
|
|
@ -0,0 +1,66 @@
|
|||
<script>
|
||||
import { Link } from 'svelte-navigator'
|
||||
import Paginate from '$/components/Paginate.svelte'
|
||||
import { getPersonas } from '$/services/personas';
|
||||
import PageTitle from '$/components/PageTitle.svelte';
|
||||
let page = 1;
|
||||
let limit = 15;
|
||||
let personas = { results: [], count: 0 };
|
||||
let loading = false;
|
||||
|
||||
$: onPage(page)
|
||||
|
||||
async function onPage(page) {
|
||||
try {
|
||||
loading = true
|
||||
const offset = (page - 1) * limit;
|
||||
personas = await getPersonas({ offset, limit })
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<PageTitle {loading}>Personas</PageTitle>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<Link to='/personas/nuevo' class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg"></i> Nuevo
|
||||
</Link>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>RUT</th>
|
||||
<th>Nombres</th>
|
||||
<th>Apellido 1</th>
|
||||
<th>Apellido 2</th>
|
||||
<th>Teléfono</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each personas.results as p}
|
||||
<tr>
|
||||
<td><Link to={'/personas/' + p.rut}>{p.rut}-{p.dv}</Link></td>
|
||||
<td>{p.nombres}</td>
|
||||
<td>{p.apellido_a}</td>
|
||||
<td>{p.apellido_b}</td>
|
||||
<td>{p.fono}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<Paginate
|
||||
offset={(page - 1) / limit}
|
||||
{limit}
|
||||
count={personas.count}
|
||||
on:page={ev => page = ev.detail}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,84 @@
|
|||
<script>
|
||||
import { getTiposPersona } from "$/services/tipos_persona";
|
||||
|
||||
export let form = {}
|
||||
export let es_nuevo = true
|
||||
|
||||
let tipos_tratamiento = [];
|
||||
let rut = '';
|
||||
|
||||
$: begin(es_nuevo)
|
||||
$: !es_nuevo && (rut = `${form.rut || ''}-${form.dv || ''}`);
|
||||
|
||||
async function begin(es_nuevo) {
|
||||
try {
|
||||
tipos_tratamiento = await getTiposPersona()
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
}
|
||||
}
|
||||
|
||||
function onChangeRut({ target: { value } }) {
|
||||
const [ rut = null, dv = null ] = value.split('-')
|
||||
form = { ...form, rut, dv }
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md mb-3">
|
||||
RUT
|
||||
<input type="text" value={rut} class="form-control" required disabled={!es_nuevo} on:input={onChangeRut}>
|
||||
</div>
|
||||
<div class="col-md mb-3">
|
||||
Tipo Tratamiento
|
||||
<select bind:value={form.id_tipo_tratamiento} class="form-select">
|
||||
<option value=""></option>
|
||||
{#each tipos_tratamiento as tipo}
|
||||
<option value={tipo.id_tipo_tratamiento}>{tipo.tratamiento}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md mb-3">
|
||||
Nombres
|
||||
<input type="text" bind:value={form.nombres} class="form-control" required>
|
||||
</div>
|
||||
<div class="col-md mb-3">
|
||||
Apellido 1
|
||||
<input type="text" bind:value={form.apellido_a} class="form-control" required>
|
||||
</div>
|
||||
<div class="col-md mb-3">
|
||||
Apellido 2
|
||||
<input type="text" bind:value={form.apellido_b} class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md mb-3">
|
||||
Teléfono
|
||||
<input type="tel" bind:value={form.fono} class="form-control">
|
||||
</div>
|
||||
<div class="col-md mb-3">
|
||||
Email
|
||||
<input type="email" bind:value={form.email} class="form-control" required>
|
||||
</div>
|
||||
<div class="col-md mb-3">
|
||||
Fecha de nacimiento
|
||||
<input type="date" bind:value={form.fecha_nacimiento} class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md mb-3">
|
||||
Comuna
|
||||
<select bind:value={form.id_comuna} class="form-select">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md mb-3">
|
||||
Dirección
|
||||
<input type="text" bind:value={form.direccion} class="form-control">
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,67 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { storeLayout } from '$/stores/global'
|
||||
import FormPersona from './Form.svelte'
|
||||
import { Link, useParams, useNavigate } from 'svelte-navigator';
|
||||
import { getPersona, createPersona, updatePersona } from '$/services/personas';
|
||||
import PageTitle from '../../components/PageTitle.svelte';
|
||||
|
||||
onMount(() => {
|
||||
$storeLayout.showSidebar = false;
|
||||
return () => {
|
||||
$storeLayout.showSidebar = true;
|
||||
}
|
||||
})
|
||||
|
||||
const params = useParams()
|
||||
const navigate = useNavigate()
|
||||
let es_nuevo = true;
|
||||
let form = {}
|
||||
let loading = false;
|
||||
|
||||
$: es_nuevo = !$params.rut
|
||||
$: begin($params)
|
||||
|
||||
async function begin({ rut = null }) {
|
||||
try {
|
||||
if (!rut) return; // si es nuevo, no busco informacion
|
||||
loading = true
|
||||
form = await getPersona(rut)
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
try {
|
||||
if (es_nuevo) {
|
||||
// para crear un usuario, es requerido enviar los datos de la persona ademas.
|
||||
await createPersona(form)
|
||||
} else {
|
||||
await updatePersona(form)
|
||||
}
|
||||
alert('Datos guardados con exito!')
|
||||
navigate('/personas')
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<PageTitle {loading}>{es_nuevo ? 'Nuevo Persona': 'Editar Persona'}</PageTitle>
|
||||
|
||||
<form action="" on:submit|preventDefault={onSave}>
|
||||
<div class="card">
|
||||
<div class="card-header d-flex">
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-save"></i> Guardar</button>
|
||||
<div class="m-auto"></div>
|
||||
<Link to="/personas" class="btn btn-outline-secondary">Volver</Link>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h4 class="h4 mb-3">Datos de la persona</h4>
|
||||
<FormPersona bind:form={form} {es_nuevo} />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -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>
|
|
@ -0,0 +1,168 @@
|
|||
<script>
|
||||
export let form = {};
|
||||
let codigo_dividido = "";
|
||||
|
||||
$: codigo_dividido = form.route_short_name
|
||||
? form.route_short_name.match(/\d+|[a-z]+/gi)
|
||||
: ["", ""];
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">ID</div>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
value={form.id_linea}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Operador</div>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
bind:value={form.id_operador}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Código LUR</div>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
bind:value={form.route_short_name}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Dirección</div>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
bind:value={form.route_long_name}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Color Fondo 1</div>
|
||||
<input type="text" class="form-control" bind:value={form.bgcolor1} required>
|
||||
<div class="form-control">
|
||||
<input
|
||||
type="color"
|
||||
class="w-100"
|
||||
bind:value={form.bgcolor1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Color Texto 1</div>
|
||||
<input type="text" class="form-control" bind:value={form.color1} required>
|
||||
<div class="form-control">
|
||||
<input
|
||||
type="color"
|
||||
class="w-100"
|
||||
bind:value={form.color1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Color Fondo 2</div>
|
||||
<input type="text" class="form-control" bind:value={form.bgcolor2} required>
|
||||
<div class="form-control">
|
||||
<input
|
||||
type="color"
|
||||
class="w-100"
|
||||
bind:value={form.bgcolor2}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">Color Texto 2</div>
|
||||
<input type="text" class="form-control" bind:value={form.color2} required>
|
||||
<div class="form-control">
|
||||
<input
|
||||
type="color"
|
||||
class="w-100"
|
||||
bind:value={form.color2}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md">
|
||||
<table
|
||||
class="m-auto border border-secondary"
|
||||
cellpadding="10"
|
||||
cellspacing="0"
|
||||
style="--bgcolor1: {form.bgcolor1}; --color1: {form.color1}; --bgcolor2: {form.bgcolor2}; --color2: {form.color2}"
|
||||
>
|
||||
<tbody class="color1">
|
||||
<tr>
|
||||
<td rowspan="4" width="80" class="color2 text-center">
|
||||
<h1 class="m-0">{codigo_dividido[0]}</h1>
|
||||
<h1 class="m-0">{codigo_dividido[1]}</h1>
|
||||
</td>
|
||||
<td
|
||||
><input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Linea 1"
|
||||
bind:value={form.linea1}
|
||||
required
|
||||
/></td
|
||||
>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
><input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Linea 2"
|
||||
bind:value={form.linea2}
|
||||
required
|
||||
/></td
|
||||
>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
><input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Linea 3"
|
||||
bind:value={form.linea3}
|
||||
required
|
||||
/></td
|
||||
>
|
||||
</tr>
|
||||
<tr class="color2">
|
||||
<td
|
||||
><input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Linea 4"
|
||||
bind:value={form.linea4}
|
||||
required
|
||||
/></td
|
||||
>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.color1 {
|
||||
background-color: var(--bgcolor1);
|
||||
color: var(--color1);
|
||||
}
|
||||
.color2 {
|
||||
background-color: var(--bgcolor2);
|
||||
color: var(--color2);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,32 @@
|
|||
<script>
|
||||
import PageTitle from "../../components/PageTitle.svelte";
|
||||
import { getLetreroLUR } from "../../services/letreros_lur";
|
||||
import { getLinea } from "../../services/lineas";
|
||||
import ModalFormRuta from "./ModalFormRuta.svelte";
|
||||
import TableRutas from "./TableRutas.svelte";
|
||||
let loading = false
|
||||
|
||||
let ruta = null
|
||||
|
||||
async function onEdita({ linea: { id_linea } }) {
|
||||
try {
|
||||
loading = true
|
||||
const linea = await getLinea(id_linea)
|
||||
const letrero = await getLetreroLUR(linea.route_short_name).catch(() => Promise.resolve({}))
|
||||
ruta = { ...linea, ...letrero }
|
||||
} catch (error) {
|
||||
console.log({ error })
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<PageTitle {loading}>Rutas de Buses</PageTitle>
|
||||
|
||||
<TableRutas on:loading={ev => loading = ev.detail} on:select={ev => onEdita(ev.detail)} />
|
||||
|
||||
{#if ruta}
|
||||
<ModalFormRuta {ruta} on:close={() => ruta = null} />
|
||||
{/if}
|
|
@ -0,0 +1,85 @@
|
|||
<script>
|
||||
import IconLoading from "../../components/IconLoading.svelte";
|
||||
import Modal from "../../components/Modal.svelte";
|
||||
import { createLetreroLUR, deleteLetreroLUR } from "../../services/letreros_lur";
|
||||
import { createLinea, deleteLinea, updateLinea } from "../../services/lineas";
|
||||
import FormRuta from "./FormRuta.svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let ruta = null;
|
||||
let form = {}
|
||||
let error_messages = null;
|
||||
let loading = false
|
||||
|
||||
$: form = { ...ruta }
|
||||
|
||||
async function onSave() {
|
||||
try {
|
||||
loading = true
|
||||
error_messages = null
|
||||
const { id_linea } = ruta;
|
||||
const { route_short_name, route_long_name,
|
||||
linea1, linea2, linea3, linea4,
|
||||
bgcolor1, color1, bgcolor2, color2
|
||||
} = form;
|
||||
if (id_linea) {
|
||||
await updateLinea({ id_linea, route_short_name, route_long_name })
|
||||
} else {
|
||||
await createLinea({ route_short_name, route_long_name })
|
||||
}
|
||||
|
||||
await deleteLetreroLUR(route_short_name).catch(() => {})
|
||||
await createLetreroLUR({
|
||||
codigo: route_short_name,
|
||||
linea1, linea2, linea3, linea4,
|
||||
bgcolor1, color1, bgcolor2, color2
|
||||
})
|
||||
dispatch('close')
|
||||
} catch (error) {
|
||||
const data = JSON.parse(error) || null
|
||||
if (data) error_messages = data;
|
||||
console.log({ data })
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
// async function onDelete() {
|
||||
// try {
|
||||
// const { id_linea, route_short_name } = ruta;
|
||||
// await deleteLetreroLUR(route_short_name)
|
||||
// await deleteLinea(id_linea)
|
||||
// dispatch('close')
|
||||
// } catch (error) {
|
||||
// console.log({ error })
|
||||
// }
|
||||
// }
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={onSave}>
|
||||
<Modal title="Ruta de Servicio" size="xl"
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<FormRuta {form} />
|
||||
|
||||
{#if error_messages}
|
||||
<div class="alert alert-danger">
|
||||
<ul>
|
||||
{#each Object.entries(error_messages) as [field, message]}
|
||||
<li>{field}: {message[0]}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<svelte:fragment slot="buttons">
|
||||
<button class="btn btn-primary" type="submit" disabled={loading}><i class="bi bi-save"></i> Guardar</button>
|
||||
<!-- {#if ruta.id_linea}
|
||||
<button class="btn btn-danger" on:click|preventDefault={onDelete}><i class="bi bi-trash"></i> Eliminar</button>
|
||||
{/if} -->
|
||||
|
||||
{#if loading}<IconLoading />{/if}
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
</form>
|
|
@ -0,0 +1,19 @@
|
|||
<script>
|
||||
import Modal from "../../components/Modal.svelte";
|
||||
import TableRutas from "./TableRutas.svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let show = true;
|
||||
|
||||
function onSelect(item) {
|
||||
dispatch('select', item)
|
||||
dispatch('close')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class={show ? '' : 'd-none'}>
|
||||
<Modal title="Rutas" size="xl" on:close={() => dispatch('close')}>
|
||||
<TableRutas on:select={ev => onSelect(ev.detail)} />
|
||||
</Modal>
|
||||
</div>
|
|
@ -0,0 +1,102 @@
|
|||
<script>
|
||||
import Paginate from "../../components/Paginate.svelte";
|
||||
import TableResponsive from "../../components/TableResponsive.svelte";
|
||||
import { getLineas } from "../../services/lineas";
|
||||
import { getOperador, getOperadores } from "../../services/operadores";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let loading = false;
|
||||
let offset = 0;
|
||||
const limit = 10;
|
||||
let lineas = { results: [], count: 0 };
|
||||
let operadores = [];
|
||||
let filtro = {}
|
||||
|
||||
async function fetchOperadores() {
|
||||
try {
|
||||
operadores = await getOperadores({ ordering: 'nombre_operador' })
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchData(offset, { id_operador = null }) {
|
||||
try {
|
||||
loading = true;
|
||||
const conditions = { offset, limit, ordering: 'route_short_name' };
|
||||
if (id_operador) conditions.id_operador = id_operador;
|
||||
lineas = await getLineas(conditions)
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
function getNombreOperador(id) {
|
||||
const operador = operadores.find(el => el.id_operador === id) || {}
|
||||
return operador.nombre_operador || ''
|
||||
}
|
||||
|
||||
function onSelect(linea) {
|
||||
const operador = operadores.find(el => el.id_operador === linea.id_operador) || {}
|
||||
const data = { linea, operador }
|
||||
dispatch('select', data)
|
||||
}
|
||||
|
||||
fetchOperadores()
|
||||
$: fetchData(offset, filtro)
|
||||
$: dispatch('loading',loading)
|
||||
</script>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<slot name="buttons" />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text">Operador</div>
|
||||
<select class="form-select" bind:value={filtro.id_operador}>
|
||||
<option value=""></option>
|
||||
{#each operadores as operador}
|
||||
<option value={operador.id_operador}>{operador.nombre_operador}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<TableResponsive>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Operador</th>
|
||||
<th>LUR</th>
|
||||
<th>Dirección</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each lineas.results as linea}
|
||||
<tr>
|
||||
<td><a href={"#"} on:click|preventDefault={() => onSelect(linea)}>{linea.id_linea}</a></td>
|
||||
<td>{getNombreOperador(linea.id_operador)}</td>
|
||||
<td>{linea.route_short_name}</td>
|
||||
<td>{linea.route_long_name}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</TableResponsive>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<Paginate
|
||||
forcePage={offset / limit}
|
||||
count={lineas.count}
|
||||
{limit}
|
||||
on:page={ev => offset = (ev.detail - 1) * limit}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
NO EXISTE
|
|
@ -0,0 +1,16 @@
|
|||
<script>
|
||||
import PageTitle from "$/components/PageTitle.svelte";
|
||||
</script>
|
||||
<PageTitle>Inicio</PageTitle>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Empty card</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,73 @@
|
|||
<script>
|
||||
import { Link } from 'svelte-navigator'
|
||||
import { createToken } from '$/services/login'
|
||||
|
||||
let form = { username: '', pass: '' }
|
||||
let message_error = '';
|
||||
let loading = false;
|
||||
|
||||
// validar usuario contraseña
|
||||
async function onIngresar() {
|
||||
try {
|
||||
loading = true;
|
||||
message_error = ''
|
||||
const { token } = await createToken(form)
|
||||
sessionStorage.setItem('token', token)
|
||||
document.location.reload();
|
||||
} catch (error) {
|
||||
message_error = error.message || error
|
||||
setTimeout(() => message_error = '', 3000)
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container p-4 bg-neutral box-login">
|
||||
<div style="margin-top: 100px;"></div>
|
||||
<div class="card mb-4 m-auto">
|
||||
<div class="card-header bg-dark text-center p-3">
|
||||
<h2 class="text-light">Acceso</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form on:submit|preventDefault={onIngresar}>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for={null}>Cuenta</label>
|
||||
<input class="form-control form-control-lg" type="text" bind:value={form.username} required
|
||||
placeholder="Ingrese su cuenta de usuario">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for={null}>Contraseña</label>
|
||||
<input class="form-control form-control-lg" type="password" bind:value={form.password} required
|
||||
placeholder="Ingrese su contraseña">
|
||||
</div>
|
||||
|
||||
{#if message_error}
|
||||
<div class="mb-3 text-danger">{message_error}</div>
|
||||
{/if}
|
||||
|
||||
<Link to="/recovery">Olvido su contraseña?</Link>
|
||||
|
||||
<div class="text-center mt-3">
|
||||
<button type="submit" class="btn btn-dark" disabled={loading}>
|
||||
<span class="fa fa-key mr-3"></span>
|
||||
Ingresar al sistema
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.box-login {
|
||||
height: 100vh;
|
||||
max-width: inherit;
|
||||
}
|
||||
|
||||
.box-login .card {
|
||||
width: 400px;
|
||||
max-width: 100%;
|
||||
min-width: 200px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1 @@
|
|||
En desarrollo
|
|
@ -0,0 +1 @@
|
|||
En desarrollo
|
|
@ -0,0 +1,82 @@
|
|||
<script>
|
||||
import { Link } from 'svelte-navigator'
|
||||
import Paginate from '$/components/Paginate.svelte'
|
||||
import PageTitle from '$/components/PageTitle.svelte'
|
||||
import { getUsuarios } from '$/services/usuarios'
|
||||
|
||||
let usuarios = { count: 0, results: [] }
|
||||
let page = 1
|
||||
const limit = 15
|
||||
let loading = false
|
||||
|
||||
$: onPage(page)
|
||||
|
||||
async function onPage(page) {
|
||||
try {
|
||||
loading = true
|
||||
const offset = (page - 1) * limit;
|
||||
usuarios = await getUsuarios({ offset, limit, ordering: 'login' })
|
||||
} catch(error) {
|
||||
alert(error.detail || error)
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<PageTitle {loading}>Usuarios</PageTitle>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="mb-3 d-flex">
|
||||
<Link to='/usuarios/nuevo' class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg"></i> Nuevo
|
||||
</Link>
|
||||
<div class="m-auto"></div>
|
||||
<Link to='/' class="btn btn-outline-secondary">Volver</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr class="table-light">
|
||||
<th>Login</th>
|
||||
<th>Nombres</th>
|
||||
<th>Apellido 1</th>
|
||||
<th>Apellido 2</th>
|
||||
<th>Email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each usuarios.results as row}
|
||||
<tr>
|
||||
<td><Link to={"/usuarios/" + row.login}>{row.login}</Link></td>
|
||||
<td>{row.persona.nombres}</td>
|
||||
<td>{row.persona.apellido_a}</td>
|
||||
<td>{row.persona.apellido_b}</td>
|
||||
<td>{row.persona.email}</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={(page - 1) * limit}
|
||||
{limit}
|
||||
count={usuarios.count}
|
||||
on:page={ev => page = ev.detail}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.table-responsive {
|
||||
max-height: calc(100vh - 300px);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,29 @@
|
|||
<script>
|
||||
export let form = {}
|
||||
export let es_nuevo = true
|
||||
export let clave2 = ''
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md mb-3">
|
||||
Login
|
||||
<input type="text" bind:value={form.login} class="form-control" required disabled={!es_nuevo}>
|
||||
</div>
|
||||
<div class="col-md mb-3 pt-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" bind:checked={form.vigente} id="usuario-vigente">
|
||||
<label class="form-check-label" for="usuario-vigente">Vigente</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md mb-3">
|
||||
Contraseña
|
||||
<input type="password" bind:value={form.clave} class="form-control" required={es_nuevo || !!clave2}>
|
||||
</div>
|
||||
<div class="col-md mb-3">
|
||||
Repita Contraseña
|
||||
<input type="password" bind:value={clave2} class="form-control" required={es_nuevo || !!form.clave}>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,65 @@
|
|||
<script>
|
||||
import { storeSession } from "$/stores/global";
|
||||
import PageTitle from "$/components/PageTitle.svelte";
|
||||
let persona = {}
|
||||
let avatar = null;
|
||||
$: persona = $storeSession.persona || {};
|
||||
$: avatar = $storeSession?.avatar_img || '/avatars/avatar2.png';
|
||||
</script>
|
||||
|
||||
<PageTitle>Perfil de usuario</PageTitle>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<img class="img-fluid" width="100%" alt="" src={avatar} />
|
||||
<section class="section">
|
||||
<div class="section-body">
|
||||
<p>Twitter del perfil: <br />
|
||||
@twitter
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col">
|
||||
<section class="section">
|
||||
<div class="section-header with-profile-charge">
|
||||
<h4 class="pb-2 border-bottom border-accent">{persona.nombres}</h4>
|
||||
<div class="section-icons">
|
||||
<a class="link-icon" href={"#"}><i class="cl cl-facebook"></i></a>
|
||||
<a class="link-icon" href={"#"}><i class="cl cl-twitter"></i></a>
|
||||
</div>
|
||||
<div class="profile-charge">{persona.apellido_a} {persona.apellido_b}</div>
|
||||
</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>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<section class="section">
|
||||
<div class="section-header">
|
||||
<h6 class="pb-2 border-bottom border-accent">Información de contacto:</h6>
|
||||
</div>
|
||||
<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>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { storeLayout } from '$/stores/global'
|
||||
import PageTitle from '$/components/PageTitle.svelte';
|
||||
import FormPersona from '../personas/Form.svelte'
|
||||
import FormUsuario from './Form.svelte'
|
||||
import { Link, useParams, useNavigate } from 'svelte-navigator';
|
||||
import { getUsuario, createUsuario, updateUsuario, deleteUsuario } from '$/services/usuarios';
|
||||
import { updatePersona } from '$/services/personas';
|
||||
|
||||
onMount(() => {
|
||||
$storeLayout.showSidebar = false;
|
||||
return () => {
|
||||
$storeLayout.showSidebar = true;
|
||||
}
|
||||
})
|
||||
|
||||
const params = useParams()
|
||||
const navigate = useNavigate()
|
||||
let es_nuevo = true;
|
||||
let form_persona = {}
|
||||
let form_usuario = {}
|
||||
let clave2 = ''
|
||||
let loading = false;
|
||||
|
||||
$: es_nuevo = !$params.login
|
||||
$: begin($params)
|
||||
|
||||
async function begin({ login = null }) {
|
||||
try {
|
||||
if (!login) return; // si es nuevo, no busco informacion
|
||||
loading = true
|
||||
const { persona, ...data } = await getUsuario(login)
|
||||
form_persona = persona;
|
||||
form_usuario = data;
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
alert(error.detail || error)
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
try {
|
||||
if (form_usuario.clave && form_usuario.clave !== clave2) {
|
||||
return alert('Segunda contraseña debe coincidir')
|
||||
}
|
||||
if (es_nuevo) {
|
||||
// para crear un usuario, es requerido enviar los datos de la persona ademas.
|
||||
await createUsuario({ ...form_persona, ...form_usuario})
|
||||
} else {
|
||||
await updateUsuario(form_usuario)
|
||||
await updatePersona(form_persona)
|
||||
}
|
||||
alert('Datos guardados con exito!')
|
||||
navigate('/usuarios')
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
}
|
||||
}
|
||||
|
||||
async function onDelete() {
|
||||
try {
|
||||
if (!confirm(`Desea eliminar a ${form_usuario.login}?`)) return;
|
||||
await deleteUsuario($params.login)
|
||||
alert('Registro eliminado con exito!')
|
||||
navigate('/usuarios')
|
||||
} catch (error) {
|
||||
alert(error.detail || error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<PageTitle {loading}>{es_nuevo ? 'Nuevo Usuario': 'Editar Usuario'}</PageTitle>
|
||||
|
||||
<form action="" on:submit|preventDefault={onSave}>
|
||||
<div class="card">
|
||||
<div class="card-header d-flex">
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-save"></i> Guardar</button>
|
||||
{#if !es_nuevo}
|
||||
<button type="button" class="btn btn-danger ms-2"
|
||||
on:click|preventDefault={onDelete}>
|
||||
<i class="bi bi-trash"></i> Eliminar
|
||||
</button>
|
||||
{/if}
|
||||
<div class="m-auto"></div>
|
||||
<Link to="/usuarios" class="btn btn-outline-secondary">Volver</Link>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h4 class="h4 mb-3">Datos de la persona</h4>
|
||||
<FormPersona bind:form={form_persona} {es_nuevo} />
|
||||
<hr>
|
||||
<h4 class="h4 my-3">Datos del usuario</h4>
|
||||
<FormUsuario bind:form={form_usuario} bind:clave2={clave2} {es_nuevo} />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,10 @@
|
|||
import PageLogin from '$/pages/site/Login.svelte'
|
||||
import PageRecovery from '$/pages/site/Recovery.svelte'
|
||||
import PagePassword from '$/pages/site/Password.svelte'
|
||||
|
||||
export const routes = [
|
||||
{ path: '/', component: PageLogin },
|
||||
{ path: '/recovery', component: PageRecovery },
|
||||
{ path: '/new-password', component: PagePassword },
|
||||
{ path: '*', component: PageLogin },
|
||||
]
|
|
@ -0,0 +1,37 @@
|
|||
import PageHome from '$/pages/site/Home.svelte'
|
||||
import PagePerfil from '$/pages/usuarios/Perfil.svelte'
|
||||
import PageError from '$/pages/site/Error.svelte'
|
||||
import PageAplicaciones from '$/pages/aplicaciones/Admin.svelte'
|
||||
import PageUsuarios from '$/pages/usuarios/Admin.svelte'
|
||||
import PageUsuarioCreate from '$/pages/usuarios/Usuario.svelte'
|
||||
import PageUsuarioModifica from '$/pages/usuarios/Usuario.svelte'
|
||||
import PageComunas from '$/pages/comunas/Admin.svelte'
|
||||
import PagePersonas from '$/pages/personas/Admin.svelte'
|
||||
import PagePersonaCreate from '$/pages/personas/Persona.svelte'
|
||||
import PagePersonaModifica from '$/pages/personas/Persona.svelte'
|
||||
import PageMapaParaderos from '$/pages/mapas/Paraderos.svelte'
|
||||
import PageMapaRutas from '$/pages/mapas/Rutas.svelte'
|
||||
import PageRoles from '$/pages/roles/Admin.svelte'
|
||||
import PageRolesyAplicaciones from '$/pages/rolesaplicaciones/Admin.svelte'
|
||||
import PageParaderos from '$/pages/paraderos/Home.svelte'
|
||||
import PageRutas from "$/pages/rutas/Home.svelte";
|
||||
|
||||
export const routes = [
|
||||
{ path: '/', component: PageHome },
|
||||
{ path: '/perfil', component: PagePerfil },
|
||||
{ path: '/aplicaciones', component: PageAplicaciones },
|
||||
{ path: '/usuarios', component: PageUsuarios },
|
||||
{ path: '/usuarios/nuevo', component: PageUsuarioCreate },
|
||||
{ path: '/usuarios/:login', component: PageUsuarioModifica },
|
||||
{ path: '/roles', component: PageRoles },
|
||||
{ path: '/rolesaplicaciones', component: PageRolesyAplicaciones },
|
||||
{ path: '/comunas', component: PageComunas },
|
||||
{ path: '/personas', component: PagePersonas },
|
||||
{ path: '/personas/nuevo', component: PagePersonaCreate },
|
||||
{ path: '/personas/:rut', component: PagePersonaModifica },
|
||||
{ path: '/mapas/paraderos', component: PageMapaParaderos },
|
||||
{ path: '/mapas/rutas', component: PageMapaRutas },
|
||||
{ path: '/paraderos', component: PageParaderos },
|
||||
{ path: '/rutas', component: PageRutas },
|
||||
{ path: '*', component: PageError },
|
||||
]
|
|
@ -0,0 +1,3 @@
|
|||
export const url_base = document.location.origin;
|
||||
export const base = import.meta.env.VITE_BACKEND || '/api';
|
||||
export const getToken = () => sessionStorage.getItem('token') || null;
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getAplicaciones(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/aplicaciones/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.json()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getAplicacion(id) {
|
||||
const res = await fetch(`${base}/aplicaciones/${id}/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.json()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function createAplicacion(data) {
|
||||
const res = await fetch(`${base}/aplicaciones/`, {
|
||||
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 updateAplicacion({ id_aplicacion: id, ...data }) {
|
||||
const res = await fetch(`${base}/aplicaciones/${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 deleteAplicacion(id) {
|
||||
const res = await fetch(`${base}/aplicaciones/${id}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.json()
|
||||
return res.text()
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getComunas(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/comunas/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getComuna(id) {
|
||||
const res = await fetch(`${base}/comunas/${id}/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function createComuna(data) {
|
||||
const res = await fetch(`${base}/comunas/`, {
|
||||
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 updateComuna({ id_comuna: id, ...data }) {
|
||||
const res = await fetch(`${base}/comunas/${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 deleteComuna(id) {
|
||||
const res = await fetch(`${base}/comunas/${id}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.text()
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getDispositivos(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/dispositivos/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
|
||||
export async function createDispositivo(data) {
|
||||
const res = await fetch(`${base}/dispositivos/`, {
|
||||
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 updateDispositivo({ id_dispositivo: id, ...data }) {
|
||||
const res = await fetch(`${base}/dispositivos/${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 deleteDispositivo(id) {
|
||||
const res = await fetch(`${base}/dispositivos/${id}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.text()
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getLetrerosLUR(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/letreros-lur/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getLetreroLUR(id) {
|
||||
const res = await fetch(`${base}/letreros-lur/${id}/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function createLetreroLUR(data) {
|
||||
const res = await fetch(`${base}/letreros-lur/`, {
|
||||
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 updateLetreroLUR({ codigo: id = null, ...data }) {
|
||||
const res = await fetch(`${base}/letreros-lur/${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 deleteLetreroLUR(id) {
|
||||
const res = await fetch(`${base}/letreros-lur/${id}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.text()
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getLineas(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/lineas/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getLinea(id) {
|
||||
const res = await fetch(`${base}/lineas/${id}/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function createLinea(data) {
|
||||
const res = await fetch(`${base}/lineas/`, {
|
||||
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 updateLinea({ id_linea: id = null, ...data }) {
|
||||
const res = await fetch(`${base}/lineas/${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 deleteLinea(id) {
|
||||
const res = await fetch(`${base}/lineas/${id}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getParaderosLinea(id_linea) {
|
||||
const res = await fetch(`${base}/lineas/paraderos?id_linea=${id_linea}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getBusesLinea(id_linea) {
|
||||
const res = await fetch(`${base}/lineas/buses?id_linea=${id_linea}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { base, getToken } from "./_config";
|
||||
|
||||
export async function createToken({ username, password }) {
|
||||
const res = await fetch(`${base}/auth/`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ username, password }),
|
||||
headers: { "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getInfoToken() {
|
||||
const res = await fetch(`${base}/auth/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getMarcasParaderos(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/mapas/paraderos/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getRutas(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/mapas/rutas/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import { base, getToken } from "./_config";
|
||||
|
||||
export async function deleteNotificacion(id) {
|
||||
//
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getOperadores(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/operadores/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getOperador(id) {
|
||||
const res = await fetch(`${base}/operadores/${id}/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function createOperador(data) {
|
||||
const res = await fetch(`${base}/operadores/`, {
|
||||
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 updateOperador({ id_operador: id = null, ...data }) {
|
||||
const res = await fetch(`${base}/operadores/${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 deleteOperador(id) {
|
||||
const res = await fetch(`${base}/operadores/${id}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export function getUrlImagen(id_paradero) {
|
||||
return `${base}/paraderos-image/${id_paradero}`
|
||||
}
|
||||
|
||||
export async function getParaderos(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/paraderos/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getParadero(rut) {
|
||||
const res = await fetch(`${base}/paraderos/${rut}/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function createParadero(data) {
|
||||
const res = await fetch(`${base}/paraderos/`, {
|
||||
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 updateParadero({ id_paradero: id = null, ...data }) {
|
||||
const res = await fetch(`${base}/paraderos/${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 deleteParadero(id) {
|
||||
const res = await fetch(`${base}/paraderos/${id}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getParaderoImagenes(id_paradero) {
|
||||
const res = await fetch(`${base}/paraderos-image/?id_paradero=${id_paradero}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
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',
|
||||
body: form,
|
||||
headers: { "Authorization": `Bearer ${getToken()}` }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
|
||||
export async function getLineasParadero(id_paradero) {
|
||||
const res = await fetch(`${base}/lineas-paradero/?id_paradero=${id_paradero}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}` }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
|
||||
export async function createLineaParadero(id_paradero, id_linea) {
|
||||
const res = await fetch(`${base}/lineas-paradero/`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ id_paradero, id_linea }),
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function deleteLineaParadero(id_paradero_linea) {
|
||||
const res = await fetch(`${base}/lineas-paradero/${id_paradero_linea}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}` }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.text()
|
||||
}
|
||||
|
||||
|
||||
export async function getInfoPublic(id_paradero) {
|
||||
const res = await fetch(`${base}/paraderos/info-public/${id_paradero}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}` }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getPersonas(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/personas/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getPersona(rut) {
|
||||
const res = await fetch(`${base}/personas/${rut}/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function createPersona(data) {
|
||||
const res = await fetch(`${base}/personas/`, {
|
||||
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 updatePersona({ rut: id = null, ...data }) {
|
||||
const res = await fetch(`${base}/personas/${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()
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getRegiones(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/regiones/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getRegion(id) {
|
||||
const res = await fetch(`${base}/regiones/${id}/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function createRegion(data) {
|
||||
const res = await fetch(`${base}/regiones/`, {
|
||||
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 updateRegion({ id, ...data }) {
|
||||
const res = await fetch(`${base}/regiones/${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 deleteRegion(id) {
|
||||
const res = await fetch(`${base}/regiones/${id}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getRutas(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/rutas/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function getRuta(id) {
|
||||
const res = await fetch(`${base}/rutas/${id}/`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function createRuta(data) {
|
||||
const res = await fetch(`${base}/rutas/`, {
|
||||
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 updateRuta({ id_paradero: id = null, ...data }) {
|
||||
const res = await fetch(`${base}/rutas/${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 deleteRuta(id) {
|
||||
const res = await fetch(`${base}/rutas/${id}/`, {
|
||||
method: 'DELETE',
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.text()
|
||||
return res.json()
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getTiposDispositivo(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/tipos/dispositivo/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.json()
|
||||
return res.json()
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
import { base, getToken } from './_config'
|
||||
|
||||
export async function getTiposPersona(params) {
|
||||
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
|
||||
const res = await fetch(`${base}/tipos/persona/${query}`, {
|
||||
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
|
||||
})
|
||||
if (!res.ok) throw await res.json()
|
||||
return res.json()
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
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 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, ...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()
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import { writable } from 'svelte/store'
|
||||
|
||||
const cacheParaderos = JSON.parse(sessionStorage.getItem('cache-paraderos') || 'null') || null;
|
||||
|
||||
export const storeMessages = writable([]);
|
||||
export const storeParaderos = writable(cacheParaderos);
|
||||
export const storeSession = writable({});
|
||||
export const storeLayout = writable({ showSidebar: true });
|
||||
|
||||
storeParaderos.subscribe(val => sessionStorage.setItem('cache-paraderos', JSON.stringify(val)))
|
|
@ -0,0 +1,2 @@
|
|||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
|
@ -0,0 +1,7 @@
|
|||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
||||
|
||||
export default {
|
||||
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
}
|