Create person if not found

This commit is contained in:
Araozu 2023-05-07 14:33:32 -05:00
parent b1ea575683
commit f011beac36
9 changed files with 297 additions and 32 deletions

View File

@ -28,6 +28,8 @@
"@nestjs/serve-static": "^3.0.1", "@nestjs/serve-static": "^3.0.1",
"@nestjs/typeorm": "^9.0.1", "@nestjs/typeorm": "^9.0.1",
"axios": "^1.4.0", "axios": "^1.4.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"mysql2": "^3.2.4", "mysql2": "^3.2.4",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0", "rxjs": "^7.2.0",

View File

@ -18,6 +18,8 @@ specifiers:
'@typescript-eslint/parser': ^5.0.0 '@typescript-eslint/parser': ^5.0.0
autoprefixer: ^10.4.14 autoprefixer: ^10.4.14
axios: ^1.4.0 axios: ^1.4.0
class-transformer: ^0.5.1
class-validator: ^0.14.0
esbuild: ^0.17.18 esbuild: ^0.17.18
esbuild-plugin-solid: ^0.5.0 esbuild-plugin-solid: ^0.5.0
eslint: ^8.0.1 eslint: ^8.0.1
@ -44,12 +46,14 @@ specifiers:
typescript: ^4.7.4 typescript: ^4.7.4
dependencies: dependencies:
'@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy
'@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru
'@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne '@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne
'@nestjs/serve-static': 3.0.1_gqkob5jndcfeb3wjw72wbdwyne '@nestjs/serve-static': 3.0.1_gqkob5jndcfeb3wjw72wbdwyne
'@nestjs/typeorm': 9.0.1_5jgqalkub5zb475ngukfuhbndu '@nestjs/typeorm': 9.0.1_5jgqalkub5zb475ngukfuhbndu
axios: 1.4.0 axios: 1.4.0
class-transformer: 0.5.1
class-validator: 0.14.0
mysql2: 3.3.0 mysql2: 3.3.0
reflect-metadata: 0.1.13 reflect-metadata: 0.1.13
rxjs: 7.8.1 rxjs: 7.8.1
@ -1295,7 +1299,7 @@ packages:
- webpack-cli - webpack-cli
dev: true dev: true
/@nestjs/common/9.4.0_atc7tu2sld2m3nk4hmwkqn6qde: /@nestjs/common/9.4.0_j3td4gnlgk75ora6o6suo62byy:
resolution: {integrity: sha512-RUcVAQsEF4WPrmzFXEOUfZnPwrLTe1UVlzXTlSyfqfqbdWDPKDGlIPVelBLfc5/+RRUQ0I5iE4+CQvpCmkqldw==} resolution: {integrity: sha512-RUcVAQsEF4WPrmzFXEOUfZnPwrLTe1UVlzXTlSyfqfqbdWDPKDGlIPVelBLfc5/+RRUQ0I5iE4+CQvpCmkqldw==}
peerDependencies: peerDependencies:
cache-manager: <=5 cache-manager: <=5
@ -1311,6 +1315,8 @@ packages:
class-validator: class-validator:
optional: true optional: true
dependencies: dependencies:
class-transformer: 0.5.1
class-validator: 0.14.0
iterare: 1.2.1 iterare: 1.2.1
reflect-metadata: 0.1.13 reflect-metadata: 0.1.13
rxjs: 7.8.1 rxjs: 7.8.1
@ -1335,7 +1341,7 @@ packages:
'@nestjs/websockets': '@nestjs/websockets':
optional: true optional: true
dependencies: dependencies:
'@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy
'@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne '@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne
'@nuxtjs/opencollective': 0.3.2 '@nuxtjs/opencollective': 0.3.2
fast-safe-stringify: 2.1.1 fast-safe-stringify: 2.1.1
@ -1354,7 +1360,7 @@ packages:
'@nestjs/common': ^9.0.0 '@nestjs/common': ^9.0.0
'@nestjs/core': ^9.0.0 '@nestjs/core': ^9.0.0
dependencies: dependencies:
'@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy
'@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru
body-parser: 1.20.2 body-parser: 1.20.2
cors: 2.8.5 cors: 2.8.5
@ -1380,7 +1386,7 @@ packages:
'@fastify/cors': 8.2.1 '@fastify/cors': 8.2.1
'@fastify/formbody': 7.4.0 '@fastify/formbody': 7.4.0
'@fastify/middie': 8.1.0 '@fastify/middie': 8.1.0
'@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy
'@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru
fastify: 4.15.0 fastify: 4.15.0
light-my-request: 5.9.1 light-my-request: 5.9.1
@ -1434,7 +1440,7 @@ packages:
fastify: fastify:
optional: true optional: true
dependencies: dependencies:
'@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy
'@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru
path-to-regexp: 0.2.5 path-to-regexp: 0.2.5
dev: false dev: false
@ -1452,7 +1458,7 @@ packages:
'@nestjs/platform-express': '@nestjs/platform-express':
optional: true optional: true
dependencies: dependencies:
'@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy
'@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru
'@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne '@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne
tslib: 2.5.0 tslib: 2.5.0
@ -1467,7 +1473,7 @@ packages:
rxjs: ^7.2.0 rxjs: ^7.2.0
typeorm: ^0.3.0 typeorm: ^0.3.0
dependencies: dependencies:
'@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy
'@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru
reflect-metadata: 0.1.13 reflect-metadata: 0.1.13
rxjs: 7.8.1 rxjs: 7.8.1
@ -1755,6 +1761,9 @@ packages:
'@types/superagent': 4.1.17 '@types/superagent': 4.1.17
dev: true dev: true
/@types/validator/13.7.16:
resolution: {integrity: sha512-VyKmLktUHYLbrSbsRi241MSUlGYomQgK/tfCNpej3Gt5qDOM10AZ3nU2aR2s5JritClXuOBu4K7MkywVW/Y6Ow==}
/@types/yargs-parser/21.0.0: /@types/yargs-parser/21.0.0:
resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
dev: true dev: true
@ -2531,6 +2540,16 @@ packages:
resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==}
dev: true dev: true
/class-transformer/0.5.1:
resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==}
/class-validator/0.14.0:
resolution: {integrity: sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==}
dependencies:
'@types/validator': 13.7.16
libphonenumber-js: 1.10.30
validator: 13.9.0
/cli-cursor/3.1.0: /cli-cursor/3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -4433,6 +4452,9 @@ packages:
type-check: 0.4.0 type-check: 0.4.0
dev: true dev: true
/libphonenumber-js/1.10.30:
resolution: {integrity: sha512-PLGc+xfrQrkya/YK2/5X+bPpxRmyJBHM+xxz9krUdSgk4Vs2ZwxX5/Ow0lv3r9PDlDtNWb4u+it8MY5rZ0IyGw==}
/light-my-request/5.9.1: /light-my-request/5.9.1:
resolution: {integrity: sha512-UT7pUk8jNCR1wR7w3iWfIjx32DiB2f3hFdQSOwy3/EPQ3n3VocyipUxcyRZR0ahoev+fky69uA+GejPa9KuHKg==} resolution: {integrity: sha512-UT7pUk8jNCR1wR7w3iWfIjx32DiB2f3hFdQSOwy3/EPQ3n3VocyipUxcyRZR0ahoev+fky69uA+GejPa9KuHKg==}
dependencies: dependencies:
@ -6248,6 +6270,10 @@ packages:
resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==}
dev: true dev: true
/validator/13.9.0:
resolution: {integrity: sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==}
engines: {node: '>= 0.10'}
/vary/1.1.2: /vary/1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}

