From dd4faea712df132851965c2b400b4d082b68f34d Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Tue, 26 Nov 2024 09:37:20 +0100 Subject: [PATCH 1/5] update styling and do stuff --- angular.json | 2 + bun.lockb | Bin 457889 -> 457889 bytes package-lock.json | 56 ++++++++++- package.json | 4 +- src/app/HotelItem/HotelItem.component.ts | 9 +- src/app/app.component.html | 6 +- src/app/app.component.ts | 10 +- src/app/app.config.ts | 3 +- src/app/app.routes.ts | 5 + .../hotel-details.component.html | 6 +- .../hotel-details/hotel-details.component.ts | 7 +- src/app/hotel-form/hotel-form.component.css | 0 src/app/hotel-form/hotel-form.component.html | 58 +++++++++++ .../hotel-form/hotel-form.component.spec.ts | 23 +++++ src/app/hotel-form/hotel-form.component.ts | 93 ++++++++++++++++++ src/app/hotel-list/hotel-list.component.html | 5 +- src/app/hotel-list/hotel-list.component.ts | 40 +------- src/index.html | 4 +- src/styles.css | 3 + 19 files changed, 266 insertions(+), 68 deletions(-) create mode 100644 src/app/hotel-form/hotel-form.component.css create mode 100644 src/app/hotel-form/hotel-form.component.html create mode 100644 src/app/hotel-form/hotel-form.component.spec.ts create mode 100644 src/app/hotel-form/hotel-form.component.ts diff --git a/angular.json b/angular.json index 0529da2..9e3a815 100644 --- a/angular.json +++ b/angular.json @@ -28,6 +28,7 @@ "src/assets" ], "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", "src/styles.css" ], "scripts": [] @@ -86,6 +87,7 @@ } ], "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", "src/styles.css" ], "scripts": [] diff --git a/bun.lockb b/bun.lockb index 1c972ffad0a0ca5c5a88aecd361e82204c3769dc..ac140097e5eceb0c59d065c53298a9ad51f0482a 100755 GIT binary patch delta 45 zcmZ4ZQ+nY~>4p}@7N!>FEiAsrIT+&%^b9TZ45kNqunM4p}@7N!>FEiAsrnV1-+dwZ}-wr3n?0b=10" } }, + "node_modules/@angular/cdk": { + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.3.tgz", + "integrity": "sha512-lUcpYTxPZuntJ1FK7V2ugapCGMIhT6TUDjIGgXfS9AxGSSKgwr8HNs6Ze9pcjYC44UhP40sYAZuiaFwmE60A2A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/cli": { "version": "18.2.3", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.3.tgz", @@ -531,6 +551,24 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/material": { + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.3.tgz", + "integrity": "sha512-JFfvXaMHMhskncaxxus4sDvie9VYdMkfYgfinkLXpZlPFyn1IzjDw0c1BcrcsuD7UxQVZ/v5tucCgq1FQfGRpA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^18.0.0 || ^19.0.0", + "@angular/cdk": "18.2.3", + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "@angular/forms": "^18.0.0 || ^19.0.0", + "@angular/platform-browser": "^18.0.0 || ^19.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/platform-browser": { "version": "18.2.3", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.3.tgz", @@ -4763,6 +4801,20 @@ "ajv": "^8.8.2" } }, + "node_modules/angular-in-memory-web-api": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.18.0.tgz", + "integrity": "sha512-Eqkr9+x3d7K4dmn6Qs3ZVAfqBDPZN0N7Qel5i8eU/pe5r44J/pfxlNW+1LC2Sb2PdENEdvFzC8wx8qly5+kQyQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^18.0.0", + "@angular/core": "^18.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -6487,7 +6539,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, + "devOptional": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -10467,7 +10519,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "entities": "^4.4.0" diff --git a/package.json b/package.json index 0671431..01682cb 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,12 @@ "private": true, "dependencies": { "@angular/animations": "^18.2.3", + "@angular/cdk": "^18.2.3", "@angular/common": "^18.2.3", "@angular/compiler": "^18.2.3", "@angular/core": "^18.2.3", "@angular/forms": "^18.2.3", + "@angular/material": "^18.2.3", "@angular/platform-browser": "^18.2.3", "@angular/platform-browser-dynamic": "^18.2.3", "@angular/router": "^18.2.3", @@ -39,4 +41,4 @@ "tailwindcss": "^3.4.10", "typescript": "~5.5.2" } -} +} \ No newline at end of file diff --git a/src/app/HotelItem/HotelItem.component.ts b/src/app/HotelItem/HotelItem.component.ts index 0e5d136..c34a644 100644 --- a/src/app/HotelItem/HotelItem.component.ts +++ b/src/app/HotelItem/HotelItem.component.ts @@ -1,17 +1,14 @@ -import { Component, Injectable, Input } from "@angular/core"; -import { ChildComponent } from "../Child/child.component"; +import { Component, Input } from "@angular/core"; import { Hotel } from "./hotel"; -import { CurrencyPipe, NgIf } from "@angular/common"; +import {CurrencyPipe, NgForOf, NgIf} from "@angular/common"; import { FormsModule } from "@angular/forms"; -import { StarRatingComponent } from "../star-rating/star-rating.component"; -import { HttpClient } from "@angular/common/http"; import { RouterLink } from "@angular/router"; @Component({ selector: 'app-hotel-item', standalone: true, templateUrl: './HotelItem.component.html', - imports: [ChildComponent, CurrencyPipe, FormsModule, StarRatingComponent, NgIf, RouterLink], + imports: [CurrencyPipe, FormsModule, NgIf, RouterLink, NgForOf], }) export class HotelItem { diff --git a/src/app/app.component.html b/src/app/app.component.html index 346d4ad..90c6b64 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,5 +1 @@ - - - - - + \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 50508e2..2a3bdb6 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,20 +1,12 @@ import { Component, Injectable } from '@angular/core'; -import { HotelItem } from './HotelItem/HotelItem.component'; -import { SearchComponent } from './Search/search.component'; -import { AsyncPipe, NgFor, NgForOf, NgIf, UpperCasePipe } from '@angular/common'; -import { TextPipe } from '../text.pipe'; import { HotelService } from './Parent/services/hotel.service'; -import { inject } from '@angular/core'; -import { filter, from, last, map, Observable, scan } from 'rxjs'; -import { HttpClient } from '@angular/common/http'; -import { Hotel } from './HotelItem/hotel'; import { RouterOutlet } from '@angular/router'; @Injectable({providedIn: "root"}) @Component({ selector: 'app-root', standalone: true, - imports: [RouterOutlet, NgFor, NgForOf, NgIf, HotelItem, SearchComponent, UpperCasePipe, TextPipe, AsyncPipe], + imports: [RouterOutlet], templateUrl: './app.component.html', providers: [HotelService], styleUrl: './app.component.css' diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 55b7251..db633ab 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -8,6 +8,7 @@ import { registerLocaleData } from '@angular/common'; import { provideHttpClient } from '@angular/common/http'; import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; import { HotelData } from './api/api'; +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; registerLocaleData(localeDe, 'de-DE'); registerLocaleData(localeCn, 'cn-CN'); @@ -16,5 +17,5 @@ export const appConfig: ApplicationConfig = { provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideHttpClient(), - importProvidersFrom(InMemoryWebApiModule.forRoot(HotelData))] + importProvidersFrom(InMemoryWebApiModule.forRoot(HotelData)), provideAnimationsAsync()] }; diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index eaaae27..9d06d6e 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,6 +1,7 @@ import { Routes } from '@angular/router'; import { HotelDetailsComponent } from './hotel-details/hotel-details.component'; import { HotelListComponent } from './hotel-list/hotel-list.component'; +import { HotelFormComponent } from './hotel-form/hotel-form.component'; export const routes: Routes = [ { @@ -11,4 +12,8 @@ export const routes: Routes = [ path: "hotels/:id", component: HotelDetailsComponent, }, + { + path: "create-hotel", + component: HotelFormComponent, + }, ]; diff --git a/src/app/hotel-details/hotel-details.component.html b/src/app/hotel-details/hotel-details.component.html index 3a1c644..2ba9075 100644 --- a/src/app/hotel-details/hotel-details.component.html +++ b/src/app/hotel-details/hotel-details.component.html @@ -1 +1,5 @@ - +
+
+ +
+
diff --git a/src/app/hotel-details/hotel-details.component.ts b/src/app/hotel-details/hotel-details.component.ts index 2d3c396..aac11b2 100644 --- a/src/app/hotel-details/hotel-details.component.ts +++ b/src/app/hotel-details/hotel-details.component.ts @@ -1,16 +1,17 @@ import { HttpClient } from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import {ActivatedRoute, Router, RouterLink} from '@angular/router'; import { Hotel } from '../HotelItem/hotel'; -import { CurrencyPipe } from '@angular/common'; +import {CurrencyPipe, NgIf} from '@angular/common'; import { StarRatingComponent } from '../star-rating/star-rating.component'; import { HotelItem } from '../HotelItem/HotelItem.component'; import { catchError, EMPTY } from 'rxjs'; +import {HotelFormComponent} from "../hotel-form/hotel-form.component"; @Component({ selector: 'app-hotel-details', standalone: true, - imports: [CurrencyPipe, StarRatingComponent, HotelItem], + imports: [CurrencyPipe, StarRatingComponent, HotelItem, HotelFormComponent, NgIf, RouterLink], templateUrl: './hotel-details.component.html', styleUrl: './hotel-details.component.css' }) diff --git a/src/app/hotel-form/hotel-form.component.css b/src/app/hotel-form/hotel-form.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/hotel-form/hotel-form.component.html b/src/app/hotel-form/hotel-form.component.html new file mode 100644 index 0000000..7804948 --- /dev/null +++ b/src/app/hotel-form/hotel-form.component.html @@ -0,0 +1,58 @@ +
+
+ + +
+
Hotel Name is required.
+
Hotel Name must be at least 3 characters long.
+
+
+
+ + +
+
Hotel Description is required.
+
Hotel Description must be at least 3 characters long. +
+
+
+
+ + +
+
Hotel Price is required.
+
Hotel Price must be a positive number.
+
+
+
+ + +
+
Hotel Rating is required.
+
Hotel Rating must be at least 0.
+
Hotel Rating cannot be more than 5.
+
+
+
+
+ + +
+ +
+
\ No newline at end of file diff --git a/src/app/hotel-form/hotel-form.component.spec.ts b/src/app/hotel-form/hotel-form.component.spec.ts new file mode 100644 index 0000000..3f1f5bd --- /dev/null +++ b/src/app/hotel-form/hotel-form.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HotelFormComponent } from './hotel-form.component'; + +describe('HotelFormComponent', () => { + let component: HotelFormComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HotelFormComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(HotelFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/hotel-form/hotel-form.component.ts b/src/app/hotel-form/hotel-form.component.ts new file mode 100644 index 0000000..a521159 --- /dev/null +++ b/src/app/hotel-form/hotel-form.component.ts @@ -0,0 +1,93 @@ +import { Component, inject, Inject, Input } from '@angular/core'; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms"; +import { Hotel } from "../HotelItem/hotel"; +import { HttpClient } from '@angular/common/http'; +import { catchError } from "rxjs"; +import { Router, RouterLink } from "@angular/router"; +import { NgIf } from "@angular/common"; +import { MatButtonModule } from "@angular/material/button"; +import { MatIconModule } from "@angular/material/icon"; + +@Component({ + selector: 'app-hotel-form', + standalone: true, + imports: [ + ReactiveFormsModule, + RouterLink, + NgIf, + MatButtonModule, + MatIconModule + ], + templateUrl: './hotel-form.component.html', + styleUrl: './hotel-form.component.css' +}) +export class HotelFormComponent { + public form!: FormGroup; + + @Input() + public hotel!: Hotel; + + public router: Router = inject(Router); + + constructor(private http: HttpClient) {} + + ngOnInit() { + this.form = new FormGroup({ + name: new FormControl(this.hotel?.hotelName || '', [Validators.minLength(3), Validators.required]), + description: new FormControl(this.hotel?.description || '', [Validators.minLength(3), Validators.required]), + price: new FormControl(this.hotel?.price || '', [Validators.min(0), Validators.required]), + rating: new FormControl(this.hotel?.rating || '', [Validators.min(0), Validators.max(5), Validators.required]) + }); + } + + public delete(id: number) { + this.http.delete(`api/hotels/${this.hotel?.id}`).pipe( + catchError(err => { + console.error('Error deleting hotel', err); + return err; + }) + ).subscribe(() => { + console.log('Hotel deleted'); + }); + + this.router.navigate(['/']); + } + + public submit() { + if (this.form.valid) { + const hotelData: Hotel = { + id: this.hotel?.id || 0, + hotelName: this.form.value.name, + description: this.form.value.description, + price: this.form.value.price, + rating: this.form.value.rating, + imageUrl: this.hotel?.imageUrl || 'https://placehold.co/2000x1050/EEE/31343C', + tags: this.hotel?.tags || [] + }; + + if (this.hotel) { + this.http.put(`api/hotels/${this.hotel.id}`, hotelData).pipe( + catchError(err => { + console.error('Error updating hotel', err); + return err; + }) + ).subscribe(() => { + console.log('Hotel updated'); + }); + } + + else { + this.http.post('api/hotels', hotelData).pipe( + catchError(err => { + console.error('Error creating hotel', err); + return err; + }) + ).subscribe(() => { + console.log('Hotel created'); + }); + } + + this.router.navigate(['/']); + } + } +} diff --git a/src/app/hotel-list/hotel-list.component.html b/src/app/hotel-list/hotel-list.component.html index b4dd192..6f2d94c 100644 --- a/src/app/hotel-list/hotel-list.component.html +++ b/src/app/hotel-list/hotel-list.component.html @@ -1,9 +1,10 @@

{{'hello' | uppercase | text}}

- @if (hotels[0].hotelName) { + +
- } +
diff --git a/src/app/hotel-list/hotel-list.component.ts b/src/app/hotel-list/hotel-list.component.ts index 528c8ce..876c910 100644 --- a/src/app/hotel-list/hotel-list.component.ts +++ b/src/app/hotel-list/hotel-list.component.ts @@ -7,6 +7,8 @@ import { Hotel } from '../HotelItem/hotel'; import { HttpClient } from '@angular/common/http'; import { filter, from, last, map, Observable, scan } from 'rxjs'; import { HotelItem } from '../HotelItem/HotelItem.component'; +import { RouterLink } from '@angular/router'; +import { MatButtonModule } from '@angular/material/button'; interface User { name: string; @@ -16,7 +18,7 @@ interface User { @Component({ selector: 'app-hotel-list', standalone: true, - imports: [UpperCasePipe, TextPipe, SearchComponent, HotelItem, NgFor, NgIf], + imports: [UpperCasePipe, TextPipe, SearchComponent, HotelItem, NgFor, NgIf, RouterLink, MatButtonModule], templateUrl: './hotel-list.component.html', styleUrl: './hotel-list.component.css' }) @@ -33,41 +35,5 @@ export class HotelListComponent { this.http.get>("api/hotels").subscribe(res => { this.hotels = res; }); - - const users = [ - { name: "Max", age: 21 }, - { name: "Peter", age: 31 }, - { name: "Hans", age: 13 }, - { name: "Klaus", age: 51 }, - { name: "Dieter", age: 1 }, - { name: "Jan-Marlon", age: 3 }, - ] - - const stream: Observable = from(users); - - stream.pipe( - filter((user) => user.age > 18), - scan((acc, user) => acc + user.age, 0), - map((ageSum, index) => ageSum / (index + 1)), - last(), - ).subscribe(console.log); - - // const stream: Observable = from([5, 1, 2, 12, 5, 14, 17, 5, "testing"]); - - // stream.pipe( - // filter((value) => typeof value === "number"), - // tap((value) => console.log("Zahl:" + value)), - // filter((value: number) => value % 2 === 0), - // tap((value) => console.log("Gerade Zahl: " + value)), - // toArray(), - //).subscribe(console.log); } - - public test() { - console.log(this.search); - } - - // public foundHotels = this.hotels.pipe( - // filter((hotel) => hotel.hotelName.includes(this.search)), - // ); } diff --git a/src/index.html b/src/index.html index f3614f8..0ca266f 100644 --- a/src/index.html +++ b/src/index.html @@ -7,8 +7,10 @@ + + - + diff --git a/src/styles.css b/src/styles.css index 2adbbf3..3ccb4fd 100644 --- a/src/styles.css +++ b/src/styles.css @@ -6,3 +6,6 @@ .button { @apply border-black rounded px-3 py-1 border-[3px]; } + +html, body { height: 100%; } +body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } \ No newline at end of file -- 2.45.3 From 074da034c580313f69cb3dc05c107d9405b04ba8 Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Tue, 26 Nov 2024 09:51:53 +0100 Subject: [PATCH 2/5] add tags functionality --- src/app/hotel-form/hotel-form.component.html | 14 +++++++++++++ src/app/hotel-form/hotel-form.component.ts | 22 +++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/app/hotel-form/hotel-form.component.html b/src/app/hotel-form/hotel-form.component.html index 7804948..93aa829 100644 --- a/src/app/hotel-form/hotel-form.component.html +++ b/src/app/hotel-form/hotel-form.component.html @@ -46,6 +46,20 @@
Hotel Rating cannot be more than 5.
+
+ +
+
+ + +
+ +
+
diff --git a/src/app/hotel-form/hotel-form.component.ts b/src/app/hotel-form/hotel-form.component.ts index a521159..c23ec43 100644 --- a/src/app/hotel-form/hotel-form.component.ts +++ b/src/app/hotel-form/hotel-form.component.ts @@ -1,12 +1,13 @@ import { Component, inject, Inject, Input } from '@angular/core'; -import { FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms"; +import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms"; import { Hotel } from "../HotelItem/hotel"; import { HttpClient } from '@angular/common/http'; import { catchError } from "rxjs"; import { Router, RouterLink } from "@angular/router"; -import { NgIf } from "@angular/common"; +import { NgFor, NgIf } from "@angular/common"; import { MatButtonModule } from "@angular/material/button"; import { MatIconModule } from "@angular/material/icon"; +import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-hotel-form', @@ -16,7 +17,9 @@ import { MatIconModule } from "@angular/material/icon"; RouterLink, NgIf, MatButtonModule, - MatIconModule + MatIconModule, + NgFor, + FormsModule ], templateUrl: './hotel-form.component.html', styleUrl: './hotel-form.component.css' @@ -36,7 +39,8 @@ export class HotelFormComponent { name: new FormControl(this.hotel?.hotelName || '', [Validators.minLength(3), Validators.required]), description: new FormControl(this.hotel?.description || '', [Validators.minLength(3), Validators.required]), price: new FormControl(this.hotel?.price || '', [Validators.min(0), Validators.required]), - rating: new FormControl(this.hotel?.rating || '', [Validators.min(0), Validators.max(5), Validators.required]) + rating: new FormControl(this.hotel?.rating || '', [Validators.min(0), Validators.max(5), Validators.required]), + tags: new FormArray(this.hotel?.tags?.map(tag => new FormControl(tag)) || []) }); } @@ -53,6 +57,14 @@ export class HotelFormComponent { this.router.navigate(['/']); } + get tags() { + return this.form.get('tags') as FormArray; + } + + public addTag() { + this.tags.push(new FormControl('')); + } + public submit() { if (this.form.valid) { const hotelData: Hotel = { @@ -62,7 +74,7 @@ export class HotelFormComponent { price: this.form.value.price, rating: this.form.value.rating, imageUrl: this.hotel?.imageUrl || 'https://placehold.co/2000x1050/EEE/31343C', - tags: this.hotel?.tags || [] + tags: this.form.value.tags || [] }; if (this.hotel) { -- 2.45.3 From 6e5eadd3a2ef0a84fcafd76df70e1d3b8dcd6f64 Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Tue, 3 Dec 2024 09:20:59 +0100 Subject: [PATCH 3/5] add better validation to forms --- src/app/app.routes.ts | 4 +++ src/app/form.guard.spec.ts | 17 ++++++++++++ src/app/form.guard.ts | 15 +++++++++++ .../hotel-details.component.html | 4 +-- .../hotel-details/hotel-details.component.ts | 15 ++++++----- src/app/hotel-form/hotel-form.component.html | 17 +++++++++--- src/app/hotel-form/hotel-form.component.ts | 6 +++-- src/app/hotel-list/hotel-list.component.html | 26 ++++++++++++++++--- 8 files changed, 86 insertions(+), 18 deletions(-) create mode 100644 src/app/form.guard.spec.ts create mode 100644 src/app/form.guard.ts diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 9d06d6e..46c3c18 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -2,6 +2,8 @@ import { Routes } from '@angular/router'; import { HotelDetailsComponent } from './hotel-details/hotel-details.component'; import { HotelListComponent } from './hotel-list/hotel-list.component'; import { HotelFormComponent } from './hotel-form/hotel-form.component'; +import { inject } from '@angular/core'; +import { formGuard } from './form.guard'; export const routes: Routes = [ { @@ -11,9 +13,11 @@ export const routes: Routes = [ { path: "hotels/:id", component: HotelDetailsComponent, + canDeactivate: [formGuard], }, { path: "create-hotel", component: HotelFormComponent, + canDeactivate: [formGuard], }, ]; diff --git a/src/app/form.guard.spec.ts b/src/app/form.guard.spec.ts new file mode 100644 index 0000000..1c3d01f --- /dev/null +++ b/src/app/form.guard.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { CanDeactivateFn } from '@angular/router'; + +import { formGuard } from './form.guard'; + +describe('formGuard', () => { + const executeGuard: CanDeactivateFn = (...guardParameters) => + TestBed.runInInjectionContext(() => formGuard(...guardParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(executeGuard).toBeTruthy(); + }); +}); diff --git a/src/app/form.guard.ts b/src/app/form.guard.ts new file mode 100644 index 0000000..bec14c4 --- /dev/null +++ b/src/app/form.guard.ts @@ -0,0 +1,15 @@ +import { CanDeactivateFn } from '@angular/router'; +import { HotelFormComponent } from './hotel-form/hotel-form.component'; +import { HotelDetailsComponent } from './hotel-details/hotel-details.component'; + +export const formGuard: CanDeactivateFn = (component: HotelFormComponent | HotelDetailsComponent, currentRoute, currentState, nextState) => { + console.log(component) + if (component instanceof HotelFormComponent && component.form?.dirty) { + return confirm('You have unsaved changes. Do you really want to leave?'); + } + else if (component instanceof HotelDetailsComponent && component.hotelForm?.form?.dirty) { + return confirm('You have unsaved changes. Do you really want to leave?'); + } + + return true; +}; diff --git a/src/app/hotel-details/hotel-details.component.html b/src/app/hotel-details/hotel-details.component.html index 2ba9075..9f26099 100644 --- a/src/app/hotel-details/hotel-details.component.html +++ b/src/app/hotel-details/hotel-details.component.html @@ -1,5 +1,5 @@
-
- +
+
diff --git a/src/app/hotel-details/hotel-details.component.ts b/src/app/hotel-details/hotel-details.component.ts index aac11b2..750c869 100644 --- a/src/app/hotel-details/hotel-details.component.ts +++ b/src/app/hotel-details/hotel-details.component.ts @@ -1,23 +1,24 @@ import { HttpClient } from '@angular/common/http'; -import { Component, OnInit } from '@angular/core'; -import {ActivatedRoute, Router, RouterLink} from '@angular/router'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; import { Hotel } from '../HotelItem/hotel'; -import {CurrencyPipe, NgIf} from '@angular/common'; -import { StarRatingComponent } from '../star-rating/star-rating.component'; -import { HotelItem } from '../HotelItem/HotelItem.component'; +import { NgIf } from '@angular/common'; import { catchError, EMPTY } from 'rxjs'; -import {HotelFormComponent} from "../hotel-form/hotel-form.component"; +import { HotelFormComponent } from "../hotel-form/hotel-form.component"; @Component({ selector: 'app-hotel-details', standalone: true, - imports: [CurrencyPipe, StarRatingComponent, HotelItem, HotelFormComponent, NgIf, RouterLink], + imports: [HotelFormComponent, NgIf], templateUrl: './hotel-details.component.html', styleUrl: './hotel-details.component.css' }) export class HotelDetailsComponent implements OnInit { public hotel: any; + @ViewChild('hotelForm', { static: false }) + public hotelForm!: HotelFormComponent; + constructor(private route: ActivatedRoute, private http: HttpClient, private router: Router) { } ngOnInit(): void { diff --git a/src/app/hotel-form/hotel-form.component.html b/src/app/hotel-form/hotel-form.component.html index 93aa829..36b5ad9 100644 --- a/src/app/hotel-form/hotel-form.component.html +++ b/src/app/hotel-form/hotel-form.component.html @@ -57,7 +57,7 @@ delete
- +
@@ -65,8 +65,19 @@
- + +
+
+

Confirm Deletion

+

Are you sure you want to delete this hotel?

+
+ + +
+
+
\ No newline at end of file diff --git a/src/app/hotel-form/hotel-form.component.ts b/src/app/hotel-form/hotel-form.component.ts index c23ec43..0323c9a 100644 --- a/src/app/hotel-form/hotel-form.component.ts +++ b/src/app/hotel-form/hotel-form.component.ts @@ -1,4 +1,4 @@ -import { Component, inject, Inject, Input } from '@angular/core'; +import { Component, HostListener, inject, Inject, Input } from '@angular/core'; import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms"; import { Hotel } from "../HotelItem/hotel"; import { HttpClient } from '@angular/common/http'; @@ -32,6 +32,8 @@ export class HotelFormComponent { public router: Router = inject(Router); + public showDeleteConfirmation = false; + constructor(private http: HttpClient) {} ngOnInit() { @@ -66,7 +68,7 @@ export class HotelFormComponent { } public submit() { - if (this.form.valid) { + if (this.form.valid && !this.form.dirty) { const hotelData: Hotel = { id: this.hotel?.id || 0, hotelName: this.form.value.name, diff --git a/src/app/hotel-list/hotel-list.component.html b/src/app/hotel-list/hotel-list.component.html index 6f2d94c..be67e98 100644 --- a/src/app/hotel-list/hotel-list.component.html +++ b/src/app/hotel-list/hotel-list.component.html @@ -1,10 +1,28 @@
-

{{'hello' | uppercase | text}}

- - -
+
+
+

{{'hello' | uppercase | text}}

+ + +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
-- 2.45.3 From a6829fc3f5398a9fa414a93b5e3ca28437b4c051 Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Tue, 3 Dec 2024 09:23:50 +0100 Subject: [PATCH 4/5] add better validation to forms --- src/app/form.guard.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/form.guard.ts b/src/app/form.guard.ts index bec14c4..60b62b7 100644 --- a/src/app/form.guard.ts +++ b/src/app/form.guard.ts @@ -3,11 +3,11 @@ import { HotelFormComponent } from './hotel-form/hotel-form.component'; import { HotelDetailsComponent } from './hotel-details/hotel-details.component'; export const formGuard: CanDeactivateFn = (component: HotelFormComponent | HotelDetailsComponent, currentRoute, currentState, nextState) => { - console.log(component) - if (component instanceof HotelFormComponent && component.form?.dirty) { - return confirm('You have unsaved changes. Do you really want to leave?'); - } - else if (component instanceof HotelDetailsComponent && component.hotelForm?.form?.dirty) { + const hasUnsavedChanges = + (component instanceof HotelFormComponent && component.form?.dirty) || + (component instanceof HotelDetailsComponent && component.hotelForm?.form?.dirty); + + if (hasUnsavedChanges) { return confirm('You have unsaved changes. Do you really want to leave?'); } -- 2.45.3 From 6d520d4467f89dfa1a9be554918f9212815aa43e Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Tue, 17 Dec 2024 08:53:49 +0100 Subject: [PATCH 5/5] add angular ws stuff idk --- src/app/Button/button.component.html | 1 - src/app/Button/button.component.ts | 11 --- src/app/Child/child.component.html | 4 - src/app/Child/child.component.ts | 25 ------ src/app/Parent/parent.component.html | 9 --- src/app/Parent/parent.component.ts | 17 ---- src/app/Parent/services/hotel.service.ts | 47 ----------- src/app/Search/search.component.html | 3 +- src/app/Search/search.component.ts | 6 +- src/app/api/api.ts | 2 +- src/app/app.component.ts | 2 +- src/app/app.routes.ts | 6 +- .../hotel-details/hotel-details.component.ts | 2 +- src/app/hotel-form/hotel-form.component.html | 81 ++++++++----------- src/app/hotel-form/hotel-form.component.ts | 61 +++++++++++--- .../HotelItem.component.html | 0 .../HotelItem.component.ts | 0 src/app/{HotelItem => hotel-item}/hotel.ts | 0 src/app/hotel-list/hotel-list.component.html | 6 +- src/app/hotel-list/hotel-list.component.ts | 16 ++-- src/app/not-found/not-found.component.css | 0 src/app/not-found/not-found.component.html | 7 ++ src/app/not-found/not-found.component.spec.ts | 23 ++++++ src/app/not-found/not-found.component.ts | 17 ++++ src/app/service/hotel.service.ts | 51 ++++++++++++ tailwind.config.js | 9 --- 26 files changed, 204 insertions(+), 202 deletions(-) delete mode 100644 src/app/Button/button.component.html delete mode 100644 src/app/Button/button.component.ts delete mode 100644 src/app/Child/child.component.html delete mode 100644 src/app/Child/child.component.ts delete mode 100644 src/app/Parent/parent.component.html delete mode 100644 src/app/Parent/parent.component.ts delete mode 100644 src/app/Parent/services/hotel.service.ts rename src/app/{HotelItem => hotel-item}/HotelItem.component.html (100%) rename src/app/{HotelItem => hotel-item}/HotelItem.component.ts (100%) rename src/app/{HotelItem => hotel-item}/hotel.ts (100%) create mode 100644 src/app/not-found/not-found.component.css create mode 100644 src/app/not-found/not-found.component.html create mode 100644 src/app/not-found/not-found.component.spec.ts create mode 100644 src/app/not-found/not-found.component.ts create mode 100644 src/app/service/hotel.service.ts diff --git a/src/app/Button/button.component.html b/src/app/Button/button.component.html deleted file mode 100644 index 54fb491..0000000 --- a/src/app/Button/button.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/Button/button.component.ts b/src/app/Button/button.component.ts deleted file mode 100644 index c77daef..0000000 --- a/src/app/Button/button.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from "@angular/core"; - - -@Component({ - selector: 'app-button', - standalone: true, - templateUrl: './button.component.html', -}) -export class ButtonComponent { - public text = 'Test'; -} diff --git a/src/app/Child/child.component.html b/src/app/Child/child.component.html deleted file mode 100644 index 84f430a..0000000 --- a/src/app/Child/child.component.html +++ /dev/null @@ -1,4 +0,0 @@ -

Child

-

Balance: {{balance}}

-

I'm broke. Now I'm about as poor as Jan-Marlon.

- diff --git a/src/app/Child/child.component.ts b/src/app/Child/child.component.ts deleted file mode 100644 index e03c3ad..0000000 --- a/src/app/Child/child.component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Component, Input, Output, EventEmitter } from "@angular/core"; - -@Component({ - selector: 'app-child', - standalone: true, - templateUrl: './child.component.html', -}) -export class ChildComponent { - @Input() public balance: number = 0; - @Output() balanceChange = new EventEmitter(); - - public isBroke = false; - - public takeMoney() { - if (this.balance <= 0) { - this.isBroke = true; - } else { - this.balance -= 50; - if (this.balance == 0) { - this.isBroke = true; - } - this.balanceChange.emit(this.balance); - } - } -} diff --git a/src/app/Parent/parent.component.html b/src/app/Parent/parent.component.html deleted file mode 100644 index 6fc429c..0000000 --- a/src/app/Parent/parent.component.html +++ /dev/null @@ -1,9 +0,0 @@ -
-

Parent

-

Kontostand: {{ balance }}

- - - - -
- diff --git a/src/app/Parent/parent.component.ts b/src/app/Parent/parent.component.ts deleted file mode 100644 index 502bb94..0000000 --- a/src/app/Parent/parent.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component } from "@angular/core"; -import { ChildComponent } from "../Child/child.component"; - - -@Component({ - selector: 'app-parent', - standalone: true, - templateUrl: './parent.component.html', - imports: [ChildComponent], -}) -export class ParentComponent { - public balance = 1000; - - public addFifty() { - this.balance += 50; - } -} diff --git a/src/app/Parent/services/hotel.service.ts b/src/app/Parent/services/hotel.service.ts deleted file mode 100644 index 2eb1125..0000000 --- a/src/app/Parent/services/hotel.service.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Injectable } from "@angular/core"; -import { Hotel } from "../../HotelItem/hotel"; -import { from, Observable } from "rxjs"; - -@Injectable() -export class HotelService { - public getHotels(): Observable { - return from([ - { - "id": 1, - "hotelName": "Buea süßes Leben", - "description": "Schöne Aussicht am Meer", - "price": 230.5, - "imageUrl": "assets/img/1.jpg", - "rating": 3.5, - "tags": ["test"] - }, - { - "id": 2, - "hotelName": "Marrakesch", - "description": "Genießen Sie den Blick auf die Berge", - "price": 145.5, - "imageUrl": "assets/img/2.jpg", - "rating": 5, - "tags": ["test"] - }, - { - "id": 3, - "hotelName": "Abuja neuer Palast", - "description": "Kompletter Aufenthalt mit Autoservice", - "price": 120.12, - "imageUrl": "assets/img/3.jpg", - "rating": 4, - "tags": ["test"] - }, - { - "id": 4, - "hotelName": "Kapstadt Stadt", - "description": "Wunderschönes Ambiente für Ihren Aufenthalt", - "price": 135.12, - "imageUrl": "assets/img/4.jpg", - "rating": 2.5, - "tags": ["test"] - } - ]); - } -} diff --git a/src/app/Search/search.component.html b/src/app/Search/search.component.html index a408279..150b096 100644 --- a/src/app/Search/search.component.html +++ b/src/app/Search/search.component.html @@ -1 +1,2 @@ - + + diff --git a/src/app/Search/search.component.ts b/src/app/Search/search.component.ts index c3564a0..2c9bdb5 100644 --- a/src/app/Search/search.component.ts +++ b/src/app/Search/search.component.ts @@ -1,7 +1,5 @@ -import { CommonModule } from "@angular/common"; -import { Component, NgModule } from "@angular/core"; -import { FormsModule, NgForm, NgModel } from "@angular/forms"; -import { Input, Output } from "@angular/core"; +import { Component, Input, Output } from "@angular/core"; +import { FormsModule } from "@angular/forms"; import { EventEmitter } from "@angular/core"; @Component({ diff --git a/src/app/api/api.ts b/src/app/api/api.ts index b330010..6aac176 100644 --- a/src/app/api/api.ts +++ b/src/app/api/api.ts @@ -1,6 +1,6 @@ import { InMemoryDbService } from 'angular-in-memory-web-api'; -import { Hotel } from '../HotelItem/hotel'; +import { Hotel } from '../hotel-item/hotel'; /** * Initial data for in memory web api diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 2a3bdb6..8725472 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,5 @@ import { Component, Injectable } from '@angular/core'; -import { HotelService } from './Parent/services/hotel.service'; +import { HotelService } from './service/hotel.service'; import { RouterOutlet } from '@angular/router'; @Injectable({providedIn: "root"}) diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 46c3c18..f64b610 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -2,8 +2,8 @@ import { Routes } from '@angular/router'; import { HotelDetailsComponent } from './hotel-details/hotel-details.component'; import { HotelListComponent } from './hotel-list/hotel-list.component'; import { HotelFormComponent } from './hotel-form/hotel-form.component'; -import { inject } from '@angular/core'; import { formGuard } from './form.guard'; +import { NotFoundComponent } from './not-found/not-found.component'; export const routes: Routes = [ { @@ -20,4 +20,8 @@ export const routes: Routes = [ component: HotelFormComponent, canDeactivate: [formGuard], }, + { + path: "**", + component: NotFoundComponent, + }, ]; diff --git a/src/app/hotel-details/hotel-details.component.ts b/src/app/hotel-details/hotel-details.component.ts index 750c869..5721836 100644 --- a/src/app/hotel-details/hotel-details.component.ts +++ b/src/app/hotel-details/hotel-details.component.ts @@ -1,7 +1,7 @@ import { HttpClient } from '@angular/common/http'; import { Component, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Hotel } from '../HotelItem/hotel'; +import { Hotel } from '../hotel-item/hotel'; import { NgIf } from '@angular/common'; import { catchError, EMPTY } from 'rxjs'; import { HotelFormComponent } from "../hotel-form/hotel-form.component"; diff --git a/src/app/hotel-form/hotel-form.component.html b/src/app/hotel-form/hotel-form.component.html index 36b5ad9..64b99a8 100644 --- a/src/app/hotel-form/hotel-form.component.html +++ b/src/app/hotel-form/hotel-form.component.html @@ -1,57 +1,40 @@
-
- - -
-
Hotel Name is required.
-
Hotel Name must be at least 3 characters long.
-
-
-
- - -
-
Hotel Description is required.
-
Hotel Description must be at least 3 characters long. + +
+ + +
+ {{errorMessage}}
-
-
- - -
-
Hotel Price is required.
-
Hotel Price must be a positive number.
-
-
-
- - -
-
Hotel Rating is required.
-
Hotel Rating must be at least 0.
-
Hotel Rating cannot be more than 5.
-
-
+ +
+
+ {{errorMessage}} +
+
@@ -75,7 +62,7 @@

Are you sure you want to delete this hotel?

- +
diff --git a/src/app/hotel-form/hotel-form.component.ts b/src/app/hotel-form/hotel-form.component.ts index 0323c9a..5998b0e 100644 --- a/src/app/hotel-form/hotel-form.component.ts +++ b/src/app/hotel-form/hotel-form.component.ts @@ -1,10 +1,10 @@ -import { Component, HostListener, inject, Inject, Input } from '@angular/core'; -import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms"; -import { Hotel } from "../HotelItem/hotel"; +import { Component, inject, Input } from '@angular/core'; +import { AbstractControl, FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms"; +import { Hotel } from "../hotel-item/hotel"; import { HttpClient } from '@angular/common/http'; -import { catchError } from "rxjs"; +import { catchError, debounceTime } from "rxjs"; import { Router, RouterLink } from "@angular/router"; -import { NgFor, NgIf } from "@angular/common"; +import { NgClass, NgFor, NgIf } from "@angular/common"; import { MatButtonModule } from "@angular/material/button"; import { MatIconModule } from "@angular/material/icon"; import { FormsModule } from '@angular/forms'; @@ -19,7 +19,8 @@ import { FormsModule } from '@angular/forms'; MatButtonModule, MatIconModule, NgFor, - FormsModule + FormsModule, + NgClass ], templateUrl: './hotel-form.component.html', styleUrl: './hotel-form.component.css' @@ -34,7 +35,9 @@ export class HotelFormComponent { public showDeleteConfirmation = false; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient) { } + + public errorMessage: string = ''; ngOnInit() { this.form = new FormGroup({ @@ -44,9 +47,18 @@ export class HotelFormComponent { rating: new FormControl(this.hotel?.rating || '', [Validators.min(0), Validators.max(5), Validators.required]), tags: new FormArray(this.hotel?.tags?.map(tag => new FormControl(tag)) || []) }); + + Object.keys(this.form.controls).forEach(key => { + const control = this.form.get(key); + control?.valueChanges.pipe( + debounceTime(1000) + ).subscribe(() => { + this.setError(control as AbstractControl, key); + }); + }); } - public delete(id: number) { + public delete() { this.http.delete(`api/hotels/${this.hotel?.id}`).pipe( catchError(err => { console.error('Error deleting hotel', err); @@ -68,7 +80,7 @@ export class HotelFormComponent { } public submit() { - if (this.form.valid && !this.form.dirty) { + if (this.form.valid && !this.errorMessage) { const hotelData: Hotel = { id: this.hotel?.id || 0, hotelName: this.form.value.name, @@ -101,7 +113,38 @@ export class HotelFormComponent { }); } + this.form.reset(); this.router.navigate(['/']); } } + + private validationErrors: Record = { + 'required': 'This field is required', + 'minlength': 'This field must be at least 3 characters long', + 'min': 'This field must be at least 0', + 'max': 'This field must be at most 5', + 'name.required': 'Hotel name is required', + 'name.minlength': 'Hotel name must be at least 3 characters long', + 'description.required': 'Hotel description is required', + 'description.minlength': 'Hotel description must be at least 3 characters long', + 'price.required': 'Hotel price is required', + 'price.min': 'Hotel price cannot be negative', + 'rating.required': 'Hotel rating is required', + 'rating.min': 'Hotel rating must be at least 0', + 'rating.max': 'Hotel rating cannot be more than 5', + 'tags.required': 'At least one tag is required', + }; + + private setError(control: AbstractControl, controlName: string) { + if ((control.touched || control.dirty) && control.errors) { + const errors = Object.keys(control.errors).map(key => { + const errorKey = `${controlName}.${key}`; + return this.validationErrors[errorKey] || this.validationErrors[key]; + }); + this.errorMessage = errors.join(' '); + } else { + this.errorMessage = ''; + } + } } + diff --git a/src/app/HotelItem/HotelItem.component.html b/src/app/hotel-item/HotelItem.component.html similarity index 100% rename from src/app/HotelItem/HotelItem.component.html rename to src/app/hotel-item/HotelItem.component.html diff --git a/src/app/HotelItem/HotelItem.component.ts b/src/app/hotel-item/HotelItem.component.ts similarity index 100% rename from src/app/HotelItem/HotelItem.component.ts rename to src/app/hotel-item/HotelItem.component.ts diff --git a/src/app/HotelItem/hotel.ts b/src/app/hotel-item/hotel.ts similarity index 100% rename from src/app/HotelItem/hotel.ts rename to src/app/hotel-item/hotel.ts diff --git a/src/app/hotel-list/hotel-list.component.html b/src/app/hotel-list/hotel-list.component.html index be67e98..5aab0f6 100644 --- a/src/app/hotel-list/hotel-list.component.html +++ b/src/app/hotel-list/hotel-list.component.html @@ -1,20 +1,20 @@
-

{{'hello' | uppercase | text}}

+

{{'hello' | uppercase | text}}

-
+
-
+
diff --git a/src/app/hotel-list/hotel-list.component.ts b/src/app/hotel-list/hotel-list.component.ts index 876c910..0711bfd 100644 --- a/src/app/hotel-list/hotel-list.component.ts +++ b/src/app/hotel-list/hotel-list.component.ts @@ -1,20 +1,14 @@ -import { CommonModule, NgFor, NgIf, UpperCasePipe } from '@angular/common'; +import { NgFor, NgIf, UpperCasePipe } from '@angular/common'; import { Component, inject } from '@angular/core'; import { TextPipe } from '../../text.pipe'; -import { SearchComponent } from '../Search/search.component'; -import { HotelService } from '../Parent/services/hotel.service'; -import { Hotel } from '../HotelItem/hotel'; +import { SearchComponent } from '../search/search.component'; +import { HotelService } from '../service/hotel.service'; +import { Hotel } from '../hotel-item/hotel'; import { HttpClient } from '@angular/common/http'; -import { filter, from, last, map, Observable, scan } from 'rxjs'; -import { HotelItem } from '../HotelItem/HotelItem.component'; +import { HotelItem } from '../hotel-item/HotelItem.component'; import { RouterLink } from '@angular/router'; import { MatButtonModule } from '@angular/material/button'; -interface User { - name: string; - age: number; -} - @Component({ selector: 'app-hotel-list', standalone: true, diff --git a/src/app/not-found/not-found.component.css b/src/app/not-found/not-found.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/not-found/not-found.component.html b/src/app/not-found/not-found.component.html new file mode 100644 index 0000000..62c460a --- /dev/null +++ b/src/app/not-found/not-found.component.html @@ -0,0 +1,7 @@ +
+
+

404

+

Page Not Found

+ Return Home +
+
diff --git a/src/app/not-found/not-found.component.spec.ts b/src/app/not-found/not-found.component.spec.ts new file mode 100644 index 0000000..5b65d9e --- /dev/null +++ b/src/app/not-found/not-found.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NotFoundComponent } from './not-found.component'; + +describe('NotFoundComponent', () => { + let component: NotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [NotFoundComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(NotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/not-found/not-found.component.ts b/src/app/not-found/not-found.component.ts new file mode 100644 index 0000000..10c6221 --- /dev/null +++ b/src/app/not-found/not-found.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { RouterLink } from '@angular/router'; + +@Component({ + selector: 'app-not-found', + standalone: true, + imports: [ + MatButtonModule, + RouterLink + ], + templateUrl: './not-found.component.html', + styleUrl: './not-found.component.css' +}) +export class NotFoundComponent { + +} diff --git a/src/app/service/hotel.service.ts b/src/app/service/hotel.service.ts new file mode 100644 index 0000000..31a5f02 --- /dev/null +++ b/src/app/service/hotel.service.ts @@ -0,0 +1,51 @@ +import { Injectable } from "@angular/core"; +import { Hotel } from "../hotel-item/hotel"; +import { HttpClient, HttpErrorResponse } from "@angular/common/http"; +import { Observable, throwError, catchError } from "rxjs"; + +@Injectable({ + providedIn: 'root' +}) +export class HotelService { + private apiUrl = 'api/hotels'; + + constructor(private http: HttpClient) {} + + private handleError(error: HttpErrorResponse) { + let errorMessage = 'An error occurred'; + if (error.error instanceof ErrorEvent) { + // Client-side error + errorMessage = `Error: ${error.error.message}`; + } else { + // Server-side error + errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`; + } + console.error(errorMessage); + return throwError(() => errorMessage); + } + + public getHotels(): Observable { + return this.http.get(this.apiUrl) + .pipe(catchError(this.handleError)); + } + + public getHotel(id: number): Observable { + return this.http.get(`${this.apiUrl}/${id}`) + .pipe(catchError(this.handleError)); + } + + public createHotel(hotel: Hotel): Observable { + return this.http.post(this.apiUrl, hotel) + .pipe(catchError(this.handleError)); + } + + public updateHotel(hotel: Hotel): Observable { + return this.http.put(`${this.apiUrl}/${hotel.id}`, hotel) + .pipe(catchError(this.handleError)); + } + + public deleteHotel(id: number): Observable<{}> { + return this.http.delete(`${this.apiUrl}/${id}`) + .pipe(catchError(this.handleError)); + } +} diff --git a/tailwind.config.js b/tailwind.config.js index 068e770..d5856ac 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,18 +1,9 @@ /** @type {import('tailwindcss').Config} */ -const colors = require('tailwindcss/colors'); - module.exports = { content: [ "./src/**/*.{html,ts}", ], - theme: { - extend: { - colors: { - ...colors, - } - }, - }, plugins: [], } -- 2.45.3