Merge remote-tracking branch 'origin/develop/Ronald' into merge/ronald

francisco/photos
Francisco Sandoval 2024-01-27 10:39:46 -03:00
commit fff8692f86
12 changed files with 783 additions and 2 deletions

View File

@ -5,7 +5,7 @@
<p class="mb-0">
<a class="text-muted" href="https://www.empresa.com/"
target="_blank" rel="noreferrer">
<strong>EMPRESA S.A.</strong>
<strong>.</strong>
</a> &copy;
</p>
</div>

View File

@ -36,6 +36,13 @@
<i class="align-middle bi bi-map fs-4" />
<span class="align-middle">Rutas</span>
</SideLink>
<li class="sidebar-header">GTFS</li>
<SideLink to="/gtfs_archivo">
<i class="align-middle bi bi-bus-front fs-4" />
<span class="align-middle">Listado Archivos GTFS</span>
</SideLink>
<li class="sidebar-header">Mantenedores</li>
@ -49,6 +56,16 @@
<span class="align-middle">Aplicaciones</span>
</SideLink>
<SideLink to="/red-transporte">
<i class="align-middle bi bi-sign-turn-right fs-4"></i>
<span class="align-middle">Red de Transporte</span>
</SideLink>
<SideLink to="/operadores">
<i class="align-middle bi bi-sign-turn-right fs-4"></i>
<span class="align-middle">Operadores</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>

View File