View File

@ -7,6 +7,8 @@ import { CertificateService } from "./certificate/certificate.service";
import { Persona } from "./model/Persona/persona.entity"; import { Persona } from "./model/Persona/persona.entity";
import { CursoGIE } from "./model/CursoGIE/cursoGIE.entity"; import { CursoGIE } from "./model/CursoGIE/cursoGIE.entity";
import { RegistroGIE } from "./model/RegistroGIE/registroGIE.entity"; import { RegistroGIE } from "./model/RegistroGIE/registroGIE.entity";
import { PersonController } from "./controller/person/person.controller";
import { PersonService } from "./controller/person/person.service";
@Module({ @Module({
imports: [ imports: [
@ -25,7 +27,7 @@ import { RegistroGIE } from "./model/RegistroGIE/registroGIE.entity";
synchronize: false, synchronize: false,
}), }),
], ],
controllers: [AppController, CertificateController], controllers: [AppController, CertificateController, PersonController],
providers: [AppService, CertificateService], providers: [AppService, CertificateService, PersonService],
}) })
export class AppModule {} export class AppModule {}

View File

@ -0,0 +1,17 @@
import { Body, Controller, InternalServerErrorException, Post, ValidationPipe } from "@nestjs/common";
import { PersonDto } from "./person.dto";
import { PersonService } from "./person.service";
@Controller("person")
export class PersonController {
constructor(private personService: PersonService) {
}
@Post()
async create(@Body(new ValidationPipe()) personDto: PersonDto) {
const success = await this.personService.create(personDto);
if (!success) {
throw new InternalServerErrorException("Can't insert into DB");
}
}
}

View File

