jleibl_hotel_manager #20
@ -1 +0,0 @@
|
||||
<button type="button" class="px-6 py-2 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75">{{ text }}</button>
|
@ -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';
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
<h1 class="text-2xl font-bold mb-4">Child</h1>
|
||||
<p class="text-lg mb-2">Balance: {{balance}}</p>
|
||||
<p [class.hidden]="!isBroke" class="text-red-500 mb-4">I'm broke. Now I'm about as poor as Jan-Marlon.</p>
|
||||
<button type="button" (click)="takeMoney()" class="px-6 py-2 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75">Take Money</button>
|
@ -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<number>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<div class="m-3">
|
||||
<h1>Parent</h1>
|
||||
<p>Kontostand: {{ balance }}</p>
|
||||
<button type="button" class="button" (click)=addFifty()>Add Money</button>
|
||||
|
||||
<app-child [(balance)]="balance"></app-child>
|
||||
<app-child [(balance)]="balance"></app-child>
|
||||
</div>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<Hotel> {
|
||||
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"]
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
<input [(ngModel)]="input" (ngModelChange)="update($event)" class="border border-gray-300 rounded-md p-2 w-full focus:border-blue-500 focus:ring focus:ring-blue-200" type="search">
|
||||
<label for="search">Search</label>
|
||||
<input [(ngModel)]="input" (ngModelChange)="update($event)" id="search" class="border border-gray-300 rounded-md p-2 w-full focus:border-blue-500 focus:ring focus:ring-blue-200" type="search">
|
||||
|
@ -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({
|
||||
|
@ -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
|
||||
|
@ -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"})
|
||||
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
@ -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";
|
||||
|
@ -1,57 +1,40 @@
|
||||
<form class="space-y-4 max-w-md mx-auto mt-4" [formGroup]="form">
|
||||
<div class="form-group">
|
||||
<label for="hotelName" class="block text-sm font-medium text-gray-700">Hotel Name</label>
|
||||
<input type="text" formControlName="name"
|
||||
class="form-control mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200"
|
||||
id="hotelName" placeholder="Enter hotel name">
|
||||
<div *ngIf="form.get('name')?.invalid && (form.get('name')?.dirty || form.get('name')?.touched)"
|
||||
class="text-red-500 text-sm mt-1">
|
||||
<div *ngIf="form.get('name')?.errors?.['required']">Hotel Name is required.</div>
|
||||
<div *ngIf="form.get('name')?.errors?.['minlength']">Hotel Name must be at least 3 characters long.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hotelDescription" class="block text-sm font-medium text-gray-700">Hotel Description</label>
|
||||
<input type="text" formControlName="description"
|
||||
class="form-control mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200"
|
||||
id="hotelDescription" placeholder="Enter hotel description">
|
||||
<div
|
||||
*ngIf="form.get('description')?.invalid && (form.get('description')?.dirty || form.get('description')?.touched)"
|
||||
class="text-red-500 text-sm mt-1">
|
||||
<div *ngIf="form.get('description')?.errors?.['required']">Hotel Description is required.</div>
|
||||
<div *ngIf="form.get('description')?.errors?.['minlength']">Hotel Description must be at least 3 characters long.
|
||||
<ng-container *ngFor="let field of ['name', 'description', 'price', 'rating']">
|
||||
<div class="form-group">
|
||||
<label [for]="'hotel' + field" class="block text-sm font-medium text-gray-700">
|
||||
Hotel {{field.charAt(0).toUpperCase() + field.slice(1)}}
|
||||
</label>
|
||||
<input [type]="field === 'price' || field === 'rating' ? 'number' : 'text'" [formControlName]="field"
|
||||
class="form-control mt-1 block w-full p-2 border rounded-md shadow-sm"
|
||||
[ngClass]="{
|
||||
'border-red-500': errorMessage.includes(field),
|
||||
'border-gray-300': !errorMessage.includes(field),
|
||||
'focus:border-red-500': errorMessage.includes(field),
|
||||
'focus:border-blue-500': !errorMessage.includes(field),
|
||||
'focus:ring-red-200': errorMessage.includes(field),
|
||||
'focus:ring-blue-200': !errorMessage.includes(field)
|
||||
}"
|
||||
[id]="'hotel' + field" [placeholder]="'Enter hotel ' + field">
|
||||
<div *ngIf="errorMessage?.includes(field)" class="text-red-500 text-sm mt-1">
|
||||
{{errorMessage}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hotelPrice" class="block text-sm font-medium text-gray-700">Hotel Price</label>
|
||||
<input type="number" formControlName="price"
|
||||
class="form-control mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200"
|
||||
id="hotelPrice" placeholder="Enter hotel price">
|
||||
<div *ngIf="form.get('price')?.invalid && (form.get('price')?.dirty || form.get('price')?.touched)"
|
||||
class="text-red-500 text-sm mt-1">
|
||||
<div *ngIf="form.get('price')?.errors?.['required']">Hotel Price is required.</div>
|
||||
<div *ngIf="form.get('price')?.errors?.['min']">Hotel Price must be a positive number.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="hotelRating" class="block text-sm font-medium text-gray-700">Hotel Rating</label>
|
||||
<input type="number" formControlName="rating"
|
||||
class="form-control mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200"
|
||||
id="hotelRating" placeholder="Enter hotel rating">
|
||||
<div *ngIf="form.get('rating')?.invalid && (form.get('rating')?.dirty || form.get('rating')?.touched)"
|
||||
class="text-red-500 text-sm mt-1">
|
||||
<div *ngIf="form.get('rating')?.errors?.['required']">Hotel Rating is required.</div>
|
||||
<div *ngIf="form.get('rating')?.errors?.['min']">Hotel Rating must be at least 0.</div>
|
||||
<div *ngIf="form.get('rating')?.errors?.['max']">Hotel Rating cannot be more than 5.</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="hotelTags" class="block text-sm font-medium text-gray-700">Hotel Tags</label>
|
||||
<div formArrayName="tags">
|
||||
<div *ngFor="let tag of tags.controls; let i=index" class="flex items-center space-x-2">
|
||||
<input type="text" [formControlName]="i"
|
||||
class="form-control mt-1 w-full p-2 border border-gray-300 rounded-md shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200"
|
||||
class="form-control mt-1 w-full p-2 border rounded-md shadow-sm"
|
||||
[ngClass]="{
|
||||
'border-red-500': errorMessage.includes('tags'),
|
||||
'border-gray-300': !errorMessage.includes('tags'),
|
||||
'focus:border-red-500': errorMessage.includes('tags'),
|
||||
'focus:border-blue-500': !errorMessage.includes('tags'),
|
||||
'focus:ring-red-200': errorMessage.includes('tags'),
|
||||
'focus:ring-blue-200': !errorMessage.includes('tags')
|
||||
}"
|
||||
[id]="'hotelTag' + i" placeholder="Enter hotel tag">
|
||||
<button mat-icon-button (click)="tags.removeAt(i)" class="mt-1">
|
||||
<mat-icon class="text-red-500">delete</mat-icon>
|
||||
@ -59,7 +42,11 @@
|
||||
</div>
|
||||
<button type="button" mat-button (click)="addTag()" class="mt-2">Add Tag</button>
|
||||
</div>
|
||||
<div *ngIf="errorMessage.includes('tags')" class="text-red-500 text-sm mt-1">
|
||||
{{errorMessage}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between">
|
||||
<div class="flex space-x-1">
|
||||
<button type="submit" mat-flat-button (click)="submit()">Submit</button>
|
||||
@ -75,7 +62,7 @@
|
||||
<p class="mb-6">Are you sure you want to delete this hotel?</p>
|
||||
<div class="flex justify-end space-x-2">
|
||||
<button mat-stroked-button (click)="showDeleteConfirmation = false">Cancel</button>
|
||||
<button mat-flat-button color="warn" (click)="delete(hotel.id)">Delete</button>
|
||||
<button mat-flat-button color="warn" (click)="delete()">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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<string, string> = {
|
||||
'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 = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
<div class="container p-4 mx-auto max-w-4xl">
|
||||
<div class="fixed top-0 left-0 w-full bg-white/80 backdrop-blur-xl z-50">
|
||||
<div class="justify-center max-w-4xl container p-4 mx-auto">
|
||||
<h1>{{'hello' | uppercase | text}}</h1>
|
||||
<h1 class="">{{'hello' | uppercase | text}}</h1>
|
||||
<app-search [(input)]="search"></app-search>
|
||||
<button routerLink="/create-hotel" mat-flat-button
|
||||
class="btn btn-primary bg-blue-500 text-white font-semibold py-2 px-4 rounded-md shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75 my-4">Create
|
||||
New Hotel</button>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="hotels[0].hotelName" class="mt-56">
|
||||
<div *ngIf="hotels[0].hotelName" class="mt-64">
|
||||
<div *ngFor="let hotel of hotels">
|
||||
<app-hotel-item *ngIf="hotel.hotelName.toLowerCase().includes(search.toLowerCase())" [hotel]="hotel"></app-hotel-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!hotels[0].hotelName" class="mt-56">
|
||||
<div *ngIf="!hotels[0].hotelName" class="mt-64">
|
||||
<div class="animate-pulse space-y-4">
|
||||
<div class="h-8 bg-gray-200 rounded w-52"></div>
|
||||
<div class="space-y-3">
|
||||
|
@ -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,
|
||||
|
0
src/app/not-found/not-found.component.css
Normal file
0
src/app/not-found/not-found.component.css
Normal file
7
src/app/not-found/not-found.component.html
Normal file
7
src/app/not-found/not-found.component.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div class="min-h-screen flex items-center justify-center bg-gray-100">
|
||||
<div class="text-center">
|
||||
<h1 class="text-9xl font-bold text-gray-800">404</h1>
|
||||
<p class="text-2xl font-medium text-gray-600 mb-8">Page Not Found</p>
|
||||
<a type="submit" mat-flat-button routerLink="/">Return Home</a>
|
||||
</div>
|
||||
</div>
|
23
src/app/not-found/not-found.component.spec.ts
Normal file
23
src/app/not-found/not-found.component.spec.ts
Normal file
@ -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<NotFoundComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [NotFoundComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(NotFoundComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
17
src/app/not-found/not-found.component.ts
Normal file
17
src/app/not-found/not-found.component.ts
Normal file
@ -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 {
|
||||
|
||||
}
|
51
src/app/service/hotel.service.ts
Normal file
51
src/app/service/hotel.service.ts
Normal file
@ -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<Hotel[]> {
|
||||
return this.http.get<Hotel[]>(this.apiUrl)
|
||||
.pipe(catchError(this.handleError));
|
||||
}
|
||||
|
||||
public getHotel(id: number): Observable<Hotel> {
|
||||
return this.http.get<Hotel>(`${this.apiUrl}/${id}`)
|
||||
.pipe(catchError(this.handleError));
|
||||
}
|
||||
|
||||
public createHotel(hotel: Hotel): Observable<Hotel> {
|
||||
return this.http.post<Hotel>(this.apiUrl, hotel)
|
||||
.pipe(catchError(this.handleError));
|
||||
}
|
||||
|
||||
public updateHotel(hotel: Hotel): Observable<Hotel> {
|
||||
return this.http.put<Hotel>(`${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));
|
||||
}
|
||||
}
|
@ -1,18 +1,9 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
|
||||
const colors = require('tailwindcss/colors');
|
||||
|
||||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{html,ts}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
...colors,
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user