From f011beac36aa35d0e82b959213352daede293d14 Mon Sep 17 00:00:00 2001 From: Araozu Date: Sun, 7 May 2023 14:33:32 -0500 Subject: [PATCH] Create person if not found --- package.json | 2 + pnpm-lock.yaml | 42 ++++- src/app.module.ts | 6 +- src/controller/person/person.controller.ts | 17 ++ src/controller/person/person.dto.ts | 8 + src/controller/person/person.service.ts | 28 ++++ src/main.ts | 1 + src/views/components/Search.tsx | 72 ++++++--- .../components/Search/RegisterPerson.tsx | 153 ++++++++++++++++++ 9 files changed, 297 insertions(+), 32 deletions(-) create mode 100644 src/controller/person/person.controller.ts create mode 100644 src/controller/person/person.dto.ts create mode 100644 src/controller/person/person.service.ts create mode 100644 src/views/components/Search/RegisterPerson.tsx diff --git a/package.json b/package.json index 6e3bc38..79262d7 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "@nestjs/serve-static": "^3.0.1", "@nestjs/typeorm": "^9.0.1", "axios": "^1.4.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", "mysql2": "^3.2.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4801918..3b6dd59 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,6 +18,8 @@ specifiers: '@typescript-eslint/parser': ^5.0.0 autoprefixer: ^10.4.14 axios: ^1.4.0 + class-transformer: ^0.5.1 + class-validator: ^0.14.0 esbuild: ^0.17.18 esbuild-plugin-solid: ^0.5.0 eslint: ^8.0.1 @@ -44,12 +46,14 @@ specifiers: typescript: ^4.7.4 dependencies: - '@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru '@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne '@nestjs/serve-static': 3.0.1_gqkob5jndcfeb3wjw72wbdwyne '@nestjs/typeorm': 9.0.1_5jgqalkub5zb475ngukfuhbndu axios: 1.4.0 + class-transformer: 0.5.1 + class-validator: 0.14.0 mysql2: 3.3.0 reflect-metadata: 0.1.13 rxjs: 7.8.1 @@ -1295,7 +1299,7 @@ packages: - webpack-cli dev: true - /@nestjs/common/9.4.0_atc7tu2sld2m3nk4hmwkqn6qde: + /@nestjs/common/9.4.0_j3td4gnlgk75ora6o6suo62byy: resolution: {integrity: sha512-RUcVAQsEF4WPrmzFXEOUfZnPwrLTe1UVlzXTlSyfqfqbdWDPKDGlIPVelBLfc5/+RRUQ0I5iE4+CQvpCmkqldw==} peerDependencies: cache-manager: <=5 @@ -1311,6 +1315,8 @@ packages: class-validator: optional: true dependencies: + class-transformer: 0.5.1 + class-validator: 0.14.0 iterare: 1.2.1 reflect-metadata: 0.1.13 rxjs: 7.8.1 @@ -1335,7 +1341,7 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy '@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 @@ -1354,7 +1360,7 @@ packages: '@nestjs/common': ^9.0.0 '@nestjs/core': ^9.0.0 dependencies: - '@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru body-parser: 1.20.2 cors: 2.8.5 @@ -1380,7 +1386,7 @@ packages: '@fastify/cors': 8.2.1 '@fastify/formbody': 7.4.0 '@fastify/middie': 8.1.0 - '@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru fastify: 4.15.0 light-my-request: 5.9.1 @@ -1434,7 +1440,7 @@ packages: fastify: optional: true dependencies: - '@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru path-to-regexp: 0.2.5 dev: false @@ -1452,7 +1458,7 @@ packages: '@nestjs/platform-express': optional: true dependencies: - '@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru '@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne tslib: 2.5.0 @@ -1467,7 +1473,7 @@ packages: rxjs: ^7.2.0 typeorm: ^0.3.0 dependencies: - '@nestjs/common': 9.4.0_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/common': 9.4.0_j3td4gnlgk75ora6o6suo62byy '@nestjs/core': 9.4.0_s52inwjga72fw7w7ilq4rk5hru reflect-metadata: 0.1.13 rxjs: 7.8.1 @@ -1755,6 +1761,9 @@ packages: '@types/superagent': 4.1.17 dev: true + /@types/validator/13.7.16: + resolution: {integrity: sha512-VyKmLktUHYLbrSbsRi241MSUlGYomQgK/tfCNpej3Gt5qDOM10AZ3nU2aR2s5JritClXuOBu4K7MkywVW/Y6Ow==} + /@types/yargs-parser/21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} dev: true @@ -2531,6 +2540,16 @@ packages: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} 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: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -4433,6 +4452,9 @@ packages: type-check: 0.4.0 dev: true + /libphonenumber-js/1.10.30: + resolution: {integrity: sha512-PLGc+xfrQrkya/YK2/5X+bPpxRmyJBHM+xxz9krUdSgk4Vs2ZwxX5/Ow0lv3r9PDlDtNWb4u+it8MY5rZ0IyGw==} + /light-my-request/5.9.1: resolution: {integrity: sha512-UT7pUk8jNCR1wR7w3iWfIjx32DiB2f3hFdQSOwy3/EPQ3n3VocyipUxcyRZR0ahoev+fky69uA+GejPa9KuHKg==} dependencies: @@ -6248,6 +6270,10 @@ packages: resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} dev: true + /validator/13.9.0: + resolution: {integrity: sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==} + engines: {node: '>= 0.10'} + /vary/1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} diff --git a/src/app.module.ts b/src/app.module.ts index 3178715..e74569c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -7,6 +7,8 @@ import { CertificateService } from "./certificate/certificate.service"; import { Persona } from "./model/Persona/persona.entity"; import { CursoGIE } from "./model/CursoGIE/cursoGIE.entity"; import { RegistroGIE } from "./model/RegistroGIE/registroGIE.entity"; +import { PersonController } from "./controller/person/person.controller"; +import { PersonService } from "./controller/person/person.service"; @Module({ imports: [ @@ -25,7 +27,7 @@ import { RegistroGIE } from "./model/RegistroGIE/registroGIE.entity"; synchronize: false, }), ], - controllers: [AppController, CertificateController], - providers: [AppService, CertificateService], + controllers: [AppController, CertificateController, PersonController], + providers: [AppService, CertificateService, PersonService], }) export class AppModule {} diff --git a/src/controller/person/person.controller.ts b/src/controller/person/person.controller.ts new file mode 100644 index 0000000..b61fce6 --- /dev/null +++ b/src/controller/person/person.controller.ts @@ -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"); + } + } +} diff --git a/src/controller/person/person.dto.ts b/src/controller/person/person.dto.ts new file mode 100644 index 0000000..ef80eb4 --- /dev/null +++ b/src/controller/person/person.dto.ts @@ -0,0 +1,8 @@ +import { IsString } from "class-validator"; + +export class PersonDto { + dni: string; + apellidoPaterno: string; + apellidoMaterno: string; + nombres: string; +} diff --git a/src/controller/person/person.service.ts b/src/controller/person/person.service.ts new file mode 100644 index 0000000..17cbdca --- /dev/null +++ b/src/controller/person/person.service.ts @@ -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; + + 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; + } + } +} diff --git a/src/main.ts b/src/main.ts index 1bbd1a8..2f6ec50 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import { FastifyAdapter, NestFastifyApplication } from "@nestjs/platform-fastify import { AppModule } from "./app.module"; import * as express from "express"; import * as path from "path"; +import { ValidationPipe } from "@nestjs/common"; async function bootstrap() { const app = await NestFactory.create(AppModule); diff --git a/src/views/components/Search.tsx b/src/views/components/Search.tsx index 9be7f2e..f9484af 100644 --- a/src/views/components/Search.tsx +++ b/src/views/components/Search.tsx @@ -1,6 +1,7 @@ -import { createSignal } from "solid-js"; +import { createSignal, Show } from "solid-js"; import { JSX } from "solid-js/jsx-runtime"; import { Person } from "../../types/Person"; +import { RegisterPerson } from "./Search/RegisterPerson"; type HTMLEventFn = JSX.EventHandlerUnion void}) { const [dni, setDni] = createSignal(""); const [loading, setLoading] = createSignal(false); const [error, setError] = createSignal(""); + const [advertencia, setAdvertencia] = createSignal(""); /* Get the user data from the DB */ const searchDNI: HTMLEventFn = (ev) => { - console.log("Retrieving from db..."); ev.preventDefault(); + search(); + }; + + const search = async() => { setLoading(true); setError(""); + setAdvertencia(""); - fetch(`/certificate/${dni()}`) - .then((response) => response.json()) - .then((person: Person) => { - props.setPerson(person); - setLoading(false); - setError(""); - }); + 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)); + } + + setLoading(false); }; return ( @@ -41,8 +57,8 @@ export function Search(props: {setPerson: (p: Person | null) => void}) {
void}) { pattern="[0-9]{8}" placeholder="NĂºmero de DNI" value={dni()} + required={true} onChange={(e) => setDni(e.target.value)} disabled={loading()} /> @@ -63,18 +80,29 @@ export function Search(props: {setPerson: (p: Person | null) => void}) { value="Buscar" disabled={loading()} /> - -

- Error: -
- {error()}  -

- + +

+ Error: +
+ {error()} +

+ +

+ Advertencia: +
+ {advertencia()} +

+ + + + ); } diff --git a/src/views/components/Search/RegisterPerson.tsx b/src/views/components/Search/RegisterPerson.tsx new file mode 100644 index 0000000..4ecdb3f --- /dev/null +++ b/src/views/components/Search/RegisterPerson.tsx @@ -0,0 +1,153 @@ +import { JSX } from "solid-js/types/jsx"; +import { createSignal } from "solid-js"; + +type HTMLEventFn = JSX.EventHandlerUnion; + +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 ( +
+

1.1 Registrar persona manualmente

+
+ +
+ +
+
+ + +
+ setNombres(e.target.value.toUpperCase())} + disabled={loading()} + /> +
+
+ +
+
+ +
+ setApellidoP(e.target.value.toUpperCase())} + disabled={loading()} + /> +
+
+ +
+ setApellidoM(e.target.value.toUpperCase())} + disabled={loading()} + /> +
+
+
+ + +
+ +
+ +

+ Error: +
+ {error()}  +

+
+ ); +}