From 0407566984aa06f82c7690fa0fe0bcb14ae53238 Mon Sep 17 00:00:00 2001 From: PauMAVA Date: Sat, 18 May 2024 14:39:56 +0200 Subject: [PATCH 01/13] Add simple pet service --- src/app/pet/pet.service.ts | 20 ++++++++++++++++++++ src/app/pet/pet.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/app/pet/pet.service.ts create mode 100644 src/app/pet/pet.ts diff --git a/src/app/pet/pet.service.ts b/src/app/pet/pet.service.ts new file mode 100644 index 0000000..d8130bb --- /dev/null +++ b/src/app/pet/pet.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/internal/Observable'; +import { HateoasResourceOperation, ResourceCollection } from '@lagoshny/ngx-hateoas-client'; +import {Pet} from "./pet"; + +@Injectable({providedIn: 'root'}) +export class PetService extends HateoasResourceOperation { + + constructor() { + super(Pet); + } + + public findByChip(query: string): Observable> { + return this.searchCollection('findByChip', { params: { text: query } }); + } + + public findByName(query: string): Observable> { + return this.searchCollection('findByName', { params: { text: query } }); + } +} diff --git a/src/app/pet/pet.ts b/src/app/pet/pet.ts new file mode 100644 index 0000000..00eb0b6 --- /dev/null +++ b/src/app/pet/pet.ts @@ -0,0 +1,27 @@ +import { HateoasResource, Resource } from "@lagoshny/ngx-hateoas-client"; + +@HateoasResource('pets') +export class Pet extends Resource { + + uri: string; + chip: string; + name: string; + dateOfBirth: Date; + isAdopted: boolean; + colour: string; + size: number; + sex: string; + race: string; + isDangerous: boolean; + + constructor(values: object = {}) { + super(); + Object.assign(this as any, values); + } + + public get id(): string { + let uriArray = this.uri.split('/'); + return uriArray.pop(); + } + +} \ No newline at end of file From ac21e5773cf0e08cdcee16e9026ceea408888f4c Mon Sep 17 00:00:00 2001 From: PauMAVA Date: Sat, 18 May 2024 14:48:49 +0200 Subject: [PATCH 02/13] Make the pet service generic. Add cats and dogs. --- src/app/pet/cat/cat.service.ts | 12 ++++++++++++ src/app/pet/cat/cat.ts | 9 +++++++++ src/app/pet/dog/dog.service.ts | 12 ++++++++++++ src/app/pet/dog/dog.ts | 9 +++++++++ src/app/pet/pet.service.ts | 14 +++++++------- 5 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 src/app/pet/cat/cat.service.ts create mode 100644 src/app/pet/cat/cat.ts create mode 100644 src/app/pet/dog/dog.service.ts create mode 100644 src/app/pet/dog/dog.ts diff --git a/src/app/pet/cat/cat.service.ts b/src/app/pet/cat/cat.service.ts new file mode 100644 index 0000000..3254cdc --- /dev/null +++ b/src/app/pet/cat/cat.service.ts @@ -0,0 +1,12 @@ +import { Injectable } from "@angular/core"; +import { PetService } from "../pet.service"; +import { Cat } from "./cat"; + +@Injectable({providedIn: 'root'}) +export class CatService extends PetService { + + constructor() { + super(Cat); + } + +} \ No newline at end of file diff --git a/src/app/pet/cat/cat.ts b/src/app/pet/cat/cat.ts new file mode 100644 index 0000000..d0f3ed7 --- /dev/null +++ b/src/app/pet/cat/cat.ts @@ -0,0 +1,9 @@ +import { HateoasResource } from "@lagoshny/ngx-hateoas-client"; +import { Pet } from '../pet'; + +@HateoasResource('cats') +export class Cat extends Pet { + + meowingLevel: number; + +} \ No newline at end of file diff --git a/src/app/pet/dog/dog.service.ts b/src/app/pet/dog/dog.service.ts new file mode 100644 index 0000000..d1d96cd --- /dev/null +++ b/src/app/pet/dog/dog.service.ts @@ -0,0 +1,12 @@ +import { Injectable } from "@angular/core"; +import { PetService } from "../pet.service"; +import { Dog } from "./dog"; + +@Injectable({providedIn: 'root'}) +export class DogService extends PetService { + + constructor() { + super(Dog); + } + +} \ No newline at end of file diff --git a/src/app/pet/dog/dog.ts b/src/app/pet/dog/dog.ts new file mode 100644 index 0000000..6d348b5 --- /dev/null +++ b/src/app/pet/dog/dog.ts @@ -0,0 +1,9 @@ +import { HateoasResource } from "@lagoshny/ngx-hateoas-client"; +import { Pet } from '../pet'; + +@HateoasResource('dogs') +export class Dog extends Pet { + + meowingLevel: number; + +} \ No newline at end of file diff --git a/src/app/pet/pet.service.ts b/src/app/pet/pet.service.ts index d8130bb..34cda86 100644 --- a/src/app/pet/pet.service.ts +++ b/src/app/pet/pet.service.ts @@ -1,20 +1,20 @@ -import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/internal/Observable'; import { HateoasResourceOperation, ResourceCollection } from '@lagoshny/ngx-hateoas-client'; import {Pet} from "./pet"; -@Injectable({providedIn: 'root'}) -export class PetService extends HateoasResourceOperation { +// We don't make pet available directly, we will inject the Cat and Dog services which extend this one. +// This is correct since in the API the Cat +export class PetService extends HateoasResourceOperation { - constructor() { - super(Pet); + constructor(resourceType: { new (): T }) { + super(resourceType); } - public findByChip(query: string): Observable> { + public findByChip(query: string): Observable> { return this.searchCollection('findByChip', { params: { text: query } }); } - public findByName(query: string): Observable> { + public findByName(query: string): Observable> { return this.searchCollection('findByName', { params: { text: query } }); } } From d7c930f1d29c9689aa34bb55d6f717fc492036b0 Mon Sep 17 00:00:00 2001 From: PauMAVA Date: Sat, 18 May 2024 14:56:42 +0200 Subject: [PATCH 03/13] Add dog and cat lists --- .../pet/cat/cat-list/cat-list.component.html | 19 ++++++++ .../pet/cat/cat-list/cat-list.component.ts | 44 +++++++++++++++++++ .../pet/dog/dog-list/dog-list.component.html | 19 ++++++++ .../pet/dog/dog-list/dog-list.component.ts | 44 +++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 src/app/pet/cat/cat-list/cat-list.component.html create mode 100644 src/app/pet/cat/cat-list/cat-list.component.ts create mode 100644 src/app/pet/dog/dog-list/dog-list.component.html create mode 100644 src/app/pet/dog/dog-list/dog-list.component.ts diff --git a/src/app/pet/cat/cat-list/cat-list.component.html b/src/app/pet/cat/cat-list/cat-list.component.html new file mode 100644 index 0000000..8310493 --- /dev/null +++ b/src/app/pet/cat/cat-list/cat-list.component.html @@ -0,0 +1,19 @@ +
+
+
+
+
Name
+ {{cat.name}} +
+
+
Chip
+

{{cat.chip}}

+
+
+
+
+ + + \ No newline at end of file diff --git a/src/app/pet/cat/cat-list/cat-list.component.ts b/src/app/pet/cat/cat-list/cat-list.component.ts new file mode 100644 index 0000000..7df8dd9 --- /dev/null +++ b/src/app/pet/cat/cat-list/cat-list.component.ts @@ -0,0 +1,44 @@ +import {Component, OnInit} from '@angular/core'; +import {Cat} from "../cat"; +import {Router} from "@angular/router"; +import {CatService} from "../cat.service"; +import {PagedResourceCollection} from "@lagoshny/ngx-hateoas-client"; + +@Component({ + selector: 'app-cat-list', + templateUrl: './cat-list.component.html', +}) +export class CatListComponent implements OnInit { + public cats: Cat[] = []; + public pageSize = 5; + public page = 1; + public totalCats = 0; + + constructor( + public router: Router, + private catService: CatService, + ) {} + + ngOnInit(): void { + this.catService + .getPage({ + pageParams: { size: this.pageSize }, + sort: { name: 'ASC' }, + }) + .subscribe((page: PagedResourceCollection) => { + this.cats = page.resources; + this.totalCats = page.totalElements; + }); + } + + changePage(): void { + this.catService + .getPage({ + pageParams: { page: this.page - 1, size: this.pageSize }, + sort: { name: 'ASC' }, + }) + .subscribe( + (page: PagedResourceCollection) => (this.cats = page.resources) + ); + } +} diff --git a/src/app/pet/dog/dog-list/dog-list.component.html b/src/app/pet/dog/dog-list/dog-list.component.html new file mode 100644 index 0000000..c9d0a98 --- /dev/null +++ b/src/app/pet/dog/dog-list/dog-list.component.html @@ -0,0 +1,19 @@ +
+
+
+
+
Name
+ {{dog.name}} +
+
+
Chip
+

{{dog.chip}}

+
+
+
+
+ + + \ No newline at end of file diff --git a/src/app/pet/dog/dog-list/dog-list.component.ts b/src/app/pet/dog/dog-list/dog-list.component.ts new file mode 100644 index 0000000..8015c16 --- /dev/null +++ b/src/app/pet/dog/dog-list/dog-list.component.ts @@ -0,0 +1,44 @@ +import {Component, OnInit} from '@angular/core'; +import {Dog} from "../dog"; +import {Router} from "@angular/router"; +import {DogService} from "../dog.service"; +import {PagedResourceCollection} from "@lagoshny/ngx-hateoas-client"; + +@Component({ + selector: 'app-dog-list', + templateUrl: './dog-list.component.html', +}) +export class DogListComponent implements OnInit { + public dogs: Dog[] = []; + public pageSize = 5; + public page = 1; + public totalDogs = 0; + + constructor( + public router: Router, + private dogService: DogService, + ) {} + + ngOnInit(): void { + this.dogService + .getPage({ + pageParams: { size: this.pageSize }, + sort: { name: 'ASC' }, + }) + .subscribe((page: PagedResourceCollection) => { + this.dogs = page.resources; + this.totalDogs = page.totalElements; + }); + } + + changePage(): void { + this.dogService + .getPage({ + pageParams: { page: this.page - 1, size: this.pageSize }, + sort: { name: 'ASC' }, + }) + .subscribe( + (page: PagedResourceCollection) => (this.dogs = page.resources) + ); + } +} From 3b4a4086a4da8951a330a96f14a1f62f484bd705 Mon Sep 17 00:00:00 2001 From: PauMAVA Date: Sat, 18 May 2024 14:58:38 +0200 Subject: [PATCH 04/13] Add routes and components to app --- src/app/app-routing.module.ts | 4 ++++ src/app/app.module.ts | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index dad05b8..2bd2e5c 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -10,6 +10,8 @@ import { UserEditComponent } from './user/user-edit/user-edit.component'; import { UserDeleteComponent } from './user/user-delete/user-delete.component'; import { ShelterListComponent } from './shelter/shelter-list/shelter-list.component' import {ShelterCreateComponent} from "./shelter/shelter-create/shelter-create.component"; +import { CatListComponent } from './pet/cat/cat-list/cat-list.component'; +import { DogListComponent } from './pet/dog/dog-list/dog-list.component'; const routes: Routes = [ { path: 'users/create', component: UserRegisterComponent}, @@ -22,6 +24,8 @@ const routes: Routes = [ { path: '', redirectTo: 'about', pathMatch: 'full'}, { path: 'shelters', component: ShelterListComponent}, { path: 'shelters/create', component: ShelterCreateComponent}, + { path: 'cats', component: CatListComponent}, + { path: 'dogs', component: DogListComponent}, ]; @NgModule({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 94a04f0..0c3c869 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -28,6 +28,10 @@ import {ShelterService} from "./shelter/shelter.service"; import {ShelterListComponent} from "./shelter/shelter-list/shelter-list.component"; import {ShelterCreateComponent} from "./shelter/shelter-create/shelter-create.component"; import {ShelterDetailComponent} from "./shelter/shelter-detail/shelter-detail.component"; +import { CatListComponent } from './pet/cat/cat-list/cat-list.component'; +import { DogListComponent } from './pet/dog/dog-list/dog-list.component'; +import { CatService } from './pet/cat/cat.service'; +import { DogService } from './pet/dog/dog.service'; @NgModule({ declarations: [ @@ -43,7 +47,9 @@ import {ShelterDetailComponent} from "./shelter/shelter-detail/shelter-detail.co UserSearchComponent, ShelterListComponent, ShelterCreateComponent, - ShelterDetailComponent + ShelterDetailComponent, + CatListComponent, + DogListComponent, ], imports: [ BrowserModule, @@ -57,12 +63,12 @@ import {ShelterDetailComponent} from "./shelter/shelter-detail/shelter-detail.co LoginBasicModule, ErrorHandlerModule, NgbModule, - ReactiveFormsModule + ReactiveFormsModule, ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true }, - AuthenticationBasicService, LoggedInGuard, UserService, ShelterService, provideAnimationsAsync() + AuthenticationBasicService, LoggedInGuard, UserService, ShelterService, CatService, DogService, provideAnimationsAsync() ], bootstrap: [AppComponent] }) From 2d16f2f5d1b17382b3b0945f78bd157f361797f6 Mon Sep 17 00:00:00 2001 From: PauMAVA Date: Sat, 18 May 2024 15:04:55 +0200 Subject: [PATCH 05/13] Add pets menu --- src/app/navbar/navbar.component.html | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index 418ef19..9944b32 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -28,6 +28,18 @@ [routerLink]="['/shelters/create']"> Create + + + From a4a2dc678f8232fce55243ca01e49d7e7c50a2b1 Mon Sep 17 00:00:00 2001 From: PauMAVA Date: Sat, 18 May 2024 17:22:09 +0200 Subject: [PATCH 06/13] Add cat creation page --- src/app/app-routing.module.ts | 2 + src/app/app.module.ts | 2 + .../cat/cat-create/cat-create.component.css | 4 + .../cat/cat-create/cat-create.component.html | 198 ++++++++++++++++++ .../cat/cat-create/cat-create.component.ts | 158 ++++++++++++++ .../pet/cat/cat-list/cat-list.component.html | 11 + 6 files changed, 375 insertions(+) create mode 100644 src/app/pet/cat/cat-create/cat-create.component.css create mode 100644 src/app/pet/cat/cat-create/cat-create.component.html create mode 100644 src/app/pet/cat/cat-create/cat-create.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 2bd2e5c..e775cfc 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -12,6 +12,7 @@ import { ShelterListComponent } from './shelter/shelter-list/shelter-list.compon import {ShelterCreateComponent} from "./shelter/shelter-create/shelter-create.component"; import { CatListComponent } from './pet/cat/cat-list/cat-list.component'; import { DogListComponent } from './pet/dog/dog-list/dog-list.component'; +import { CatCreateComponent } from './pet/cat/cat-create/cat-create.component'; const routes: Routes = [ { path: 'users/create', component: UserRegisterComponent}, @@ -25,6 +26,7 @@ const routes: Routes = [ { path: 'shelters', component: ShelterListComponent}, { path: 'shelters/create', component: ShelterCreateComponent}, { path: 'cats', component: CatListComponent}, + { path: 'cats/create', component: CatCreateComponent}, { path: 'dogs', component: DogListComponent}, ]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 0c3c869..f55473d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -32,6 +32,7 @@ import { CatListComponent } from './pet/cat/cat-list/cat-list.component'; import { DogListComponent } from './pet/dog/dog-list/dog-list.component'; import { CatService } from './pet/cat/cat.service'; import { DogService } from './pet/dog/dog.service'; +import { CatCreateComponent } from './pet/cat/cat-create/cat-create.component'; @NgModule({ declarations: [ @@ -49,6 +50,7 @@ import { DogService } from './pet/dog/dog.service'; ShelterCreateComponent, ShelterDetailComponent, CatListComponent, + CatCreateComponent, DogListComponent, ], imports: [ diff --git a/src/app/pet/cat/cat-create/cat-create.component.css b/src/app/pet/cat/cat-create/cat-create.component.css new file mode 100644 index 0000000..72f16bc --- /dev/null +++ b/src/app/pet/cat/cat-create/cat-create.component.css @@ -0,0 +1,4 @@ +.form-error { + color: red; + font-size: x-small; +} \ No newline at end of file diff --git a/src/app/pet/cat/cat-create/cat-create.component.html b/src/app/pet/cat/cat-create/cat-create.component.html new file mode 100644 index 0000000..1b3dc24 --- /dev/null +++ b/src/app/pet/cat/cat-create/cat-create.component.html @@ -0,0 +1,198 @@ +
+
+
+
+ + +
+ Name is required +
+
+ +
+ + +
+ Chip is required +
+
+ +
+ + +
+ Date of birth is required +
+
+ Invalid date +
+
+ +
+ + +
+ Colour is required +
+
+ Invalid date +
+
+ +
+ + +
+ Size is required +
+
+ Size is invalid +
+
+ +
+ + +
+ Sex is required +
+
+ Sex is invalid +
+
+ +
+ + +
+ Race is required +
+
+ +
+ + +
+ +
+ + +
+ Meowing level is required +
+
+ +
+ + +
+ + +
+
+
diff --git a/src/app/pet/cat/cat-create/cat-create.component.ts b/src/app/pet/cat/cat-create/cat-create.component.ts new file mode 100644 index 0000000..7301b43 --- /dev/null +++ b/src/app/pet/cat/cat-create/cat-create.component.ts @@ -0,0 +1,158 @@ +import {Component, OnInit} from '@angular/core'; +import {Router} from "@angular/router"; +import {CatService} from "../cat.service"; +import {Cat} from "../cat"; +import {AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from "@angular/forms"; +import {PagedResourceCollection} from "@lagoshny/ngx-hateoas-client"; + +@Component({ + selector: 'app-cat-create', + templateUrl: './cat-create.component.html', + styleUrls: ['./cat-create.component.css'] +}) +export class CatCreateComponent implements OnInit{ + + closeResult = ''; + public isModalSaved: boolean = false; + public cats: Cat[] = []; + public cat: Cat; + public commonNameInput: string = ''; + public commonEmailsList: any = []; + public catForm: FormGroup; + + constructor( + private router: Router, + private catService: CatService, + ) {} + + ngOnInit(): void { + this.cat = new Cat(); + this.catForm = new FormGroup({ + chip: new FormControl(this.cat.chip, [ + Validators.required, + ]), + name: new FormControl(this.cat.name, [ + Validators.required, + ]), + dateOfBirth: new FormControl(this.cat.dateOfBirth, [ + Validators.required, + this.dateValidator(), + ]), + colour: new FormControl(this.cat.colour, [ + Validators.required, + this.colourValidator(), + ]), + size: new FormControl(this.cat.size, [ + Validators.required, + this.numberValidator(), + ]), + sex: new FormControl(this.cat.sex, [ + Validators.required, + this.sexValidator(), + ]), + race: new FormControl(this.cat.race, [ + Validators.required, + ]), + isDangerous: new FormControl(this.cat.isDangerous, []), + meowingLevel: new FormControl(this.cat.meowingLevel, [ + Validators.required, + ]), + }); + this.loadCatList(); + } + + loadCatList() { + this.catService + .getPage({ + sort: { name: 'ASC' }, + }) + .subscribe((cats: PagedResourceCollection) => { + this.cats = cats.resources.sort((a, b) => + a.chip.localeCompare(b.chip) + ); + }); + } + + dateValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const dateValue = control.value; + const isValidDate = !isNaN(Date.parse(dateValue)); + return isValidDate ? null : { invalidDate: { value: control.value } }; + }; + } + + colourValidator(): ValidatorFn { + const hexColorRe: RegExp = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; + return (control: AbstractControl): ValidationErrors | null => { + const isValidColour = hexColorRe.test(control.value); + return isValidColour ? null : { invalidColour: { value: control.value } }; + }; + } + + numberValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const numberValue = control.value; + const isValidNumber = !isNaN(numberValue) && numberValue > 0; + return isValidNumber ? null : { invalidNumber: { value: control.value } }; + }; + } + + sexValidator(): ValidatorFn { + const validSexes = ['male', 'female']; + return (control: AbstractControl): ValidationErrors | null => { + const isValidSex = control.value !== null && control.value !== undefined && validSexes.includes(control.value.toLowerCase()); + return isValidSex ? null : { invalidSex: { value: control.value } }; + }; + } + + get chip() { + return this.catForm.get('chip'); + } + + get name() { + return this.catForm.get('name'); + } + + get dateOfBirth() { + return this.catForm.get('dateOfBirth'); + } + + get isAdopted() { + return this.catForm.get('isAdopted'); + } + + get colour() { + return this.catForm.get('colour'); + } + + get size() { + return this.catForm.get('size'); + } + + get sex() { + return this.catForm.get('sex'); + } + + get race() { + return this.catForm.get('race'); + } + + get isDangerous() { + return this.catForm.get('isDangerous'); + } + + get meowingLevel() { + return this.catForm.get('meowingLevel'); + } + + onSubmit(): void { + this.cat.isAdopted = false; + this.catService + .createResource({ body: this.cat }) + .subscribe((cat: Cat) => { + const uri = (cat as any).uri; + this.router.navigate([uri]).then(); + }); + } + +} diff --git a/src/app/pet/cat/cat-list/cat-list.component.html b/src/app/pet/cat/cat-list/cat-list.component.html index 8310493..70555ae 100644 --- a/src/app/pet/cat/cat-list/cat-list.component.html +++ b/src/app/pet/cat/cat-list/cat-list.component.html @@ -1,3 +1,14 @@ +
+ +
From ad2418bd0e1d0c087a3e00bffb64c7dc32d5b5f0 Mon Sep 17 00:00:00 2001 From: PauMAVA Date: Sat, 18 May 2024 17:50:28 +0200 Subject: [PATCH 07/13] Add cat detail --- src/app/app-routing.module.ts | 8 ++- src/app/app.module.ts | 2 + .../cat/cat-create/cat-create.component.html | 10 +-- .../cat/cat-create/cat-create.component.ts | 12 ++-- .../cat/cat-detail/cat-detail.component.html | 63 +++++++++++++++++++ .../cat/cat-detail/cat-detail.component.ts | 25 ++++++++ .../pet/cat/cat-list/cat-list.component.html | 32 ++++++++-- src/app/pet/pet.ts | 4 +- 8 files changed, 135 insertions(+), 21 deletions(-) create mode 100644 src/app/pet/cat/cat-detail/cat-detail.component.html create mode 100644 src/app/pet/cat/cat-detail/cat-detail.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index e775cfc..3a71692 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -13,6 +13,7 @@ import {ShelterCreateComponent} from "./shelter/shelter-create/shelter-create.co import { CatListComponent } from './pet/cat/cat-list/cat-list.component'; import { DogListComponent } from './pet/dog/dog-list/dog-list.component'; import { CatCreateComponent } from './pet/cat/cat-create/cat-create.component'; +import { CatDetailComponent } from './pet/cat/cat-detail/cat-detail.component'; const routes: Routes = [ { path: 'users/create', component: UserRegisterComponent}, @@ -25,9 +26,10 @@ const routes: Routes = [ { path: '', redirectTo: 'about', pathMatch: 'full'}, { path: 'shelters', component: ShelterListComponent}, { path: 'shelters/create', component: ShelterCreateComponent}, - { path: 'cats', component: CatListComponent}, - { path: 'cats/create', component: CatCreateComponent}, - { path: 'dogs', component: DogListComponent}, + { path: 'cats', component: CatListComponent, canActivate: [LoggedInGuard]}, + { path: 'cats/create', component: CatCreateComponent, canActivate: [LoggedInGuard]}, + { path: 'cats/:id', component: CatDetailComponent, canActivate: [LoggedInGuard]}, + { path: 'dogs', component: DogListComponent, canActivate: [LoggedInGuard]}, ]; @NgModule({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index f55473d..f1b41e3 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -33,6 +33,7 @@ import { DogListComponent } from './pet/dog/dog-list/dog-list.component'; import { CatService } from './pet/cat/cat.service'; import { DogService } from './pet/dog/dog.service'; import { CatCreateComponent } from './pet/cat/cat-create/cat-create.component'; +import { CatDetailComponent } from './pet/cat/cat-detail/cat-detail.component'; @NgModule({ declarations: [ @@ -51,6 +52,7 @@ import { CatCreateComponent } from './pet/cat/cat-create/cat-create.component'; ShelterDetailComponent, CatListComponent, CatCreateComponent, + CatDetailComponent, DogListComponent, ], imports: [ diff --git a/src/app/pet/cat/cat-create/cat-create.component.html b/src/app/pet/cat/cat-create/cat-create.component.html index 1b3dc24..906d1fd 100644 --- a/src/app/pet/cat/cat-create/cat-create.component.html +++ b/src/app/pet/cat/cat-create/cat-create.component.html @@ -142,14 +142,14 @@
- +
diff --git a/src/app/pet/cat/cat-create/cat-create.component.ts b/src/app/pet/cat/cat-create/cat-create.component.ts index 7301b43..267fc00 100644 --- a/src/app/pet/cat/cat-create/cat-create.component.ts +++ b/src/app/pet/cat/cat-create/cat-create.component.ts @@ -53,7 +53,7 @@ export class CatCreateComponent implements OnInit{ race: new FormControl(this.cat.race, [ Validators.required, ]), - isDangerous: new FormControl(this.cat.isDangerous, []), + dangerous: new FormControl(this.cat.dangerous, []), meowingLevel: new FormControl(this.cat.meowingLevel, [ Validators.required, ]), @@ -117,8 +117,8 @@ export class CatCreateComponent implements OnInit{ return this.catForm.get('dateOfBirth'); } - get isAdopted() { - return this.catForm.get('isAdopted'); + get adopted() { + return this.catForm.get('adopted'); } get colour() { @@ -137,8 +137,8 @@ export class CatCreateComponent implements OnInit{ return this.catForm.get('race'); } - get isDangerous() { - return this.catForm.get('isDangerous'); + get dangerous() { + return this.catForm.get('dangerous'); } get meowingLevel() { @@ -146,7 +146,7 @@ export class CatCreateComponent implements OnInit{ } onSubmit(): void { - this.cat.isAdopted = false; + this.cat.adopted = false; this.catService .createResource({ body: this.cat }) .subscribe((cat: Cat) => { diff --git a/src/app/pet/cat/cat-detail/cat-detail.component.html b/src/app/pet/cat/cat-detail/cat-detail.component.html new file mode 100644 index 0000000..d8a9458 --- /dev/null +++ b/src/app/pet/cat/cat-detail/cat-detail.component.html @@ -0,0 +1,63 @@ +
+
+

{{cat.name}}

+
+
+
Chip
+

{{cat.chip}}

+
+
+
Date of birth
+

{{cat.dateOfBirth}}

+
+
+
Colour
+
+
+
+
Size
+

Small

+

Average

+

Large

+

Extra Large

+
+
+
Sex
+

Male

+

Female

+
+
+
Race
+

{{cat.race}}

+
+
+
Dangerous
+ + +
+
+
Adopted
+ + +
+
+
Meowing level
+

No meowing

+

Low meowing

+

Average meowing

+

Loud meowing

+
+
+ +
+
\ No newline at end of file diff --git a/src/app/pet/cat/cat-detail/cat-detail.component.ts b/src/app/pet/cat/cat-detail/cat-detail.component.ts new file mode 100644 index 0000000..21703a7 --- /dev/null +++ b/src/app/pet/cat/cat-detail/cat-detail.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { CatService } from '../cat.service'; +import { Cat } from '../cat'; + +@Component({ + selector: 'app-cat-detail', + templateUrl: './cat-detail.component.html' +}) +export class CatDetailComponent implements OnInit { + public cat: Cat = new Cat(); + + constructor(private route: ActivatedRoute, + private catService: CatService) { + } + + ngOnInit(): void { + const id = this.route.snapshot.paramMap.get('id'); + this.catService.getResource(id).subscribe( + cat => { + this.cat = cat; + }); + } + +} diff --git a/src/app/pet/cat/cat-list/cat-list.component.html b/src/app/pet/cat/cat-list/cat-list.component.html index 70555ae..6b21f65 100644 --- a/src/app/pet/cat/cat-list/cat-list.component.html +++ b/src/app/pet/cat/cat-list/cat-list.component.html @@ -12,13 +12,35 @@
-
+
+
Chip
+

{{cat.chip}}

+
+
Name
- {{cat.name}} +

{{cat.name}}

-
-
Chip
-

{{cat.chip}}

+
+
Date of birth
+

{{cat.dateOfBirth}}

+
+
+ +
diff --git a/src/app/pet/pet.ts b/src/app/pet/pet.ts index 00eb0b6..aaeb127 100644 --- a/src/app/pet/pet.ts +++ b/src/app/pet/pet.ts @@ -7,12 +7,12 @@ export class Pet extends Resource { chip: string; name: string; dateOfBirth: Date; - isAdopted: boolean; + adopted: boolean; colour: string; size: number; sex: string; race: string; - isDangerous: boolean; + dangerous: boolean; constructor(values: object = {}) { super(); From 1a373e9e29c572410a7d0dd0e660ce5a04549624 Mon Sep 17 00:00:00 2001 From: PauMAVA Date: Sat, 18 May 2024 18:15:33 +0200 Subject: [PATCH 08/13] Add edit cat and multiple improvements --- src/app/app-routing.module.ts | 2 + src/app/app.module.ts | 2 + .../cat/cat-create/cat-create.component.ts | 2 - .../cat/cat-detail/cat-detail.component.html | 4 +- .../pet/cat/cat-edit/cat-edit.component.html | 198 ++++++++++++++++++ .../pet/cat/cat-edit/cat-edit.component.ts | 139 ++++++++++++ .../pet/cat/cat-list/cat-list.component.html | 2 +- 7 files changed, 344 insertions(+), 5 deletions(-) create mode 100644 src/app/pet/cat/cat-edit/cat-edit.component.html create mode 100644 src/app/pet/cat/cat-edit/cat-edit.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 3a71692..e978470 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -14,6 +14,7 @@ import { CatListComponent } from './pet/cat/cat-list/cat-list.component'; import { DogListComponent } from './pet/dog/dog-list/dog-list.component'; import { CatCreateComponent } from './pet/cat/cat-create/cat-create.component'; import { CatDetailComponent } from './pet/cat/cat-detail/cat-detail.component'; +import { CatEditComponent } from './pet/cat/cat-edit/cat-edit.component'; const routes: Routes = [ { path: 'users/create', component: UserRegisterComponent}, @@ -29,6 +30,7 @@ const routes: Routes = [ { path: 'cats', component: CatListComponent, canActivate: [LoggedInGuard]}, { path: 'cats/create', component: CatCreateComponent, canActivate: [LoggedInGuard]}, { path: 'cats/:id', component: CatDetailComponent, canActivate: [LoggedInGuard]}, + { path: 'cats/edit/:id', component: CatEditComponent, canActivate: [LoggedInGuard]}, { path: 'dogs', component: DogListComponent, canActivate: [LoggedInGuard]}, ]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index f1b41e3..816e245 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -34,6 +34,7 @@ import { CatService } from './pet/cat/cat.service'; import { DogService } from './pet/dog/dog.service'; import { CatCreateComponent } from './pet/cat/cat-create/cat-create.component'; import { CatDetailComponent } from './pet/cat/cat-detail/cat-detail.component'; +import { CatEditComponent } from './pet/cat/cat-edit/cat-edit.component'; @NgModule({ declarations: [ @@ -53,6 +54,7 @@ import { CatDetailComponent } from './pet/cat/cat-detail/cat-detail.component'; CatListComponent, CatCreateComponent, CatDetailComponent, + CatEditComponent, DogListComponent, ], imports: [ diff --git a/src/app/pet/cat/cat-create/cat-create.component.ts b/src/app/pet/cat/cat-create/cat-create.component.ts index 267fc00..2102a8a 100644 --- a/src/app/pet/cat/cat-create/cat-create.component.ts +++ b/src/app/pet/cat/cat-create/cat-create.component.ts @@ -16,8 +16,6 @@ export class CatCreateComponent implements OnInit{ public isModalSaved: boolean = false; public cats: Cat[] = []; public cat: Cat; - public commonNameInput: string = ''; - public commonEmailsList: any = []; public catForm: FormGroup; constructor( diff --git a/src/app/pet/cat/cat-detail/cat-detail.component.html b/src/app/pet/cat/cat-detail/cat-detail.component.html index d8a9458..58ee7ca 100644 --- a/src/app/pet/cat/cat-detail/cat-detail.component.html +++ b/src/app/pet/cat/cat-detail/cat-detail.component.html @@ -52,10 +52,10 @@
Meowing level
- -
diff --git a/src/app/pet/cat/cat-edit/cat-edit.component.html b/src/app/pet/cat/cat-edit/cat-edit.component.html new file mode 100644 index 0000000..9bb4d80 --- /dev/null +++ b/src/app/pet/cat/cat-edit/cat-edit.component.html @@ -0,0 +1,198 @@ +
+
+
+
+ + +
+ Name is required +
+
+ +
+ + +
+ Chip is required +
+
+ +
+ + +
+ Date of birth is required +
+
+ Invalid date +
+
+ +
+ + +
+ Colour is required +
+
+ Invalid date +
+
+ +
+ + +
+ Size is required +
+
+ Size is invalid +
+
+ +
+ + +
+ Sex is required +
+
+ Sex is invalid +
+
+ +
+ + +
+ Race is required +
+
+ +
+ + +
+ +
+ + +
+ Meowing level is required +
+
+ +
+ + +
+ + +
+
+
diff --git a/src/app/pet/cat/cat-edit/cat-edit.component.ts b/src/app/pet/cat/cat-edit/cat-edit.component.ts new file mode 100644 index 0000000..3d73822 --- /dev/null +++ b/src/app/pet/cat/cat-edit/cat-edit.component.ts @@ -0,0 +1,139 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Router } from '@angular/router'; +import { Cat } from '../cat'; +import { CatService } from '../cat.service'; +import { AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; + +@Component({ + selector: 'app-cat-edit', + templateUrl: './cat-edit.component.html' +}) +export class CatEditComponent implements OnInit { + public cat: Cat = new Cat(); + public catForm: FormGroup; + + constructor(private route: ActivatedRoute, + private router: Router, + private catService: CatService) { + } + + ngOnInit(): void { + const id = this.route.snapshot.paramMap.get('id'); + this.catService.getResource(id).subscribe( + (cat: Cat) => this.cat = cat ); + this.catForm = new FormGroup({ + chip: new FormControl(this.cat.chip, [ + Validators.required, + ]), + name: new FormControl(this.cat.name, [ + Validators.required, + ]), + dateOfBirth: new FormControl(this.cat.dateOfBirth, [ + Validators.required, + this.dateValidator(), + ]), + colour: new FormControl(this.cat.colour, [ + Validators.required, + this.colourValidator(), + ]), + size: new FormControl(this.cat.size, [ + Validators.required, + this.numberValidator(), + ]), + sex: new FormControl(this.cat.sex, [ + Validators.required, + this.sexValidator(), + ]), + race: new FormControl(this.cat.race, [ + Validators.required, + ]), + dangerous: new FormControl(this.cat.dangerous, []), + meowingLevel: new FormControl(this.cat.meowingLevel, [ + Validators.required, + ]), + }); + } + + + dateValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const dateValue = control.value; + const isValidDate = !isNaN(Date.parse(dateValue)); + return isValidDate ? null : { invalidDate: { value: control.value } }; + }; + } + + colourValidator(): ValidatorFn { + const hexColorRe: RegExp = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; + return (control: AbstractControl): ValidationErrors | null => { + const isValidColour = hexColorRe.test(control.value); + return isValidColour ? null : { invalidColour: { value: control.value } }; + }; + } + + numberValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const numberValue = control.value; + const isValidNumber = !isNaN(numberValue) && numberValue > 0; + return isValidNumber ? null : { invalidNumber: { value: control.value } }; + }; + } + + sexValidator(): ValidatorFn { + const validSexes = ['male', 'female']; + return (control: AbstractControl): ValidationErrors | null => { + const isValidSex = control.value !== null && control.value !== undefined && validSexes.includes(control.value.toLowerCase()); + return isValidSex ? null : { invalidSex: { value: control.value } }; + }; + } + + get chip() { + return this.catForm.get('chip'); + } + + get name() { + return this.catForm.get('name'); + } + + get dateOfBirth() { + return this.catForm.get('dateOfBirth'); + } + + get adopted() { + return this.catForm.get('adopted'); + } + + get colour() { + return this.catForm.get('colour'); + } + + get size() { + return this.catForm.get('size'); + } + + get sex() { + return this.catForm.get('sex'); + } + + get race() { + return this.catForm.get('race'); + } + + get dangerous() { + return this.catForm.get('dangerous'); + } + + get meowingLevel() { + return this.catForm.get('meowingLevel'); + } + + + onSubmit(): void { + this.catService.patchResource(this.cat).subscribe( + (patchedCat: Cat) => { + this.router.navigate(['cats', patchedCat.id]); + }); + } + +} diff --git a/src/app/pet/cat/cat-list/cat-list.component.html b/src/app/pet/cat/cat-list/cat-list.component.html index 6b21f65..fa9f806 100644 --- a/src/app/pet/cat/cat-list/cat-list.component.html +++ b/src/app/pet/cat/cat-list/cat-list.component.html @@ -14,7 +14,7 @@
Chip
-

{{cat.chip}}

+

{{cat.chip}}

Name
From f84c2176fceb2a2726c97dca45ab2d70c0a85a2a Mon Sep 17 00:00:00 2001 From: PauMAVA Date: Sat, 18 May 2024 18:22:14 +0200 Subject: [PATCH 09/13] Add cat deletion --- src/app/app-routing.module.ts | 4 ++- src/app/app.module.ts | 2 ++ .../cat/cat-delete/cat-delete.component.html | 16 ++++++++++ .../cat/cat-delete/cat-delete.component.ts | 31 +++++++++++++++++++ .../cat/cat-detail/cat-detail.component.html | 4 +-- .../pet/cat/cat-edit/cat-edit.component.html | 2 +- .../pet/cat/cat-list/cat-list.component.html | 2 +- 7 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 src/app/pet/cat/cat-delete/cat-delete.component.html create mode 100644 src/app/pet/cat/cat-delete/cat-delete.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index e978470..46c9583 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -15,6 +15,7 @@ import { DogListComponent } from './pet/dog/dog-list/dog-list.component'; import { CatCreateComponent } from './pet/cat/cat-create/cat-create.component'; import { CatDetailComponent } from './pet/cat/cat-detail/cat-detail.component'; import { CatEditComponent } from './pet/cat/cat-edit/cat-edit.component'; +import { CatDeleteComponent } from './pet/cat/cat-delete/cat-delete.component'; const routes: Routes = [ { path: 'users/create', component: UserRegisterComponent}, @@ -30,7 +31,8 @@ const routes: Routes = [ { path: 'cats', component: CatListComponent, canActivate: [LoggedInGuard]}, { path: 'cats/create', component: CatCreateComponent, canActivate: [LoggedInGuard]}, { path: 'cats/:id', component: CatDetailComponent, canActivate: [LoggedInGuard]}, - { path: 'cats/edit/:id', component: CatEditComponent, canActivate: [LoggedInGuard]}, + { path: 'cats/:id/edit', component: CatEditComponent, canActivate: [LoggedInGuard]}, + { path: 'cats/:id/delete', component: CatDeleteComponent, canActivate: [LoggedInGuard]}, { path: 'dogs', component: DogListComponent, canActivate: [LoggedInGuard]}, ]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 816e245..3fcb07a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -35,6 +35,7 @@ import { DogService } from './pet/dog/dog.service'; import { CatCreateComponent } from './pet/cat/cat-create/cat-create.component'; import { CatDetailComponent } from './pet/cat/cat-detail/cat-detail.component'; import { CatEditComponent } from './pet/cat/cat-edit/cat-edit.component'; +import { CatDeleteComponent } from './pet/cat/cat-delete/cat-delete.component'; @NgModule({ declarations: [ @@ -54,6 +55,7 @@ import { CatEditComponent } from './pet/cat/cat-edit/cat-edit.component'; CatListComponent, CatCreateComponent, CatDetailComponent, + CatDeleteComponent, CatEditComponent, DogListComponent, ], diff --git a/src/app/pet/cat/cat-delete/cat-delete.component.html b/src/app/pet/cat/cat-delete/cat-delete.component.html new file mode 100644 index 0000000..3a3ade3 --- /dev/null +++ b/src/app/pet/cat/cat-delete/cat-delete.component.html @@ -0,0 +1,16 @@ +
+
+
+

Are you sure you want to delete this record?

+
+
+
+
+ + +
+
+
+ \ No newline at end of file diff --git a/src/app/pet/cat/cat-delete/cat-delete.component.ts b/src/app/pet/cat/cat-delete/cat-delete.component.ts new file mode 100644 index 0000000..3929b77 --- /dev/null +++ b/src/app/pet/cat/cat-delete/cat-delete.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Cat } from '../cat'; +import { CatService } from '../cat.service'; + +@Component({ + selector: 'app-cat-delete', + templateUrl: './cat-delete.component.html' +}) +export class CatDeleteComponent implements OnInit { + public cat: Cat = new Cat(); + private id: string; + + constructor(private route: ActivatedRoute, + private router: Router, + private catService: CatService) { + } + + ngOnInit(): void { + this.id = this.route.snapshot.paramMap.get('id'); + this.catService.getResource(this.id).subscribe( + cat => this.cat = cat); + } + + delete(): void { + this.catService.deleteResource(this.cat).subscribe( + () => { + this.router.navigate(['cats']); + }); + } +} diff --git a/src/app/pet/cat/cat-detail/cat-detail.component.html b/src/app/pet/cat/cat-detail/cat-detail.component.html index 58ee7ca..d8a9458 100644 --- a/src/app/pet/cat/cat-detail/cat-detail.component.html +++ b/src/app/pet/cat/cat-detail/cat-detail.component.html @@ -52,10 +52,10 @@
Meowing level
- -
diff --git a/src/app/pet/cat/cat-edit/cat-edit.component.html b/src/app/pet/cat/cat-edit/cat-edit.component.html index 9bb4d80..15c80f8 100644 --- a/src/app/pet/cat/cat-edit/cat-edit.component.html +++ b/src/app/pet/cat/cat-edit/cat-edit.component.html @@ -179,7 +179,7 @@
+ + +
+ + diff --git a/src/app/pet/dog/dog-create/dog-create.component.ts b/src/app/pet/dog/dog-create/dog-create.component.ts new file mode 100644 index 0000000..a6fdbf0 --- /dev/null +++ b/src/app/pet/dog/dog-create/dog-create.component.ts @@ -0,0 +1,156 @@ +import {Component, OnInit} from '@angular/core'; +import {Router} from "@angular/router"; +import {DogService} from "../dog.service"; +import {Dog} from "../dog"; +import {AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from "@angular/forms"; +import {PagedResourceCollection} from "@lagoshny/ngx-hateoas-client"; + +@Component({ + selector: 'app-dog-create', + templateUrl: './dog-create.component.html', + styleUrls: ['./dog-create.component.css'] +}) +export class DogCreateComponent implements OnInit{ + + closeResult = ''; + public isModalSaved: boolean = false; + public dogs: Dog[] = []; + public dog: Dog; + public dogForm: FormGroup; + + constructor( + private router: Router, + private dogService: DogService, + ) {} + + ngOnInit(): void { + this.dog = new Dog(); + this.dogForm = new FormGroup({ + chip: new FormControl(this.dog.chip, [ + Validators.required, + ]), + name: new FormControl(this.dog.name, [ + Validators.required, + ]), + dateOfBirth: new FormControl(this.dog.dateOfBirth, [ + Validators.required, + this.dateValidator(), + ]), + colour: new FormControl(this.dog.colour, [ + Validators.required, + this.colourValidator(), + ]), + size: new FormControl(this.dog.size, [ + Validators.required, + this.numberValidator(), + ]), + sex: new FormControl(this.dog.sex, [ + Validators.required, + this.sexValidator(), + ]), + race: new FormControl(this.dog.race, [ + Validators.required, + ]), + dangerous: new FormControl(this.dog.dangerous, []), + barkingLevel: new FormControl(this.dog.barkingLevel, [ + Validators.required, + ]), + }); + this.loadDogList(); + } + + loadDogList() { + this.dogService + .getPage({ + sort: { name: 'ASC' }, + }) + .subscribe((dogs: PagedResourceCollection) => { + this.dogs = dogs.resources.sort((a, b) => + a.chip.localeCompare(b.chip) + ); + }); + } + + dateValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const dateValue = control.value; + const isValidDate = !isNaN(Date.parse(dateValue)); + return isValidDate ? null : { invalidDate: { value: control.value } }; + }; + } + + colourValidator(): ValidatorFn { + const hexColorRe: RegExp = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; + return (control: AbstractControl): ValidationErrors | null => { + const isValidColour = hexColorRe.test(control.value); + return isValidColour ? null : { invalidColour: { value: control.value } }; + }; + } + + numberValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const numberValue = control.value; + const isValidNumber = !isNaN(numberValue) && numberValue > 0; + return isValidNumber ? null : { invalidNumber: { value: control.value } }; + }; + } + + sexValidator(): ValidatorFn { + const validSexes = ['male', 'female']; + return (control: AbstractControl): ValidationErrors | null => { + const isValidSex = control.value !== null && control.value !== undefined && validSexes.includes(control.value.toLowerCase()); + return isValidSex ? null : { invalidSex: { value: control.value } }; + }; + } + + get chip() { + return this.dogForm.get('chip'); + } + + get name() { + return this.dogForm.get('name'); + } + + get dateOfBirth() { + return this.dogForm.get('dateOfBirth'); + } + + get adopted() { + return this.dogForm.get('adopted'); + } + + get colour() { + return this.dogForm.get('colour'); + } + + get size() { + return this.dogForm.get('size'); + } + + get sex() { + return this.dogForm.get('sex'); + } + + get race() { + return this.dogForm.get('race'); + } + + get dangerous() { + return this.dogForm.get('dangerous'); + } + + get barkingLevel() { + return this.dogForm.get('barkingLevel'); + } + + onSubmit(): void { + this.dog.adopted = false; + this.dogService + .createResource({ body: this.dog }) + .subscribe((dog: Dog) => { + const uri = (dog as any).uri; + this.router.navigate([uri]).then(); + }); + } + +} diff --git a/src/app/pet/dog/dog-delete/dog-delete.component.html b/src/app/pet/dog/dog-delete/dog-delete.component.html new file mode 100644 index 0000000..ee8f0b6 --- /dev/null +++ b/src/app/pet/dog/dog-delete/dog-delete.component.html @@ -0,0 +1,16 @@ +
+
+
+

Are you sure you want to delete this record?

+
+
+
+
+ + +
+
+
+ \ No newline at end of file diff --git a/src/app/pet/dog/dog-delete/dog-delete.component.ts b/src/app/pet/dog/dog-delete/dog-delete.component.ts new file mode 100644 index 0000000..2417326 --- /dev/null +++ b/src/app/pet/dog/dog-delete/dog-delete.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Dog } from '../dog'; +import { DogService } from '../dog.service'; + +@Component({ + selector: 'app-dog-delete', + templateUrl: './dog-delete.component.html' +}) +export class DogDeleteComponent implements OnInit { + public dog: Dog = new Dog(); + private id: string; + + constructor(private route: ActivatedRoute, + private router: Router, + private dogService: DogService) { + } + + ngOnInit(): void { + this.id = this.route.snapshot.paramMap.get('id'); + this.dogService.getResource(this.id).subscribe( + dog => this.dog = dog); + } + + delete(): void { + this.dogService.deleteResource(this.dog).subscribe( + () => { + this.router.navigate(['dogs']); + }); + } +} diff --git a/src/app/pet/dog/dog-detail/dog-detail.component.html b/src/app/pet/dog/dog-detail/dog-detail.component.html new file mode 100644 index 0000000..393202d --- /dev/null +++ b/src/app/pet/dog/dog-detail/dog-detail.component.html @@ -0,0 +1,63 @@ +
+
+

{{dog.name}}

+
+
+
Chip
+

{{dog.chip}}

+
+
+
Date of birth
+

{{dog.dateOfBirth}}

+
+
+
Colour
+
+
+
+
Size
+

Small

+

Average

+

Large

+

Extra Large

+
+
+
Sex
+

Male

+

Female

+
+
+
Race
+

{{dog.race}}

+
+
+
Dangerous
+ + +
+
+
Adopted
+ + +
+
+
Barking level
+

No barking

+

Low barking

+

Average barking

+

Loud barking

+
+
+ +
+
\ No newline at end of file diff --git a/src/app/pet/dog/dog-detail/dog-detail.component.ts b/src/app/pet/dog/dog-detail/dog-detail.component.ts new file mode 100644 index 0000000..6e0bb88 --- /dev/null +++ b/src/app/pet/dog/dog-detail/dog-detail.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { DogService } from '../dog.service'; +import { Dog } from '../dog'; + +@Component({ + selector: 'app-dog-detail', + templateUrl: './dog-detail.component.html' +}) +export class DogDetailComponent implements OnInit { + public dog: Dog = new Dog(); + + constructor(private route: ActivatedRoute, + private dogService: DogService) { + } + + ngOnInit(): void { + const id = this.route.snapshot.paramMap.get('id'); + this.dogService.getResource(id).subscribe( + dog => { + this.dog = dog; + }); + } + +} diff --git a/src/app/pet/dog/dog-edit/dog-edit.component.html b/src/app/pet/dog/dog-edit/dog-edit.component.html new file mode 100644 index 0000000..7472239 --- /dev/null +++ b/src/app/pet/dog/dog-edit/dog-edit.component.html @@ -0,0 +1,198 @@ +
+
+
+
+ + +
+ Name is required +
+
+ +
+ + +
+ Chip is required +
+
+ +
+ + +
+ Date of birth is required +
+
+ Invalid date +
+
+ +
+ + +
+ Colour is required +
+
+ Invalid date +
+
+ +
+ + +
+ Size is required +
+
+ Size is invalid +
+
+ +
+ + +
+ Sex is required +
+
+ Sex is invalid +
+
+ +
+ + +
+ Race is required +
+
+ +
+ + +
+ +
+ + +
+ Barking level is required +
+
+ +
+ + +
+ + +
+
+
diff --git a/src/app/pet/dog/dog-edit/dog-edit.component.ts b/src/app/pet/dog/dog-edit/dog-edit.component.ts new file mode 100644 index 0000000..bec45db --- /dev/null +++ b/src/app/pet/dog/dog-edit/dog-edit.component.ts @@ -0,0 +1,139 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Router } from '@angular/router'; +import { Dog } from '../dog'; +import { DogService } from '../dog.service'; +import { AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; + +@Component({ + selector: 'app-dog-edit', + templateUrl: './dog-edit.component.html' +}) +export class DogEditComponent implements OnInit { + public dog: Dog = new Dog(); + public dogForm: FormGroup; + + constructor(private route: ActivatedRoute, + private router: Router, + private dogService: DogService) { + } + + ngOnInit(): void { + const id = this.route.snapshot.paramMap.get('id'); + this.dogService.getResource(id).subscribe( + (dog: Dog) => this.dog = dog ); + this.dogForm = new FormGroup({ + chip: new FormControl(this.dog.chip, [ + Validators.required, + ]), + name: new FormControl(this.dog.name, [ + Validators.required, + ]), + dateOfBirth: new FormControl(this.dog.dateOfBirth, [ + Validators.required, + this.dateValidator(), + ]), + colour: new FormControl(this.dog.colour, [ + Validators.required, + this.colourValidator(), + ]), + size: new FormControl(this.dog.size, [ + Validators.required, + this.numberValidator(), + ]), + sex: new FormControl(this.dog.sex, [ + Validators.required, + this.sexValidator(), + ]), + race: new FormControl(this.dog.race, [ + Validators.required, + ]), + dangerous: new FormControl(this.dog.dangerous, []), + barkingLevel: new FormControl(this.dog.barkingLevel, [ + Validators.required, + ]), + }); + } + + + dateValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const dateValue = control.value; + const isValidDate = !isNaN(Date.parse(dateValue)); + return isValidDate ? null : { invalidDate: { value: control.value } }; + }; + } + + colourValidator(): ValidatorFn { + const hexColorRe: RegExp = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; + return (control: AbstractControl): ValidationErrors | null => { + const isValidColour = hexColorRe.test(control.value); + return isValidColour ? null : { invalidColour: { value: control.value } }; + }; + } + + numberValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const numberValue = control.value; + const isValidNumber = !isNaN(numberValue) && numberValue > 0; + return isValidNumber ? null : { invalidNumber: { value: control.value } }; + }; + } + + sexValidator(): ValidatorFn { + const validSexes = ['male', 'female']; + return (control: AbstractControl): ValidationErrors | null => { + const isValidSex = control.value !== null && control.value !== undefined && validSexes.includes(control.value.toLowerCase()); + return isValidSex ? null : { invalidSex: { value: control.value } }; + }; + } + + get chip() { + return this.dogForm.get('chip'); + } + + get name() { + return this.dogForm.get('name'); + } + + get dateOfBirth() { + return this.dogForm.get('dateOfBirth'); + } + + get adopted() { + return this.dogForm.get('adopted'); + } + + get colour() { + return this.dogForm.get('colour'); + } + + get size() { + return this.dogForm.get('size'); + } + + get sex() { + return this.dogForm.get('sex'); + } + + get race() { + return this.dogForm.get('race'); + } + + get dangerous() { + return this.dogForm.get('dangerous'); + } + + get barkingLevel() { + return this.dogForm.get('barkingLevel'); + } + + + onSubmit(): void { + this.dogService.patchResource(this.dog).subscribe( + (patchedDog: Dog) => { + this.router.navigate(['dogs', patchedDog.id]); + }); + } + +} diff --git a/src/app/pet/dog/dog-list/dog-list.component.html b/src/app/pet/dog/dog-list/dog-list.component.html index c9d0a98..717a80a 100644 --- a/src/app/pet/dog/dog-list/dog-list.component.html +++ b/src/app/pet/dog/dog-list/dog-list.component.html @@ -1,14 +1,52 @@ +
+ +

Dogs

+
+
+ + +
-
-
Name
- {{dog.name}} -
-
+
Chip

{{dog.chip}}

+
+
Name
+

{{dog.name}}

+
+
+
Date of birth
+

{{dog.dateOfBirth}}

+
+
+ + +
diff --git a/src/app/pet/dog/dog-list/dog-list.component.ts b/src/app/pet/dog/dog-list/dog-list.component.ts index 8015c16..d1edc63 100644 --- a/src/app/pet/dog/dog-list/dog-list.component.ts +++ b/src/app/pet/dog/dog-list/dog-list.component.ts @@ -41,4 +41,9 @@ export class DogListComponent implements OnInit { (page: PagedResourceCollection) => (this.dogs = page.resources) ); } + + detail(dog: Dog): void { + this.router.navigate(['/dogs', dog.id]); + } + } diff --git a/src/app/pet/dog/dog-search/dog-search.component.html b/src/app/pet/dog/dog-search/dog-search.component.html new file mode 100644 index 0000000..910bb08 --- /dev/null +++ b/src/app/pet/dog/dog-search/dog-search.component.html @@ -0,0 +1,12 @@ +
+ +
Sorry, suggestions could not be loaded.
+
+ + +
+
{{r.name}} - {{r.chip}}
+
+ \ No newline at end of file diff --git a/src/app/pet/dog/dog-search/dog-search.component.ts b/src/app/pet/dog/dog-search/dog-search.component.ts new file mode 100644 index 0000000..3774ca8 --- /dev/null +++ b/src/app/pet/dog/dog-search/dog-search.component.ts @@ -0,0 +1,42 @@ +import { Component, EventEmitter, Output } from '@angular/core'; +import { Observable, of, OperatorFunction } from 'rxjs'; +import { catchError, debounceTime, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators'; +import { ResourceCollection } from '@lagoshny/ngx-hateoas-client'; +import { Dog } from '../dog'; +import { DogService } from '../dog.service'; + +@Component({ + selector: 'app-dog-search', + templateUrl: './dog-search.component.html' +}) + +export class DogSearchComponent { + @Output() emitResults: EventEmitter = new EventEmitter(); + searchFailed = false; + searching = false; + + constructor(private dogService: DogService) { + } + + autocomplete: OperatorFunction = (text$: Observable) => + text$.pipe( + debounceTime(500), + distinctUntilChanged(), + tap(() => this.searching = true), + switchMap(term => term.length < 3 ? of([]) : + this.dogService.findByIdContaining(term).pipe( + map((collection: ResourceCollection) => collection.resources), + tap(() => this.searchFailed = false), + catchError(() => { + this.searchFailed = true; + return of([]); + }) + ) + ), + tap(() => this.searching = false ) + ) + + select(item: any): void { + this.emitResults.emit(item as Dog); + } +} diff --git a/src/app/pet/dog/dog.service.ts b/src/app/pet/dog/dog.service.ts index d1d96cd..fbafecd 100644 --- a/src/app/pet/dog/dog.service.ts +++ b/src/app/pet/dog/dog.service.ts @@ -1,6 +1,8 @@ import { Injectable } from "@angular/core"; import { PetService } from "../pet.service"; import { Dog } from "./dog"; +import { Observable, forkJoin, map } from "rxjs"; +import { ResourceCollection } from "@lagoshny/ngx-hateoas-client"; @Injectable({providedIn: 'root'}) export class DogService extends PetService { @@ -9,4 +11,18 @@ export class DogService extends PetService { super(Dog); } + public findByIdContaining(query: string): Observable> { + let res1 = this.searchCollection('findByName', { params: { name: query } }); + let res2 = this.searchCollection('findByChip', { params: { chip: query } }); + return forkJoin([res1, res2]).pipe( + map(([result1, result2]) => { + const combinedResources = [...result1.resources, ...result2.resources]; + return { + ...result1, + resources: combinedResources, + } as ResourceCollection; + }) + );; + } + } \ No newline at end of file diff --git a/src/app/pet/dog/dog.ts b/src/app/pet/dog/dog.ts index 6d348b5..545e33a 100644 --- a/src/app/pet/dog/dog.ts +++ b/src/app/pet/dog/dog.ts @@ -4,6 +4,6 @@ import { Pet } from '../pet'; @HateoasResource('dogs') export class Dog extends Pet { - meowingLevel: number; + barkingLevel: number; } \ No newline at end of file