diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index a4616a2..0d7fa49 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -14,8 +14,10 @@ import {AdoptionEditComponent} from './adoptions/adoption-edit/adoption-edit.com import {AdoptionDetailComponent} from './adoptions/adoption-detail/adoption-detail.component'; 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 {ShelterListComponent} from './shelter/shelter-list/shelter-list.component' import {ShelterCreateComponent} from "./shelter/shelter-create/shelter-create.component"; +import {LocationListComponent} from "./location/location-list/location-list.component"; +import {LocationCreateComponent} from "./location/location-create/location-create.component"; const routes: Routes = [ { path: 'users/create', component: UserRegisterComponent}, @@ -23,9 +25,8 @@ const routes: Routes = [ { path: 'users/:id/edit', component: UserEditComponent, canActivate: [LoggedInGuard]}, { path: 'users/:id', component: UserDetailComponent, canActivate: [LoggedInGuard]}, { path: 'users', component: UserListComponent, canActivate: [LoggedInGuard]}, - { path: 'about', component: AboutComponent}, - { path: '404', component: NotFoundComponent}, - { path: '', redirectTo: 'about', pathMatch: 'full'}, + { path: 'locations', component: LocationListComponent, canActivate: [LoggedInGuard]}, + { path: 'locations/create', component: LocationCreateComponent, canActivate: [LoggedInGuard]}, { path: 'adoptions' , component: AdoptionListComponent }, { path: 'adoptions/create', component: AdoptionCreateComponent }, { path: 'adoptions/edit', component: AdoptionEditComponent }, @@ -34,6 +35,9 @@ const routes: Routes = [ { path: 'adoptions/search', component: AdoptionSearchComponent }, { path: 'shelters', component: ShelterListComponent}, { path: 'shelters/create', component: ShelterCreateComponent}, + { path: 'about', component: AboutComponent}, + { path: '404', component: NotFoundComponent}, + { path: '', redirectTo: 'about', pathMatch: 'full'}, ]; @NgModule({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index bf37af6..e3945b5 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -23,17 +23,21 @@ import {HttpErrorInterceptor} from './error-handler/http-error-interceptor'; import {AuthenticationBasicService} from './login-basic/authentication-basic.service'; import {LoggedInGuard} from './login-basic/loggedin.guard'; import {UserService} from './user/user.service'; +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import {AdoptionSearchComponent} from './adoptions/adoption-search/adoption-search.component'; import {AdoptionListComponent} from './adoptions/adoption-list/adoption-list.component'; import {AdoptionEditComponent} from './adoptions/adoption-edit/adoption-edit.component'; import {AdoptionDetailComponent} from './adoptions/adoption-detail/adoption-detail.component'; 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"; +import {LocationService} from './location/location.service' +import {LocationListComponent} from './location/location-list/location-list.component'; +import {LocationSearchComponent} from './location/location-search/location-search.component'; +import {LocationCreateComponent} from "./location/location-create/location-create.component"; @NgModule({ declarations: [ @@ -55,7 +59,10 @@ import {ShelterDetailComponent} from "./shelter/shelter-detail/shelter-detail.co AdoptionListComponent, ShelterListComponent, ShelterCreateComponent, - ShelterDetailComponent + ShelterDetailComponent, + LocationListComponent, + LocationSearchComponent, + LocationCreateComponent ], imports: [ BrowserModule, @@ -74,7 +81,8 @@ import {ShelterDetailComponent} from "./shelter/shelter-detail/shelter-detail.co providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true }, - AuthenticationBasicService, LoggedInGuard, UserService, ShelterService, provideAnimationsAsync() + AuthenticationBasicService, LoggedInGuard, UserService, ShelterService, LocationService, + provideAnimationsAsync() ], bootstrap: [AppComponent] }) diff --git a/src/app/location/location-create/location-create.component.html b/src/app/location/location-create/location-create.component.html new file mode 100644 index 0000000..1b19f20 --- /dev/null +++ b/src/app/location/location-create/location-create.component.html @@ -0,0 +1,87 @@ + +
+
+
+ + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+ + +
+
+
diff --git a/src/app/location/location-create/location-create.component.ts b/src/app/location/location-create/location-create.component.ts new file mode 100644 index 0000000..a49a6b2 --- /dev/null +++ b/src/app/location/location-create/location-create.component.ts @@ -0,0 +1,122 @@ +import {Component, OnInit} from '@angular/core'; +import {Router} from "@angular/router"; +import {LocationService} from "../location.service"; +import {Location} from "../location"; +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-location-create', + templateUrl: './location-create.component.html', +}) +export class LocationCreateComponent implements OnInit{ + + closeResult = ''; + public isModalSaved: boolean = false; + public locations: Location[] = []; + public location: Location; + public commonNameInput: string = ''; + public commonEmailsList: any = []; + public locationForm: FormGroup; + + constructor( + private router: Router, + private locationService: LocationService, + private modalService: NgbModal + ) {} + + ngOnInit(): void { + this.location = new Location(); + this.locationForm = new FormGroup({ + address: new FormControl(this.location.address), + city: new FormControl(this.location.city), + province: new FormControl(this.location.province), + postalCode: new FormControl(this.location.postalCode), + }); + this.loadLocationList(); + } + + loadLocationList() { + this.locationService + .getPage({ + sort: { address: 'ASC' }, + }) + .subscribe((locations: PagedResourceCollection) => { + this.locations = locations.resources.sort((a, b) => + a.address.localeCompare(b.address) + ); + }); + } + + 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.locationForm.get('name'); + } + + get email() { + return this.locationForm.get('email'); + } + + get mobile() { + return this.locationForm.get('mobile'); + } + + onSubmit(): void { + this.location.latitude = 555; + this.location.longitude = 555; + this.locationService + .createResource({ body: this.location }) + .subscribe((location: Location) => { + console.log(location); + const uri = (location as any).uri; + this.router.navigate([uri]).then(); + }); + } + +} diff --git a/src/app/location/location-list/location-list.component.html b/src/app/location/location-list/location-list.component.html new file mode 100644 index 0000000..83e76c1 --- /dev/null +++ b/src/app/location/location-list/location-list.component.html @@ -0,0 +1,33 @@ + + +
+
+
+
+
Location ID
+ {{location.id}} +
+
+
City
+