@ -0,0 +1,8 @@
import { IsString } from "class-validator";
export class PersonDto {
dni: string;
apellidoPaterno: string;
apellidoMaterno: string;
nombres: string;
}

View File

@ -0,0 +1,28 @@
import { Injectable, InternalServerErrorException } from "@nestjs/common";
import { PersonDto } from "./person.dto";
import { DataSource, Repository } from "typeorm";
import { Persona } from "../../model/Persona/persona.entity";
@Injectable()
export class PersonService {
personaRepository: Repository<Persona>;
constructor(private dataSource: DataSource) {
this.personaRepository = dataSource.getRepository(Persona);
}
async create(data: PersonDto) {
const p = new Persona();
p.dni = data.dni;
p.nombres = data.nombres;
p.apellidoMaterno = data.apellidoMaterno;
p.apellidoPaterno = data.apellidoPaterno;
try {
await this.personaRepository.save(p);
return true;
} catch (e) {
return false;
}
}
}

View File

@ -3,6 +3,7 @@ import { FastifyAdapter, NestFastifyApplication } from "@nestjs/platform-fastify
import { AppModule } from "./app.module"; import { AppModule } from "./app.module";
import * as express from "express"; import * as express from "express";
import * as path from "path"; import * as path from "path";
import { ValidationPipe } from "@nestjs/common";
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AppModule); const app = await NestFactory.create(AppModule);

View File

