-
Hotel Description is required.
-
Hotel Description must be at least 3 characters long.
+
+
-
-
-
+
+
+
@@ -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 @@
+
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: [],
}