diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 99aa932..a4616a2 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -15,6 +15,7 @@ import {AdoptionDetailComponent} from './adoptions/adoption-detail/adoption-deta import {AdoptionDeleteComponent} from './adoptions/adoption-delete/adoption-delete.component'; import {AdoptionCreateComponent} from "./adoptions/adoption-create/adoption-create.component"; import { ShelterListComponent } from './shelter/shelter-list/shelter-list.component' +import {ShelterCreateComponent} from "./shelter/shelter-create/shelter-create.component"; const routes: Routes = [ { path: 'users/create', component: UserRegisterComponent}, @@ -32,6 +33,7 @@ const routes: Routes = [ { path: 'adoptions/detail', component: AdoptionDetailComponent }, { path: 'adoptions/search', component: AdoptionSearchComponent }, { path: 'shelters', component: ShelterListComponent}, + { path: 'shelters/create', component: ShelterCreateComponent}, ]; @NgModule({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8a7c555..11f2d3b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -30,6 +30,10 @@ import {AdoptionDetailComponent} from './adoptions/adoption-detail/adoption-deta import {AdoptionDeleteComponent} from './adoptions/adoption-delete/adoption-delete.component'; import {AdoptionCreateComponent} from "./adoptions/adoption-create/adoption-create.component"; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; +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"; @NgModule({ declarations: [ @@ -49,6 +53,9 @@ import { provideAnimationsAsync } from '@angular/platform-browser/animations/asy AdoptionDetailComponent, AdoptionEditComponent, AdoptionListComponent + ShelterListComponent, + ShelterCreateComponent, + ShelterDetailComponent ], imports: [ BrowserModule, @@ -62,12 +69,12 @@ import { provideAnimationsAsync } from '@angular/platform-browser/animations/asy LoginBasicModule, ErrorHandlerModule, NgbModule, - ReactiveFormsModule, + ReactiveFormsModule ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true }, - AuthenticationBasicService, LoggedInGuard, UserService, provideAnimationsAsync() + AuthenticationBasicService, LoggedInGuard, UserService, ShelterService, provideAnimationsAsync() ], bootstrap: [AppComponent] }) diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index 55e04f2..418ef19 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -18,6 +18,16 @@ [routerLink]="['/users']"> List + diff --git a/src/app/shelter/shelter-create/shelter-create.component.html b/src/app/shelter/shelter-create/shelter-create.component.html new file mode 100644 index 0000000..0d81e0b --- /dev/null +++ b/src/app/shelter/shelter-create/shelter-create.component.html @@ -0,0 +1,97 @@ +
+
+
+ + +
+ Name is required. +
+
+ Invalid name. +
+
+ +
+ + +
+ Invalid email. +
+
+ +
+ +
+ +
+
+ Invalid mobile. +
+
+ + + + + + + + + + + + + + + + +
+ + +
+
+
diff --git a/src/app/shelter/shelter-create/shelter-create.component.ts b/src/app/shelter/shelter-create/shelter-create.component.ts new file mode 100644 index 0000000..9a88928 --- /dev/null +++ b/src/app/shelter/shelter-create/shelter-create.component.ts @@ -0,0 +1,129 @@ +import {Component, OnInit} from '@angular/core'; +import {Router} from "@angular/router"; +import {ShelterService} from "../shelter.service"; +import {Shelter} from "../shelter"; +import {AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from "@angular/forms"; +import {ModalDismissReasons, NgbModal} from "@ng-bootstrap/ng-bootstrap"; +import {PagedResourceCollection} from "@lagoshny/ngx-hateoas-client"; +import {formatDate} from "@angular/common"; + +@Component({ + selector: 'app-shelter-create', + templateUrl: './shelter-create.component.html', +}) +export class ShelterCreateComponent implements OnInit{ + + closeResult = ''; + public isModalSaved: boolean = false; + public shelters: Shelter[] = []; + public shelter: Shelter; + public commonNameInput: string = ''; + public commonEmailsList: any = []; + public shelterForm: FormGroup; + + constructor( + private router: Router, + private shelterService: ShelterService, + private modalService: NgbModal + ) {} + + ngOnInit(): void { + this.shelter = new Shelter(); + this.shelterForm = new FormGroup({ + name: new FormControl(this.shelter.name, [ + Validators.required, + ]), + email: new FormControl(this.shelter.email, [ + Validators.required, + this.emailValidator(), + ]), + mobile: new FormControl(this.shelter.mobile, [ + Validators.required, + this.mobileValidator(), + ]), + }); + this.loadShelterList(); + } + + loadShelterList() { + this.shelterService + .getPage({ + sort: { name: 'ASC' }, + }) + .subscribe((shelters: PagedResourceCollection) => { + this.shelters = shelters.resources.sort((a, b) => + a.name.localeCompare(b.name) + ); + }); + } + + open(content) { + this.modalService + .open(content, { ariaLabelledBy: 'modal-basic-title' }) + .result.then( + (result) => { + this.closeResult = `Closed with: ${result}`; + }, + (reason) => { + this.closeResult = `Dismissed ${this.getDismissReason(reason)}`; + } + ); + } + + private getDismissReason(reason: any): string { + if (reason === ModalDismissReasons.ESC) { + return 'by pressing ESC'; + } else if (reason === ModalDismissReasons.BACKDROP_CLICK) { + return 'by clicking on a backdrop'; + } else { + return `with: ${reason}`; + } + } + + saveAndClose(modal: any) { + this.isModalSaved = true; + modal.close('Save click'); + } + + emailValidator(): ValidatorFn { + const nameRe: RegExp = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/; + return (control: AbstractControl): ValidationErrors | null => { + const invalid = !nameRe.test(control.value); + return invalid ? { invalidName: { value: control.value } } : null; + }; + } + + mobileValidator(): ValidatorFn { + const nameRe: RegExp = /^$|[0-9]{11}/; + return (control: AbstractControl): ValidationErrors | null => { + const invalid = !nameRe.test(control.value); + return invalid ? { invalidName: { value: control.value } } : null; + }; + + } + + get name() { + return this.shelterForm.get('name'); + } + + get email() { + return this.shelterForm.get('email'); + } + + get mobile() { + return this.shelterForm.get('mobile'); + } + + onSubmit(): void { + this.shelter.createdAt = new Date(); + this.shelter.updatedAt = new Date(); + this.shelterService + .createResource({ body: this.shelter }) + .subscribe((shelter: Shelter) => { + console.log(shelter); + const uri = (shelter as any).uri; + this.router.navigate([uri]).then(); + }); + } + +} diff --git a/src/app/shelter/shelter-delete/shelter-delete.component.html b/src/app/shelter/shelter-delete/shelter-delete.component.html new file mode 100644 index 0000000..7725f68 --- /dev/null +++ b/src/app/shelter/shelter-delete/shelter-delete.component.html @@ -0,0 +1,25 @@ +
+
+
Please, confirm deletion:
+
+
+
+ + +
+
+
diff --git a/src/app/shelter/shelter-delete/shelter-delete.component.ts b/src/app/shelter/shelter-delete/shelter-delete.component.ts new file mode 100644 index 0000000..a26d8af --- /dev/null +++ b/src/app/shelter/shelter-delete/shelter-delete.component.ts @@ -0,0 +1,37 @@ +import {Component, OnInit} from '@angular/core'; +import {ActivatedRoute, Router} from "@angular/router"; +import {Shelter} from "../shelter"; +import {ShelterService} from "../shelter.service"; + +@Component({ + selector: 'app-shelter-delete', + templateUrl: './shelter-delete.component.html', +}) +export class ShelterDeleteComponent implements OnInit { + idShelter: string; + shelter: Shelter; + constructor( + private router: Router, + private shelterService: ShelterService, + private route: ActivatedRoute + ) {} + ngOnInit(): void { + this.idShelter = this.route.snapshot.paramMap.get('id'); + } + delete() { + this.shelterService.getResource(this.idShelter).subscribe((shelter: Shelter) => { + this.shelter = shelter; + this.shelterService + .patchResource(this.shelter) + .subscribe((patchedShelter: Shelter) => { + this.shelterService.deleteResourceById(patchedShelter.id).subscribe({ + next: (e) => { + this.router.navigateByUrl('/shelters'); + }, + error: (err) => { + }, + }); + }); + }); + } +} diff --git a/src/app/shelter/shelter-detail/shelter-detail.component.html b/src/app/shelter/shelter-detail/shelter-detail.component.html new file mode 100644 index 0000000..13250a5 --- /dev/null +++ b/src/app/shelter/shelter-detail/shelter-detail.component.html @@ -0,0 +1,45 @@ +
+
+

Shelter {{ shelterDetail.id }}

+
+
+
Name
+

+ {{ shelterDetail.name }} +

+
+
+
Email
+

{{ shelterDetail.email }}

+
+ +
+
+
diff --git a/src/app/shelter/shelter-detail/shelter-detail.component.ts b/src/app/shelter/shelter-detail/shelter-detail.component.ts new file mode 100644 index 0000000..8eaf462 --- /dev/null +++ b/src/app/shelter/shelter-detail/shelter-detail.component.ts @@ -0,0 +1,29 @@ +import {Component, OnInit} from '@angular/core'; +import {Shelter} from "../shelter"; +import {ActivatedRoute} from "@angular/router"; +import {AuthenticationBasicService} from "../../login-basic/authentication-basic.service"; +import {ShelterService} from "../shelter.service"; + +@Component({ + selector: 'app-shelter-detail', + templateUrl: './shelter-detail.component.html', +}) +export class ShelterDetailComponent implements OnInit{ + shelterDetail: Shelter; + constructor( + private route: ActivatedRoute, + private shelterService: ShelterService, + private authenticationService: AuthenticationBasicService + ) {} + + ngOnInit(): void { + const id = this.route.snapshot.paramMap.get('id'); + this.shelterService.getResource(id).subscribe((shelter: Shelter) => { + this.shelterDetail = shelter; + }); + } + + isRole(role: string): boolean { + return this.authenticationService.isRole(role); + } +} diff --git a/src/app/shelter/shelter-list/shelter-list.component.html b/src/app/shelter/shelter-list/shelter-list.component.html index 31ee882..a5b98f8 100644 --- a/src/app/shelter/shelter-list/shelter-list.component.html +++ b/src/app/shelter/shelter-list/shelter-list.component.html @@ -1 +1,21 @@ -

+
+
+
+
+
Name
+ {{shelter.name}} +
+
+
E-mail
+

{{shelter.email}}

+
+
+
+
+ + + + + diff --git a/src/app/shelter/shelter-list/shelter-list.component.ts b/src/app/shelter/shelter-list/shelter-list.component.ts index f5943c6..30c8add 100644 --- a/src/app/shelter/shelter-list/shelter-list.component.ts +++ b/src/app/shelter/shelter-list/shelter-list.component.ts @@ -1,11 +1,44 @@ -import { Component } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; +import {Shelter} from "../shelter"; +import {Router} from "@angular/router"; +import {ShelterService} from "../shelter.service"; +import {PagedResourceCollection} from "@lagoshny/ngx-hateoas-client"; @Component({ selector: 'app-shelter-list', - standalone: true, - imports: [], templateUrl: './shelter-list.component.html', }) -export class ShelterListComponent { +export class ShelterListComponent implements OnInit { + public shelters: Shelter[] = []; + public pageSize = 5; + public page = 1; + public totalShelters = 0; + constructor( + public router: Router, + private shelterService: ShelterService, + ) {} + + ngOnInit(): void { + this.shelterService + .getPage({ + pageParams: { size: this.pageSize }, + sort: { name: 'ASC' }, + }) + .subscribe((page: PagedResourceCollection) => { + this.shelters = page.resources; + console.log(this.shelters); + this.totalShelters = page.totalElements; + }); + } + changePage(): void { + this.shelterService + .getPage({ + pageParams: { page: this.page - 1, size: this.pageSize }, + sort: { name: 'ASC' }, + }) + .subscribe( + (page: PagedResourceCollection) => (this.shelters = page.resources) + ); + } } diff --git a/src/app/shelter/shelter.service.ts b/src/app/shelter/shelter.service.ts new file mode 100644 index 0000000..6df3803 --- /dev/null +++ b/src/app/shelter/shelter.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/internal/Observable'; +import { HateoasResourceOperation, ResourceCollection } from '@lagoshny/ngx-hateoas-client'; +import {Shelter} from "./shelter"; + +@Injectable({providedIn: 'root'}) +export class ShelterService extends HateoasResourceOperation { + + constructor() { + super(Shelter); + } + + public findByName(query: string): Observable> { + return this.searchCollection('findByName', { params: { text: query } }); + } +} diff --git a/src/app/shelter/shelter.ts b/src/app/shelter/shelter.ts new file mode 100644 index 0000000..368a723 --- /dev/null +++ b/src/app/shelter/shelter.ts @@ -0,0 +1,22 @@ +import { HateoasResource, Resource } from '@lagoshny/ngx-hateoas-client'; + +@HateoasResource('shelters') +export class Shelter extends Resource { + uri: string; + name: string; + email: string; + mobile: string; + createdAt: Date; + updatedAt: Date; + isActive: boolean; + + constructor(values: object = {}) { + super(); + Object.assign(this as any, values); + } + + public get id(): string { + let uriArray = this.uri.split('/'); + return uriArray.pop(); + } +}