@ -1,6 +1,7 @@
import { createSignal } from "solid-js"; import { createSignal, Show } from "solid-js";
import { JSX } from "solid-js/jsx-runtime"; import { JSX } from "solid-js/jsx-runtime";
import { Person } from "../../types/Person"; import { Person } from "../../types/Person";
import { RegisterPerson } from "./Search/RegisterPerson";
type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & { type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & {
submitter: HTMLElement; submitter: HTMLElement;
@ -14,23 +15,38 @@ export function Search(props: {setPerson: (p: Person | null) => void}) {
const [dni, setDni] = createSignal(""); const [dni, setDni] = createSignal("");
const [loading, setLoading] = createSignal(false); const [loading, setLoading] = createSignal(false);
const [error, setError] = createSignal(""); const [error, setError] = createSignal("");
const [advertencia, setAdvertencia] = createSignal("");
/* /*
Get the user data from the DB Get the user data from the DB
*/ */
const searchDNI: HTMLEventFn = (ev) => { const searchDNI: HTMLEventFn = (ev) => {
console.log("Retrieving from db...");
ev.preventDefault(); ev.preventDefault();
search();
};
const search = async() => {
setLoading(true); setLoading(true);
setError(""); setError("");
setAdvertencia("");
try {
const response = await fetch(`/certificate/${dni()}`);
const body = await response.json();
if (response.ok) {
props.setPerson(body);
} else if (response.status === 404) {
console.error(body);
setAdvertencia("Persona no encontrada. Se debe insertar manualmente sus datos.");
} else {
setError(body);
}
} catch (e) {
setError(JSON.stringify(e));
}
fetch(`/certificate/${dni()}`)
.then((response) => response.json())
.then((person: Person) => {
props.setPerson(person);
setLoading(false); setLoading(false);
setError("");
});
}; };
return ( return (
@ -41,8 +57,8 @@ export function Search(props: {setPerson: (p: Person | null) => void}) {
<br /> <br />
<input <input
id="search-dni" id="search-dni"
class="bg-c-background text-c-on-background border-c-outline border rounded px-2 py-1 class="bg-c-background text-c-on-background border-c-outline border-2 rounded px-2 py-1
invalid:border-c-on-error invalid:bg-c-error invalid:text-c-on-error invalid:border-c-error invalid:text-c-error
focus:border-c-primary outline-none focus:border-c-primary outline-none
disabled:opacity-50 disabled:cursor-not-allowed" disabled:opacity-50 disabled:cursor-not-allowed"
type="text" type="text"
@ -51,6 +67,7 @@ export function Search(props: {setPerson: (p: Person | null) => void}) {
pattern="[0-9]{8}" pattern="[0-9]{8}"
placeholder="Número de DNI" placeholder="Número de DNI"
value={dni()} value={dni()}
required={true}
onChange={(e) => setDni(e.target.value)} onChange={(e) => setDni(e.target.value)}
disabled={loading()} disabled={loading()}
/> />
@ -63,18 +80,29 @@ export function Search(props: {setPerson: (p: Person | null) => void}) {
value="Buscar" value="Buscar"
disabled={loading()} disabled={loading()}
/> />
</form>
<p <p
class="my-2 p-1 rounded class="my-2 p-1 rounded w-fit mx-4 bg-c-error text-c-on-error"
bg-c-error text-c-on-error"
style={{display: error() === "" ? "none" : "block"}} style={{display: error() === "" ? "none" : "block"}}
> >
Error: Error:
<br /> <br />
{error()}&nbsp; {error()}
</p> </p>
</form> <p
class="my-2 mx-4 p-1 rounded border border-c-error text-c-error font-medium w-fit"
style={{display: advertencia() === "" ? "none" : "block"}}
>
Advertencia:
<br />
{advertencia()}
</p>
<Show when={advertencia() !== ""}>
<RegisterPerson dni={dni()} onSuccess={search} />
</Show>
</div> </div>
); );
} }

View File

@ -0,0 +1,153 @@
import { JSX } from "solid-js/types/jsx";
import { createSignal } from "solid-js";
type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & {
submitter: HTMLElement;
}>;
export function RegisterPerson(props: {dni: string, onSuccess: () => void}) {
const [nombres, setNombres] = createSignal("");
const [apellidoP, setApellidoP] = createSignal("");
const [apellidoM, setApellidoM] = createSignal("");
const [loading, setLoading] = createSignal(false);
const [error, setError] = createSignal("");
const register: HTMLEventFn = async(ev) => {
ev.preventDefault();
setLoading(true);
setError("");
if (props.dni.length !== 8) {
setLoading(false);
setError("DNI es invalido");
return;
}
const response = await fetch("/person", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
dni: props.dni.toUpperCase(),
apellidoPaterno: apellidoP().toUpperCase(),
apellidoMaterno: apellidoM().toUpperCase(),
nombres: nombres().toUpperCase(),
}),
});
if (response.ok) {
props.onSuccess();
} else {
const d = await response.json();
setError(`Error creando persona en BD. ${d}`);
}
setLoading(false);
};
return (
<div class="p-2">
<h2 class="my-2 font-medium text-xl">1.1 Registrar persona manualmente</h2>
<form onsubmit={register} class="px-4">
<label for="add-person-dni">DNI</label>
<br/>
<input
id="add-person-dni"
name="add-person-dni"
class="bg-c-background text-c-on-background border-c-outline border-2 rounded px-2 py-1
invalid:border-c-error invalid:text-c-error
outline-none
disabled:cursor-not-allowed"
type="text"
minLength={8}
maxLength={8}
pattern="[0-9]{8}"
placeholder="DNI"
value={props.dni}
required={true}
disabled={true}
/>
<br/>
<br/>
<label for="add-person-nombres">Nombres</label>
<br/>
<input
id="add-person-nombres"
name="add-person-nombres"
class="bg-c-background text-c-on-background border-c-outline border-2 rounded px-2 py-1
invalid:border-c-error invalid:text-c-error
focus:border-c-primary outline-none
disabled:opacity-50 disabled:cursor-not-allowed"
type="text"
placeholder="Nombres"
value={nombres()}
required={true}
onChange={(e) => setNombres(e.target.value.toUpperCase())}
disabled={loading()}
/>
<br/>
<br/>
<div class="grid grid-cols-2 w-1/2">
<div>
<label for="add-person-apellido-paterno">Apellido Paterno</label>
<br/>
<input
id="add-person-apellido-paterno"
class="bg-c-background text-c-on-background border-c-outline border-2 rounded px-2 py-1
invalid:border-c-error invalid:text-c-error
focus:border-c-primary outline-none
disabled:opacity-50 disabled:cursor-not-allowed"
type="text"
placeholder="Apellido paterno"
value={apellidoP()}
required={true}
onChange={(e) => setApellidoP(e.target.value.toUpperCase())}
disabled={loading()}
/>
</div>
<div>
<label for="add-person-apellido-paterno">Apellido Materno</label>
<br/>
<input
id="add-person-apellido-materno"
class="bg-c-background text-c-on-background border-c-outline border-2 rounded px-2 py-1
invalid:border-c-error invalid:text-c-error
focus:border-c-primary outline-none
disabled:opacity-50 disabled:cursor-not-allowed"
type="text"
placeholder="Apellido materno"
value={apellidoM()}
required={true}
onChange={(e) => setApellidoM(e.target.value.toUpperCase())}
disabled={loading()}
/>
</div>
</div>
<br/>
<input
class="bg-c-primary text-c-on-primary px-4 py-2 rounded-md cursor-pointer
disabled:opacity-50 disabled:cursor-not-allowed"
type="submit"
value="Registrar persona"
disabled={loading()}
/>
</form>
<br/>
<p
class="my-2 p-1 rounded mx-4 bg-c-error text-c-on-error"
style={{display: error() === "" ? "none" : "block"}}
>
Error:
<br />
{error()}&nbsp;
</p>
</div>
);
}