se agrega mantenedor de usuarios
parent
7bd6affb30
commit
b8c7a24ae1
|
@ -11,6 +11,7 @@ services:
|
|||
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 ;
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
import { onMount } from 'svelte'
|
||||
import { getInfoToken } from '$/services/login'
|
||||
import { storeSession } from '$/stores/session'
|
||||
import { storeSession, storeLayout } from '$/stores/global'
|
||||
|
||||
onMount(() => {
|
||||
document.dispatchEvent(new Event('DOMContentLoaded'));
|
||||
|
@ -45,14 +45,18 @@
|
|||
<main>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-3 bd-sidebar d none d-md-block d-lg-block">
|
||||
<Sidebar />
|
||||
</div>
|
||||
{#if $storeLayout.showSidebar}
|
||||
<div class="col-12 col-md-3 bd-sidebar d none d-md-block d-lg-block">
|
||||
<Sidebar />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="col-12 col-md-9">
|
||||
<div class="col-12 col-md">
|
||||
<section class="doc-section">
|
||||
{#each routes as r}
|
||||
<Route path={r.path} component={r.component} />
|
||||
<Route path={r.path}>
|
||||
<svelte:component this={r.component}/>
|
||||
</Route>
|
||||
{/each}
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -8,4 +8,5 @@
|
|||
|
||||
<div class="sidebar-header text-red mt-3">Mantenedores</div>
|
||||
<SideLink to="/aplicaciones">Aplicaciones</SideLink>
|
||||
<SideLink to="/usuarios">Usuarios</SideLink>
|
||||
</nav>
|
||||
|
|
|
@ -82,7 +82,8 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="d-flex">
|
||||
<a href={"#"} class="btn btn-sm btn-outline-dark mr-3" on:click|preventDefault={() => onPage(page)}>Refrescar</a>
|
||||
<Paginate
|
||||
{offset}
|
||||
{limit}
|
||||
|
|
|
@ -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" disabled={!es_nuevo} required on:input={onChangeRut}>
|
||||
</div>
|
||||
<div class="col-md mb-3">
|
||||
Tipo Tratamiento
|
||||
<select bind:value={form.id_tipo_tratamiento} class="form-control">
|
||||
<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-control">
|
||||
<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,34 @@
|
|||
<script>
|
||||
import Paginate from '$/components/Paginate.svelte'
|
||||
let page = 1;
|
||||
let limit = 15;
|
||||
let count = 0;
|
||||
let personas = [];
|
||||
</script>
|
||||
|
||||
<h2 class="h3 mb-3">Personas</h2>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header"><button class="btn btn-outline-primary">Nuevo</button></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>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<Paginate
|
||||
offset={(page - 1) / limit}
|
||||
{limit}
|
||||
{count}
|
||||
on:page={ev => page = ev.detail}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,70 @@
|
|||
<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
|
||||
|
||||
$: onPage(page)
|
||||
|
||||
async function onPage(page) {
|
||||
try {
|
||||
const offset = (page - 1) * limit;
|
||||
usuarios = await getUsuarios({ offset, limit, ordering: 'login' })
|
||||
} catch(error) {
|
||||
alert(error.detail || error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<PageTitle>Usuarios</PageTitle>
|
||||
|
||||
<div class="mb-3 d-flex">
|
||||
<Link to='/usuarios/nuevo' class="btn btn-pill-primary">Nuevo</Link>
|
||||
<div class="m-auto"></div>
|
||||
<Link to='/' class="btn btn-pill-secondary">Volver</Link>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr class="table-accent">
|
||||
<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 class="d-flex">
|
||||
<a href={"#"} class="btn btn-sm btn-outline-dark mr-3" on:click|preventDefault={() => onPage(page)}>Refrescar</a>
|
||||
<Paginate
|
||||
offset={(page - 1) * limit}
|
||||
{limit}
|
||||
count={usuarios.count}
|
||||
on:page={ev => page = ev.detail}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.table-responsive {
|
||||
max-height: calc(100vh - 300px);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,89 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { storeLayout } from '$/stores/global'
|
||||
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 = ''
|
||||
|
||||
$: es_nuevo = !$params.login
|
||||
$: begin($params)
|
||||
$: console.log({ form_persona })
|
||||
|
||||
async function begin({ login = null }) {
|
||||
try {
|
||||
if (!login) return; // si es nuevo, no busco informacion
|
||||
const { persona, ...data } = await getUsuario(login)
|
||||
form_persona = persona;
|
||||
form_usuario = data;
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
alert(error.detail || error)
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
<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 ml-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,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="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="persona-es_vigente" checked={form.vigente}>
|
||||
<label class="custom-control-label" for="persona-es_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,11 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { storeLayout } from '$/stores/global'
|
||||
|
||||
onMount(() => {
|
||||
$storeLayout.showSidebar = false;
|
||||
return () => {
|
||||
$storeLayout.showSidebar = true;
|
||||
}
|
||||
})
|
||||
</script>
|
|
@ -1,9 +1,8 @@
|
|||
<script>
|
||||
import { storeSession } from "$/stores/session";
|
||||
import { storeSession } from "$/stores/global";
|
||||
import PageTitle from "$/components/PageTitle.svelte";
|
||||
let persona = {}
|
||||
$: persona = $storeSession.persona || {};
|
||||
$: console.log({ persona })
|
||||
</script>
|
||||
|
||||
<PageTitle>Perfil de usuario</PageTitle>
|
|
@ -1,11 +1,17 @@
|
|||
import PageHome from '$/pages/site/Home.svelte'
|
||||
import PagePerfil from '$/pages/usuario/Perfil.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/Create.svelte'
|
||||
import PageUsuarioModifica from '$/pages/usuarios/Create.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: '*', component: PageError },
|
||||
]
|
|
@ -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.json()
|
||||
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.json()
|
||||
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.json()
|
||||
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.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,5 @@
|
|||
import { writable } from 'svelte/store'
|
||||
|
||||
export const storeMessages = writable([]);
|
||||
export const storeSession = writable({});
|
||||
export const storeLayout = writable({ showSidebar: true });
|
|
@ -1,3 +0,0 @@
|
|||
import { writable } from 'svelte/store'
|
||||
|
||||
export const storeMessages = writable([]);
|
|
@ -1,3 +0,0 @@
|
|||
import { writable } from 'svelte/store'
|
||||
|
||||
export const storeSession = writable({});
|
Loading…
Reference in New Issue