{{location["city"]}}

+
+
+
PostalCode
+

{{location["postalCode"]}}

+
+
+
Address
+

{{location.address}}

+
+
+
Province
+

{{location["province"]}}

+
+
+
+ + + + diff --git a/src/app/location/location-list/location-list.component.ts b/src/app/location/location-list/location-list.component.ts new file mode 100644 index 0000000..d909edb --- /dev/null +++ b/src/app/location/location-list/location-list.component.ts @@ -0,0 +1,39 @@ + +import { Router } from '@angular/router'; +import { Component, OnInit } from '@angular/core'; +import { LocationService } from '../location.service'; +import { PagedResourceCollection } from '@lagoshny/ngx-hateoas-client'; +import {Location} from "../location"; + +@Component({ + selector: 'app-location-list', + templateUrl: './location-list.component.html' +}) +export class LocationListComponent implements OnInit { + public locations: Location[] = []; + public pageSize = 5; + public page = 1; + public totalLocations = 0; + + constructor( + public router: Router, + private locationService: LocationService) { + } + + ngOnInit(): void { + this.locationService.getPage({ pageParams: { size: this.pageSize }, sort: { id: 'ASC' } }).subscribe( + (page: PagedResourceCollection) => { + this.locations = page.resources; + this.totalLocations = page.totalElements; + }); + } + + changePage(): void { + this.locationService.getPage({ pageParams: { page: this.page - 1, size: this.pageSize }, sort: { id: 'ASC' } }).subscribe( + (page: PagedResourceCollection) => this.locations = page.resources); + } + + detail(location: Location): void { + this.router.navigate(['locations', location.id]); + } +} diff --git a/src/app/location/location-search/location-search.component.html b/src/app/location/location-search/location-search.component.html new file mode 100644 index 0000000..441a363 --- /dev/null +++ b/src/app/location/location-search/location-search.component.html @@ -0,0 +1,11 @@ +
+ +
Sorry, suggestions could not be loaded.
+
+ + +
+
{{r.email}}
+
ยบ diff --git a/src/app/location/location-search/location-search.component.ts b/src/app/location/location-search/location-search.component.ts new file mode 100644 index 0000000..b31088f --- /dev/null +++ b/src/app/location/location-search/location-search.component.ts @@ -0,0 +1,43 @@ + +import { Component, EventEmitter, Output } from '@angular/core'; +import { LocationService } from '../location.service'; +import { Observable, of, OperatorFunction } from 'rxjs'; +import { catchError, debounceTime, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators'; +import { ResourceCollection } from '@lagoshny/ngx-hateoas-client'; +import {Location} from "../location"; + +@Component({ + selector: 'app-location-search', + templateUrl: './location-search.component.html' +}) + +export class LocationSearchComponent { + @Output() emitResults: EventEmitter = new EventEmitter(); + searchFailed = false; + searching = false; + + constructor(private locationService: LocationService) { + } + + autocomplete: OperatorFunction = (text$: Observable) => + text$.pipe( + debounceTime(500), + distinctUntilChanged(), + tap(() => this.searching = true), + switchMap(term => term.length < 3 ? of([]) : + this.locationService.findById(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 Location); + } +} diff --git a/src/app/location/location.service.ts b/src/app/location/location.service.ts new file mode 100644 index 0000000..68a1364 --- /dev/null +++ b/src/app/location/location.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from "@angular/core"; +import { Observable } from "rxjs/internal/Observable"; +import { HateoasResourceOperation, ResourceCollection } from "@lagoshny/ngx-hateoas-client"; +import { Location } from "./location"; + +@Injectable({providedIn: "root"}) +export class LocationService extends HateoasResourceOperation { + + constructor() { + super(Location); + } + + public findLocationByAddressAndProvinceAndCityAndPostalCode(address:string, province:string, city:string, postalcode:number): Observable> { + const params = { + address: address, + province: province, + city: city, + postalcode: postalcode.toString() + }; + return this.searchCollection('findLocationByAddressAndProvinceAndCityAndPostalCode', {params}); + + } + + public findById(id: string): Observable> { + return this.searchCollection("findById", { params: { id: id } }) + } + + public findByPostalCode(postalCode: string): Observable> { + return this.searchCollection("findByPostalCode", { params: { postalCode: postalCode } }) + } + + public findByProvince(province: string): Observable> { + return this.searchCollection("findByProvince", { params: { province: province } }) + } + + + + public findByCity(city: string): Observable> { + return this.searchCollection("findByCity", { params: { city: city } }) + } + public findByAddress(address: string): Observable> { + return this.searchCollection("findByAddress", { params: { address: address } }) + } + +} + + + diff --git a/src/app/location/location.ts b/src/app/location/location.ts new file mode 100644 index 0000000..ea4b325 --- /dev/null +++ b/src/app/location/location.ts @@ -0,0 +1,22 @@ +import { HateoasResource, Resource } from '@lagoshny/ngx-hateoas-client'; + +@HateoasResource('locations') +export class Location extends Resource { + address: string; + latitude: number; + longitude: number; + province: string; + city: string; + postalCode:number; + uri: string; + + constructor(values: object = {}) { + super(); + Object.assign(this as any, values); + } + + public get id(): string { + let uriArray = this.uri.split('/'); + return uriArray.pop(); + } +} diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index 418ef19..adbc437 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -11,7 +11,7 @@ About +