update styling and do stuff
This commit is contained in:
parent
21eb309acc
commit
dd4faea712
@ -28,6 +28,7 @@
|
|||||||
"src/assets"
|
"src/assets"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
|
"@angular/material/prebuilt-themes/azure-blue.css",
|
||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
@ -86,6 +87,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
|
"@angular/material/prebuilt-themes/azure-blue.css",
|
||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
|
56
package-lock.json
generated
56
package-lock.json
generated
@ -9,13 +9,16 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^18.2.3",
|
"@angular/animations": "^18.2.3",
|
||||||
|
"@angular/cdk": "^18.2.3",
|
||||||
"@angular/common": "^18.2.3",
|
"@angular/common": "^18.2.3",
|
||||||
"@angular/compiler": "^18.2.3",
|
"@angular/compiler": "^18.2.3",
|
||||||
"@angular/core": "^18.2.3",
|
"@angular/core": "^18.2.3",
|
||||||
"@angular/forms": "^18.2.3",
|
"@angular/forms": "^18.2.3",
|
||||||
|
"@angular/material": "^18.2.3",
|
||||||
"@angular/platform-browser": "^18.2.3",
|
"@angular/platform-browser": "^18.2.3",
|
||||||
"@angular/platform-browser-dynamic": "^18.2.3",
|
"@angular/platform-browser-dynamic": "^18.2.3",
|
||||||
"@angular/router": "^18.2.3",
|
"@angular/router": "^18.2.3",
|
||||||
|
"angular-in-memory-web-api": "^0.18.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.14.3"
|
"zone.js": "~0.14.3"
|
||||||
@ -385,6 +388,23 @@
|
|||||||
"node": ">=10"
|
"node": ">=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": {
|
"node_modules/@angular/cli": {
|
||||||
"version": "18.2.3",
|
"version": "18.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.3.tgz",
|
||||||
@ -531,6 +551,24 @@
|
|||||||
"rxjs": "^6.5.3 || ^7.4.0"
|
"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": {
|
"node_modules/@angular/platform-browser": {
|
||||||
"version": "18.2.3",
|
"version": "18.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.3.tgz",
|
||||||
@ -4763,6 +4801,20 @@
|
|||||||
"ajv": "^8.8.2"
|
"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": {
|
"node_modules/ansi-colors": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
||||||
@ -6487,7 +6539,7 @@
|
|||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
@ -10467,7 +10519,7 @@
|
|||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
|
||||||
"integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
|
"integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"entities": "^4.4.0"
|
"entities": "^4.4.0"
|
||||||
|
@ -11,10 +11,12 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^18.2.3",
|
"@angular/animations": "^18.2.3",
|
||||||
|
"@angular/cdk": "^18.2.3",
|
||||||
"@angular/common": "^18.2.3",
|
"@angular/common": "^18.2.3",
|
||||||
"@angular/compiler": "^18.2.3",
|
"@angular/compiler": "^18.2.3",
|
||||||
"@angular/core": "^18.2.3",
|
"@angular/core": "^18.2.3",
|
||||||
"@angular/forms": "^18.2.3",
|
"@angular/forms": "^18.2.3",
|
||||||
|
"@angular/material": "^18.2.3",
|
||||||
"@angular/platform-browser": "^18.2.3",
|
"@angular/platform-browser": "^18.2.3",
|
||||||
"@angular/platform-browser-dynamic": "^18.2.3",
|
"@angular/platform-browser-dynamic": "^18.2.3",
|
||||||
"@angular/router": "^18.2.3",
|
"@angular/router": "^18.2.3",
|
||||||
@ -39,4 +41,4 @@
|
|||||||
"tailwindcss": "^3.4.10",
|
"tailwindcss": "^3.4.10",
|
||||||
"typescript": "~5.5.2"
|
"typescript": "~5.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,17 +1,14 @@
|
|||||||
import { Component, Injectable, Input } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
import { ChildComponent } from "../Child/child.component";
|
|
||||||
import { Hotel } from "./hotel";
|
import { Hotel } from "./hotel";
|
||||||
import { CurrencyPipe, NgIf } from "@angular/common";
|
import {CurrencyPipe, NgForOf, NgIf} from "@angular/common";
|
||||||
import { FormsModule } from "@angular/forms";
|
import { FormsModule } from "@angular/forms";
|
||||||
import { StarRatingComponent } from "../star-rating/star-rating.component";
|
|
||||||
import { HttpClient } from "@angular/common/http";
|
|
||||||
import { RouterLink } from "@angular/router";
|
import { RouterLink } from "@angular/router";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hotel-item',
|
selector: 'app-hotel-item',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
templateUrl: './HotelItem.component.html',
|
templateUrl: './HotelItem.component.html',
|
||||||
imports: [ChildComponent, CurrencyPipe, FormsModule, StarRatingComponent, NgIf, RouterLink],
|
imports: [CurrencyPipe, FormsModule, NgIf, RouterLink, NgForOf],
|
||||||
})
|
})
|
||||||
export class HotelItem {
|
export class HotelItem {
|
||||||
|
|
||||||
|
@ -1,5 +1 @@
|
|||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
<!-- <p>ID: {{response.id}}</p> -->
|
|
||||||
<!-- <p>userId: {{response.userId}}</p> -->
|
|
||||||
<!-- <p>title: {{response.title}}</p> -->
|
|
||||||
<!-- <p>completed: {{response.completed}}</p> -->
|
|
@ -1,20 +1,12 @@
|
|||||||
import { Component, Injectable } from '@angular/core';
|
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 { 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';
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
|
||||||
@Injectable({providedIn: "root"})
|
@Injectable({providedIn: "root"})
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [RouterOutlet, NgFor, NgForOf, NgIf, HotelItem, SearchComponent, UpperCasePipe, TextPipe, AsyncPipe],
|
imports: [RouterOutlet],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
providers: [HotelService],
|
providers: [HotelService],
|
||||||
styleUrl: './app.component.css'
|
styleUrl: './app.component.css'
|
||||||
|
@ -8,6 +8,7 @@ import { registerLocaleData } from '@angular/common';
|
|||||||
import { provideHttpClient } from '@angular/common/http';
|
import { provideHttpClient } from '@angular/common/http';
|
||||||
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
|
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
|
||||||
import { HotelData } from './api/api';
|
import { HotelData } from './api/api';
|
||||||
|
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||||
|
|
||||||
registerLocaleData(localeDe, 'de-DE');
|
registerLocaleData(localeDe, 'de-DE');
|
||||||
registerLocaleData(localeCn, 'cn-CN');
|
registerLocaleData(localeCn, 'cn-CN');
|
||||||
@ -16,5 +17,5 @@ export const appConfig: ApplicationConfig = {
|
|||||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||||
provideRouter(routes),
|
provideRouter(routes),
|
||||||
provideHttpClient(),
|
provideHttpClient(),
|
||||||
importProvidersFrom(InMemoryWebApiModule.forRoot(HotelData))]
|
importProvidersFrom(InMemoryWebApiModule.forRoot(HotelData)), provideAnimationsAsync()]
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import { HotelDetailsComponent } from './hotel-details/hotel-details.component';
|
import { HotelDetailsComponent } from './hotel-details/hotel-details.component';
|
||||||
import { HotelListComponent } from './hotel-list/hotel-list.component';
|
import { HotelListComponent } from './hotel-list/hotel-list.component';
|
||||||
|
import { HotelFormComponent } from './hotel-form/hotel-form.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -11,4 +12,8 @@ export const routes: Routes = [
|
|||||||
path: "hotels/:id",
|
path: "hotels/:id",
|
||||||
component: HotelDetailsComponent,
|
component: HotelDetailsComponent,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "create-hotel",
|
||||||
|
component: HotelFormComponent,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
@ -1 +1,5 @@
|
|||||||
<app-hotel-item [hotel]="hotel" [isDetail]="true"></app-hotel-item>
|
<div class="container p-4 mx-auto max-w-4xl">
|
||||||
|
<div class="border border-gray-500 p-4 rounded mt-4" *ngIf="hotel">
|
||||||
|
<app-hotel-form [hotel]="hotel"></app-hotel-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Component, OnInit } from '@angular/core';
|
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 { Hotel } from '../HotelItem/hotel';
|
||||||
import { CurrencyPipe } from '@angular/common';
|
import {CurrencyPipe, NgIf} from '@angular/common';
|
||||||
import { StarRatingComponent } from '../star-rating/star-rating.component';
|
import { StarRatingComponent } from '../star-rating/star-rating.component';
|
||||||
import { HotelItem } from '../HotelItem/HotelItem.component';
|
import { HotelItem } from '../HotelItem/HotelItem.component';
|
||||||
import { catchError, EMPTY } from 'rxjs';
|
import { catchError, EMPTY } from 'rxjs';
|
||||||
|
import {HotelFormComponent} from "../hotel-form/hotel-form.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hotel-details',
|
selector: 'app-hotel-details',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CurrencyPipe, StarRatingComponent, HotelItem],
|
imports: [CurrencyPipe, StarRatingComponent, HotelItem, HotelFormComponent, NgIf, RouterLink],
|
||||||
templateUrl: './hotel-details.component.html',
|
templateUrl: './hotel-details.component.html',
|
||||||
styleUrl: './hotel-details.component.css'
|
styleUrl: './hotel-details.component.css'
|
||||||
})
|
})
|
||||||
|
0
src/app/hotel-form/hotel-form.component.css
Normal file
0
src/app/hotel-form/hotel-form.component.css
Normal file
58
src/app/hotel-form/hotel-form.component.html
Normal file
58
src/app/hotel-form/hotel-form.component.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<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.
|
||||||
|
</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>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="flex space-x-1">
|
||||||
|
<button type="submit" mat-flat-button (click)="submit()">Submit</button>
|
||||||
|
<button type="button" mat-stroked-button routerLink="/">Cancel</button>
|
||||||
|
</div>
|
||||||
|
<button mat-icon-button aria-label="Delete hotel" *ngIf="hotel" (click)="delete(hotel.id)">
|
||||||
|
<mat-icon class="text-red-500">delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
23
src/app/hotel-form/hotel-form.component.spec.ts
Normal file
23
src/app/hotel-form/hotel-form.component.spec.ts
Normal file
@ -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<HotelFormComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [HotelFormComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(HotelFormComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
93
src/app/hotel-form/hotel-form.component.ts
Normal file
93
src/app/hotel-form/hotel-form.component.ts
Normal file
@ -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(['/']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
<div class="container p-4 mx-auto max-w-4xl">
|
<div class="container p-4 mx-auto max-w-4xl">
|
||||||
<h1>{{'hello' | uppercase | text}}</h1>
|
<h1>{{'hello' | uppercase | text}}</h1>
|
||||||
<app-search [(input)]="search"></app-search>
|
<app-search [(input)]="search"></app-search>
|
||||||
@if (hotels[0].hotelName) {
|
<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 *ngIf="hotels[0].hotelName">
|
||||||
<div *ngFor="let hotel of hotels">
|
<div *ngFor="let hotel of hotels">
|
||||||
<app-hotel-item *ngIf="hotel.hotelName.toLowerCase().includes(search.toLowerCase())" [hotel]="hotel"></app-hotel-item>
|
<app-hotel-item *ngIf="hotel.hotelName.toLowerCase().includes(search.toLowerCase())" [hotel]="hotel"></app-hotel-item>
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,6 +7,8 @@ import { Hotel } from '../HotelItem/hotel';
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { filter, from, last, map, Observable, scan } from 'rxjs';
|
import { filter, from, last, map, Observable, scan } from 'rxjs';
|
||||||
import { HotelItem } from '../HotelItem/HotelItem.component';
|
import { HotelItem } from '../HotelItem/HotelItem.component';
|
||||||
|
import { RouterLink } from '@angular/router';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
name: string;
|
name: string;
|
||||||
@ -16,7 +18,7 @@ interface User {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hotel-list',
|
selector: 'app-hotel-list',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [UpperCasePipe, TextPipe, SearchComponent, HotelItem, NgFor, NgIf],
|
imports: [UpperCasePipe, TextPipe, SearchComponent, HotelItem, NgFor, NgIf, RouterLink, MatButtonModule],
|
||||||
templateUrl: './hotel-list.component.html',
|
templateUrl: './hotel-list.component.html',
|
||||||
styleUrl: './hotel-list.component.css'
|
styleUrl: './hotel-list.component.css'
|
||||||
})
|
})
|
||||||
@ -33,41 +35,5 @@ export class HotelListComponent {
|
|||||||
this.http.get<Array<Hotel>>("api/hotels").subscribe(res => {
|
this.http.get<Array<Hotel>>("api/hotels").subscribe(res => {
|
||||||
this.hotels = 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<User> = 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<number | string> = 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)),
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,10 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
<link rel="stylesheet" href="./styles.css">
|
<link rel="stylesheet" href="./styles.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="mat-typography">
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -6,3 +6,6 @@
|
|||||||
.button {
|
.button {
|
||||||
@apply border-black rounded px-3 py-1 border-[3px];
|
@apply border-black rounded px-3 py-1 border-[3px];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html, body { height: 100%; }
|
||||||
|
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
Loading…
x
Reference in New Issue
Block a user