@ -0,0 +1,117 @@
<script>
import Paginate from "$/components/Paginate.svelte";
import { getGtfsArchivo,getGtfsArchivoId } from "$/services/gtfs_archivo";
import PageTitle from "$/components/PageTitle.svelte";
import { useLocation } from "svelte-navigator";
import { getPermisosPath } from "$/services/usuarios";
const limit = 15;
let page = 1;
let offset = 0;
let count = 0;
let ordering = 'id_archivo'
let redes = []
let red = null
let lista_gtfs = []
let gtfs = null
let location = useLocation()
getPermisosPath($location.pathname)
.then(data => escritura = data.escritura)
.catch(error => console.log({ error }))
$: onPage(page)
async function onPage(p) {
try {
loading = true
offset = (p - 1) * limit;
const data = await getGtfsArchivo({ offset, limit, ordering })
lista_gtfs = data.results;
count = data.count;
} catch (error) {
globalThis.toast.error(error)
} finally {
loading = false;
}
}
function onOrderBy(field) {
ordering = ordering === field ? '-' + field : field;
onPage(page)
}
</script>
<PageTitle {loading}>Listado Archivos GTFS</PageTitle>
<div class="card">
<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 style="width: 5%">
<a href={"#"} on:click|preventDefault={() => onOrderBy('id_gtfs')}>ID</a>
{#if ordering === 'id_gtfs'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-id_gtfs'}<i class="bi bi-caret-down-fill"></i>{/if}
</th>
<th>
<a href={"#"} on:click|preventDefault={() => onOrderBy('nombre_red')}>Archivo</a>
{#if ordering === 'archivo'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-archivo'}<i class="bi bi-caret-down-fill"></i>{/if}
</th>
<th>
<a href={"#"} on:click|preventDefault={() => onOrderBy('created')}>Creado el</a>
{#if ordering === 'created'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-created'}<i class="bi bi-caret-down-fill"></i>{/if}
</th>
<th>
<a href={"#"} on:click|preventDefault={() => onOrderBy('status')}>Estado</a>
{#if ordering === 'status'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-status'}<i class="bi bi-caret-down-fill"></i>{/if}
</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 lista_gtfs as app, index}
<tr>
<td class="table-light">{offset + index + 1}</td>
<td>{app.id_red}</td>
<td><a href={app.archivo}>{app.archivo}</a></td>
<td>{app.created}</td>
<td>{app.status}</td>
<td>{app.vigente ? '✅':'🚫'}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
<div class="card-footer d-flex">
<Paginate
forcePage={page}
{limit}
{count}
on:page={ev => page = ev.detail}
/>
</div>
</div>
<style>
.table-responsive {
max-height: calc(100vh - 300px);
}
</style>

View File

@ -0,0 +1,54 @@
<script>
import { getRedTransporte } from "$/services/red_transporte";
import { getGtfsArchivo } from "$/services/gtfs_archivo";
export let id_red;
export let id_archivo;
export let loading = false;
let redes= []
let archivos = []
let redes_archivos = []
getRedTransporte({ vigente: 1 })
.then(data => data.sort((a,b) => a.nombre_red < b.nombre_red? -1 : 1))
.then(data => redes = data)
.catch(error => globalThis.toast.error(error))
getGtfsArchivo()
.then(data => data.sort((a,b) => a.archivo < b.archivo? -1 : 1))
.then(data => redes= data)
.catch(error => globalThis.toast.error(error))
function onChangeRed() {
id_archivo = ''
if (!id_archivo) {
redes_archivos = []
} else {
const archivos_filtrados = archivos.filter(el => el.id_red === id_red);
const archivos_ordenados = archivos_filtrados.sort((a,b) => a.archivo < b.archivo ? -1 : 1);
redes_archivos = archivos_ordenados;
}
}
</script>
<div class="row">
<div class="col-md">
<div class="input-group mb-3">
<div class="input-group-text">Redes de Transporte</div>
<select bind:value={id_red} class="form-select" on:change={onChangeRed}>
<option value=""></option>
{#each redes as rt}
<option value={rt.id_red}>{rt.nombre_red}</option>
{/each}
</select>
</div>
</div>
</div>

View File

@ -0,0 +1,127 @@
<script>
import Paginate from "$/components/Paginate.svelte";
import { getOperadores } from "$/services/operadores";
import PageTitle from "$/components/PageTitle.svelte";
import ModalOperador from "./ModalOperador.svelte";
import { useLocation } from "svelte-navigator";
import { getPermisosPath } from "$/services/usuarios";
const limit = 15;
let page = 1;
let offset = 0;
let count = 0;
let ordering = 'id_operador'
let operadores = []
let operador = null
let loading = false;
let escritura = false;
let location = useLocation()
getPermisosPath($location.pathname)
.then(data => escritura = data.escritura)
.catch(error => console.log({ error }))
$: onPage(page)
async function onPage(p) {
try {
loading = true
offset = (p - 1) * limit;
const data = await getOperadores({ offset, limit, ordering })
operadores = data.results;
count = data.count;
} catch (error) {
globalThis.toast.error(error)
} finally {
loading = false;
}
}
function onEdita(item) {
operador = item;
}
function onNuevo() {
operador = {}
}
function onOrderBy(field) {
ordering = ordering === field ? '-' + field : field;
onPage(page)
}
</script>
<PageTitle {loading}>Operadores</PageTitle>
<div class="card">
<div class="card-header">
{#if escritura}
<button class="btn btn-primary" on:click|preventDefault={onNuevo}>
<i class="bi bi-plus-lg"></i> Nuevo
</button>
{/if}
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-sm table-bordered">
<thead>
<tr class="table-light">
<th style="width:5%">Nro</th>
<th>
<a href={"#"} on:click|preventDefault={() => onOrderBy('id_operador')}>ID</a>
{#if ordering === 'id_operador'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-id_operador'}<i class="bi bi-caret-down-fill"></i>{/if}
</th>
<th>
<a href={"#"} on:click|preventDefault={() => onOrderBy('nombre_operador')}>Nombre</a>
{#if ordering === 'nombre_operador'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-nombre_operador'}<i class="bi bi-caret-down-fill"></i>{/if}
</th>
<th>
<a href={"#"} on:click|preventDefault={() => onOrderBy('vigente')}>Vigente</a>
{#if ordering === 'vigente'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-vigente'}<i class="bi bi-caret-down-fill"></i>{/if}
</th>
</tr>
</thead>
<tbody>
{#each operadores as app, index}
<tr key={index}>
<td class="table-light">{offset + index + 1}</td>
<td>{app.id_operador}</td>
<td><a href={"#"} on:click|preventDefault={() => onEdita(app)}>{app.nombre_operador}</a></td>
<td>{app.vigente ? '✅':'🚫'}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
<div class="card-footer d-flex">
<a href={"#"} class="btn btn-outline-secondary me-3" on:click|preventDefault={() => onPage(page)}>
<i class="bi bi-arrow-repeat"></i>
</a>
<Paginate
{limit}
{count}
on:page={ev => page = ev.detail}
/>
</div>
</div>
{#if operador}
<ModalOperador
{operador}
{escritura}
on:close={() => operador = null}
on:refresh={() => onPage(page)}
/>
{/if}
<style>
.table-responsive {
max-height: calc(100vh - 300px);
}
</style>

View File

@ -0,0 +1,101 @@
<script>
import Modal from "../../components/Modal.svelte";
import { getOperador, createOperador, updateOperador, deleteOperador } from "$/services/operadores";
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
export let operador = {};
export let escritura = false;
let form = {}
let loading = false;
$: begin(operador.id_operador)
async function begin(id) {
try {
if (!id) return;
form = await getOperador(id) || {}
} catch (error) {
globalThis.toast.success(error.detail || error)
}
}
async function onSave() {
try {
loading = true;
if (operador.id_operador) {
form = await updateOperador(form)
} else {
form = await createOperador(form)
}
globalThis.toast.success('Se ha guardado el Operador')
dispatch('refresh')
dispatch('close')
} catch (error) {
if (error.detail) {
globalThis.toast.success(error.detail)
} else {
globalThis.toast.success(JSON.stringify(error))
}
} finally {
loading = false;
}
}
async function onDelete() {
try {
if (!confirm('Eliminará el registro?')) return;
loading = true;
await deleteOperador(form.id_operador)
globalThis.toast.success('Se ha eliminado el Operador')
dispatch('refresh')
dispatch('close')
} catch (error) {
globalThis.toast.error(error.detail || error)
} finally {
loading = false;
}
}
</script>
<form action="" on:submit|preventDefault={onSave}>
<Modal title={'Operador #' + (operador.id_operador || 'Nuevo')}
size="lg"
on:close={() => dispatch('close')}>
<div class={"form" + (escritura ? '' : ' disabled')}>
<div class="row mb-3">
<div class="col-md-3">ID</div>
<div class="col-md">
{#if operador.id_operador}
<input type="text" value={form.id_operador} disabled class="form-control">
{:else}
<input type="text" bind:value={form.id_operador} 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_operador} 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">
{#if escritura}
<button class="btn btn-primary"type="submit" disabled={loading}>Guardar</button>
<button class="btn btn-danger" on:click|preventDefault={onDelete} disabled={loading}>Eliminar</button>
{/if}
</svelte:fragment>
</Modal>
</form>
<style>
.disabled { pointer-events: none !important; }
</style>

View File

@ -0,0 +1,146 @@
<script>
import Paginate from "$/components/Paginate.svelte";
import { getRedTransporte } from "$/services/red_transporte";
import PageTitle from "$/components/PageTitle.svelte";
import ModalRedTransporte from "./ModalRedTransporte.svelte";
import { useLocation } from "svelte-navigator";
import { getPermisosPath } from "$/services/usuarios";
const limit = 15;
let page = 1;
let offset = 0;
let count = 0;
let ordering = 'id_red'
let redes = []
let red = null
let loading = false;
let escritura = false;
let location = useLocation()
getPermisosPath($location.pathname)
.then(data => escritura = data.escritura)
.catch(error => console.log({ error }))
$: onPage(page)
async function onPage(p) {
try {
loading = true
offset = (p - 1) * limit;
const data = await getRedTransporte({ offset, limit, ordering })
redes = data.results;
count = data.count;
} catch (error) {
globalThis.toast.error(error)
} finally {
loading = false;
}
}
function onEdita(item) {
red = item;
}
function onNuevo() {
red = {}
}
function onOrderBy(field) {
ordering = ordering === field ? '-' + field : field;
onPage(page)
}
</script>
<PageTitle {loading}>Redes de Transporte</PageTitle>
<div class="card">
<div class="card-header">
{#if escritura}
<button class="btn btn-primary" on:click|preventDefault={onNuevo}>
<i class="bi bi-plus-lg"></i> Nuevo
</button>
{/if}
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-sm table-bordered">
<thead>
<tr class="table-light">
<th style="width: 5%">Nro</th>
<th style="width: 5%">
<a href={"#"} on:click|preventDefault={() => onOrderBy('id_red')}>ID</a>
{#if ordering === 'id_red'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-id_red'}<i class="bi bi-caret-down-fill"></i>{/if}
</th>
<th>
<a href={"#"} on:click|preventDefault={() => onOrderBy('nombre_red')}>Nombre</a>
{#if ordering === 'nombre_red'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-nombre_red'}<i class="bi bi-caret-down-fill"></i>{/if}
</th>
<th>
<a href={"#"} on:click|preventDefault={() => onOrderBy('descripcion')}>Descripcion</a>
{#if ordering === 'descripcion'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-descripcion'}<i class="bi bi-caret-down-fill"></i>{/if}
</th>
<th>
<a href={"#"} on:click|preventDefault={() => onOrderBy('url_gtfs_rt')}>URL_GTFS_RT</a>
{#if ordering === 'url_gtfs_rt'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-url_gtfs_rt'}<i class="bi bi-caret-down-fill"></i>{/if}
</th>
<th>
<a href={"#"} on:click|preventDefault={() => onOrderBy('api_key')}>api_key</a>
{#if ordering === 'api_key'}<i class="bi bi-caret-up-fill"></i>{/if}
{#if ordering === '-api_key'}<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 redes as app, index}
<tr>
<td class="table-light">{offset + index + 1}</td>
<td>{app.id_red}</td>
<td><a href={"#"} on:click|preventDefault={() => onEdita(app)}>{app.nombre_red}</a></td>
<td>{app.descripcion}</td>
<td>{app.url_gtfs_rt}</td>
<td>{app.api_key}</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
forcePage={page}
{limit}
{count}
on:page={ev => page = ev.detail}
/>
</div>
</div>
{#if red}
<ModalRedTransporte
{red}
{escritura}
on:close={() => red = null}
on:refresh={() => onPage(page)}
/>
{/if}
<style>
.table-responsive {
max-height: calc(100vh - 300px);
}
</style>

View File

@ -0,0 +1,119 @@
<script>
import Modal from "../../components/Modal.svelte";
import { getRedTransporte, createRedTransporte, updateRedTransporte, deleteRedTransporte, getRedTransporteId } from "$/services/red_transporte";
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
export let red = {};
export let escritura = false;
let form = {}
let loading = false;
$: begin(red.id_red)
async function begin(id) {
try {
if (!id) return;
form = await getRedTransporteId(id) || {}
} catch (error) {
globalThis.toast.success(error.detail || error)
}
}
async function onSave() {
try {
loading = true;
if (red.id_red) {
form = await updateRedTransporte(form)
} else {
form = await createRedTransporte(form)
}
globalThis.toast.success('Se ha guardado la Red de Transporte')
dispatch('refresh')
dispatch('close')
} catch (error) {
if (error.detail) {
globalThis.toast.success(error.detail)
} else {
globalThis.toast.success(JSON.stringify(error))
}
} finally {
loading = false;
}
}
async function onDelete() {
try {
if (!confirm('Eliminará el registro?')) return;
loading = true;
await deleteRedTransporte(form.id_red)
globalThis.toast.success('Se ha eliminado la Red de Transporte')
dispatch('refresh')
dispatch('close')
} catch (error) {
globalThis.toast.error(error.detail || error)
} finally {
loading = false;
}
}
</script>
<form action="" on:submit|preventDefault={onSave}>
<Modal title={'Red de Transporte #' + (red.id_red || 'Nuevo')}
size="lg"
on:close={() => dispatch('close')}>
<div class={"form" + (escritura ? '' : ' disabled')}>
<div class="row mb-3">
<div class="col-md-3">ID</div>
<div class="col-md">
{#if red.id_red}
<input type="number" value={form.id_red} disabled class="form-control">
{:else}
<input type="number" bind:value={form.id_red} 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_red} required class="form-control">
</div>
</div>
<div class="mb-3">
<div class="col-md-3">Descripcion</div>
<div class="col-md">
<input type="text" bind:value={form.descripcion} class="form-control">
</div>
</div>
<div class="mb-4">
<div class="col-md-3">URL GTFS-RT</div>
<div class="col-md">
<input type="text" bind:value={form.url_gtfs_rt} class="form-control">
</div>
</div>
<div class="mb-5">
<div class="col-md-3">API Key</div>
<div class="col-md">
<input type="text" bind:value={form.api_key} class="form-control">
</div>
</div>
<div class="mb-6">
<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">
{#if escritura}
<button class="btn btn-primary"type="submit" disabled={loading}>Guardar</button>
<button class="btn btn-danger" on:click|preventDefault={onDelete} disabled={loading}>Eliminar</button>
{/if}
</svelte:fragment>
</Modal>
</form>
<style>
.disabled { pointer-events: none !important; }
</style>

View File

@ -2,6 +2,9 @@ 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 PageOperador from '$/pages/operadores/Admin.svelte'
import PageRedTransporte from '$/pages/red_transporte/Admin.svelte'
import PageArchivoGTFS from '$/pages/gtfs_archivo/Admin.svelte'
import PageUsuarios from '$/pages/usuarios/Admin.svelte'
import PageUsuarioCreate from '$/pages/usuarios/Usuario.svelte'
import PageUsuarioModifica from '$/pages/usuarios/Usuario.svelte'
@ -21,6 +24,9 @@ export const routes_base = [
{ path: '/', component: PageHome, public: true },
{ path: '/perfil', component: PagePerfil, public: true },
{ path: '/aplicaciones', component: PageAplicaciones },
{ path: '/red-transporte', component: PageRedTransporte },
{ path: '/gtfs-archivo', component: PageArchivoGTFS },
{ path: '/operadores', component: PageOperador },
{ path: '/usuarios', component: PageUsuarios },
{ path: '/usuarios/nuevo', component: PageUsuarioCreate },
{ path: '/usuarios/:login', component: PageUsuarioModifica },

View File

@ -0,0 +1,47 @@
import { base, getToken } from './_config'
export async function getGtfsArchivo(params) {
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
const res = await fetch(`${base}/gtfs-archivo/${query}`, {
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
})
if (!res.ok) throw await res.json()
return res.json()
}
export async function getGtfsArchivoId(id) {
const res = await fetch(`${base}/gtfs-archivo/${id}/`, {
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
})
if (!res.ok) throw await res.json()
return res.json()
}
export async function createGtfsArchivo(data) {
const res = await fetch(`${base}/gtfs-archivo/`, {
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 updateGtfsArchivo({ id_red: id = null, ...data }) {
const res = await fetch(`${base}/gtfs-archivo/${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 deleteGtfsArchivo(id) {
const res = await fetch(`${base}/gtfs-archivo/${id}/`, {
method: 'DELETE',
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
})
if (!res.ok) throw await res.json()
return res.text()
}

View File

@ -44,5 +44,5 @@ export async function deleteOperador(id) {
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
})
if (!res.ok) throw await res.text()
return res.json()
return res.text()
}

View File

@ -0,0 +1,47 @@
import { base, getToken } from './_config'
export async function getRedTransporte(params) {
const query = !params ? '' : '?' + (new URLSearchParams(params).toString());
const res = await fetch(`${base}/red-transporte/${query}`, {
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
})
if (!res.ok) throw await res.json()
return res.json()
}
export async function getRedTransporteId(id) {
const res = await fetch(`${base}/red-transporte/${id}/`, {
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
})
if (!res.ok) throw await res.json()
return res.json()
}
export async function createRedTransporte(data) {
const res = await fetch(`${base}/red-transporte/`, {
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 updateRedTransporte({ id_red: id = null, ...data }) {
const res = await fetch(`${base}/red-transporte/${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 deleteRedTransporte(id) {
const res = await fetch(`${base}/red-transporte/${id}/`, {
method: 'DELETE',
headers: { "Authorization": `Bearer ${getToken()}`, "Content-Type": "application/json" }
})
if (!res.ok) throw await res.json()
return res.text()
}