style: format code for consistency and readability
This commit is contained in:
		
					parent
					
						
							
								69d26e89b7
							
						
					
				
			
			
				commit
				
					
						386813c524
					
				
			
		
					 44 changed files with 566 additions and 370 deletions
				
			
		
							
								
								
									
										17
									
								
								angular.json
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								angular.json
									
										
									
									
									
								
							|  | @ -16,9 +16,7 @@ | ||||||
|             "outputPath": "dist/hotel-manager", |             "outputPath": "dist/hotel-manager", | ||||||
|             "index": "src/index.html", |             "index": "src/index.html", | ||||||
|             "browser": "src/main.ts", |             "browser": "src/main.ts", | ||||||
|             "polyfills": [ |             "polyfills": ["zone.js"], | ||||||
|               "zone.js" |  | ||||||
|             ], |  | ||||||
|             "tsConfig": "tsconfig.app.json", |             "tsConfig": "tsconfig.app.json", | ||||||
|             "assets": [ |             "assets": [ | ||||||
|               { |               { | ||||||
|  | @ -27,9 +25,7 @@ | ||||||
|               }, |               }, | ||||||
|               "src/assets" |               "src/assets" | ||||||
|             ], |             ], | ||||||
|             "styles": [ |             "styles": ["src/styles.css"], | ||||||
|               "src/styles.css" |  | ||||||
|             ], |  | ||||||
|             "scripts": [] |             "scripts": [] | ||||||
|           }, |           }, | ||||||
|           "configurations": { |           "configurations": { | ||||||
|  | @ -74,10 +70,7 @@ | ||||||
|         "test": { |         "test": { | ||||||
|           "builder": "@angular-devkit/build-angular:karma", |           "builder": "@angular-devkit/build-angular:karma", | ||||||
|           "options": { |           "options": { | ||||||
|             "polyfills": [ |             "polyfills": ["zone.js", "zone.js/testing"], | ||||||
|               "zone.js", |  | ||||||
|               "zone.js/testing" |  | ||||||
|             ], |  | ||||||
|             "tsConfig": "tsconfig.spec.json", |             "tsConfig": "tsconfig.spec.json", | ||||||
|             "assets": [ |             "assets": [ | ||||||
|               { |               { | ||||||
|  | @ -85,9 +78,7 @@ | ||||||
|                 "input": "public" |                 "input": "public" | ||||||
|               } |               } | ||||||
|             ], |             ], | ||||||
|             "styles": [ |             "styles": ["src/styles.css"], | ||||||
|               "src/styles.css" |  | ||||||
|             ], |  | ||||||
|             "scripts": [] |             "scripts": [] | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,4 @@ | ||||||
| { | { | ||||||
|   "$schema": "https://docs.renovatebot.com/renovate-schema.json", |   "$schema": "https://docs.renovatebot.com/renovate-schema.json", | ||||||
|   "extends": [ |   "extends": ["local>Renovate/renovate-config"] | ||||||
|     "local>Renovate/renovate-config" |  | ||||||
|   ] |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1 +1,6 @@ | ||||||
| <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> | <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,5 +1,4 @@ | ||||||
| import { Component } from "@angular/core"; | import { Component } from '@angular/core'; | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-button', |   selector: 'app-button', | ||||||
|  |  | ||||||
|  | @ -1,4 +1,12 @@ | ||||||
| <h1 class="text-2xl font-bold mb-4">Child</h1> | <h1 class="text-2xl font-bold mb-4">Child</h1> | ||||||
| <p class="text-lg mb-2">Balance: {{balance}}</p> | <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> | <p [class.hidden]="!isBroke" class="text-red-500 mb-4"> | ||||||
| <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> |   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,4 +1,4 @@ | ||||||
| import { Component, Input, Output, EventEmitter } from "@angular/core"; | import { Component, Input, Output, EventEmitter } from '@angular/core'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-child', |   selector: 'app-child', | ||||||
|  |  | ||||||
|  | @ -1,8 +1,37 @@ | ||||||
| <p class="text-2xl font-bold text-gray-800 mb-2">Name: {{hotel.hotelName}}</p> | <p class="text-2xl font-bold text-gray-800 mb-2">Name: {{ hotel.hotelName }}</p> | ||||||
| <p class="text-base text-gray-600 mb-4">Description: {{hotel.description}}</p> | <p class="text-base text-gray-600 mb-4">Description: {{ hotel.description }}</p> | ||||||
| <p class="text-lg font-medium text-green-600 mb-4">Price: {{hotel.price | currency : getCurrencyCode(selectedLanguage) : "symbol" : "2.2-2" : selectedLanguage}}</p> | <p class="text-lg font-medium text-green-600 mb-4"> | ||||||
| <select [ngModel]="selectedLanguage" (ngModelChange)="languageChange($event)" class="input-field block w-full p-2 border border-gray-300 rounded-md mb-4 focus:border-blue-500 focus:ring focus:ring-blue-200"> |   Price: | ||||||
|   <option *ngFor="let lang of langs" [value]="lang.code" [selected]="lang.lang == 'de'">{{lang.lang}}</option> |   {{ | ||||||
|  |     hotel.price | ||||||
|  |       | currency | ||||||
|  |         : getCurrencyCode(selectedLanguage) | ||||||
|  |         : "symbol" | ||||||
|  |         : "2.2-2" | ||||||
|  |         : selectedLanguage | ||||||
|  |   }} | ||||||
|  | </p> | ||||||
|  | <select | ||||||
|  |   [ngModel]="selectedLanguage" | ||||||
|  |   (ngModelChange)="languageChange($event)" | ||||||
|  |   class="input-field block w-full p-2 border border-gray-300 rounded-md mb-4 focus:border-blue-500 focus:ring focus:ring-blue-200" | ||||||
|  | > | ||||||
|  |   <option | ||||||
|  |     *ngFor="let lang of langs" | ||||||
|  |     [value]="lang.code" | ||||||
|  |     [selected]="lang.lang == 'de'" | ||||||
|  |   > | ||||||
|  |     {{ lang.lang }} | ||||||
|  |   </option> | ||||||
| </select> | </select> | ||||||
| <img src="{{hotel.imageUrl}}" alt="Hotel" class="w-full rounded-full h-auto animate-shake shadow-lg mb-4"> | <img | ||||||
| <a *ngIf="!isDetail" routerLink="/hotels/{{hotel.id}}" class="submit-button text-blue-600 hover:text-blue-800 hover:underline mb-4 block">Details</a> |   src="{{ hotel.imageUrl }}" | ||||||
|  |   alt="Hotel" | ||||||
|  |   class="w-full rounded-full h-auto animate-shake shadow-lg mb-4" | ||||||
|  | /> | ||||||
|  | <a | ||||||
|  |   *ngIf="!isDetail" | ||||||
|  |   routerLink="/hotels/{{ hotel.id }}" | ||||||
|  |   class="submit-button text-blue-600 hover:text-blue-800 hover:underline mb-4 block" | ||||||
|  |   >Details</a | ||||||
|  | > | ||||||
|  |  | ||||||
|  | @ -1,20 +1,27 @@ | ||||||
| import { Component, Injectable, Input } from "@angular/core"; | import { Component, Injectable, Input } from '@angular/core'; | ||||||
| import { ChildComponent } from "../Child/child.component"; | import { ChildComponent } from '../Child/child.component'; | ||||||
| import { Hotel } from "./hotel"; | import { Hotel } from './hotel'; | ||||||
| import { CurrencyPipe, NgFor, NgIf } from "@angular/common"; | import { CurrencyPipe, NgFor, NgIf } from '@angular/common'; | ||||||
| import { FormsModule } from "@angular/forms"; | import { FormsModule } from '@angular/forms'; | ||||||
| import { StarRatingComponent } from "../star-rating/star-rating.component"; | import { StarRatingComponent } from '../star-rating/star-rating.component'; | ||||||
| import { HttpClient } from "@angular/common/http"; | 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, NgFor], |   imports: [ | ||||||
|  |     ChildComponent, | ||||||
|  |     CurrencyPipe, | ||||||
|  |     FormsModule, | ||||||
|  |     StarRatingComponent, | ||||||
|  |     NgIf, | ||||||
|  |     RouterLink, | ||||||
|  |     NgFor, | ||||||
|  |   ], | ||||||
| }) | }) | ||||||
| export class HotelItem { | export class HotelItem { | ||||||
| 
 |  | ||||||
|   @Input() public hotel!: Hotel; |   @Input() public hotel!: Hotel; | ||||||
|   public selectedLanguage?: string; |   public selectedLanguage?: string; | ||||||
| 
 | 
 | ||||||
|  | @ -38,19 +45,19 @@ export class HotelItem { | ||||||
| 
 | 
 | ||||||
|   public langs = [ |   public langs = [ | ||||||
|     { |     { | ||||||
|       "lang": "en", |       lang: 'en', | ||||||
|       "code": "en-US", |       code: 'en-US', | ||||||
|       "currency": "USD" |       currency: 'USD', | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "lang": "cn", |       lang: 'cn', | ||||||
|       "code": "cn-CN", |       code: 'cn-CN', | ||||||
|       "currency": "CNY" |       currency: 'CNY', | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "lang": "de", |       lang: 'de', | ||||||
|       "code": "de-DE", |       code: 'de-DE', | ||||||
|       "currency": "EUR" |       currency: 'EUR', | ||||||
|     } |     }, | ||||||
|   ]; |   ]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,9 +1,8 @@ | ||||||
| <div class="m-3"> | <div class="m-3"> | ||||||
|   <h1>Parent</h1> |   <h1>Parent</h1> | ||||||
| <p>Kontostand: {{ balance }}</p> |   <p>Kontostand: {{ balance }}</p> | ||||||
| <button type="button" class="button" (click)=addFifty()>Add Money</button> |   <button type="button" class="button" (click)="addFifty()">Add Money</button> | ||||||
| 
 | 
 | ||||||
| <app-child [(balance)]="balance"></app-child> |   <app-child [(balance)]="balance"></app-child> | ||||||
| <app-child [(balance)]="balance"></app-child> |   <app-child [(balance)]="balance"></app-child> | ||||||
| </div> | </div> | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| import { Component } from "@angular/core"; | import { Component } from '@angular/core'; | ||||||
| import { ChildComponent } from "../Child/child.component"; | import { ChildComponent } from '../Child/child.component'; | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-parent', |   selector: 'app-parent', | ||||||
|  |  | ||||||
|  | @ -1,47 +1,47 @@ | ||||||
| import { Injectable } from "@angular/core"; | import { Injectable } from '@angular/core'; | ||||||
| import { Hotel } from "../../HotelItem/hotel"; | import { Hotel } from '../../HotelItem/hotel'; | ||||||
| import { from, Observable } from "rxjs"; | import { from, Observable } from 'rxjs'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class HotelService { | export class HotelService { | ||||||
|   public getHotels(): Observable<Hotel> { |   public getHotels(): Observable<Hotel> { | ||||||
|     return from([ |     return from([ | ||||||
|       { |       { | ||||||
|         "id": 1, |         id: 1, | ||||||
|         "hotelName": "Buea süßes Leben", |         hotelName: 'Buea süßes Leben', | ||||||
|         "description": "Schöne Aussicht am Meer", |         description: 'Schöne Aussicht am Meer', | ||||||
|         "price": 230.5, |         price: 230.5, | ||||||
|         "imageUrl": "assets/img/1.jpg", |         imageUrl: 'assets/img/1.jpg', | ||||||
|         "rating": 3.5, |         rating: 3.5, | ||||||
|         "tags": ["test"] |         tags: ['test'], | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         "id": 2, |         id: 2, | ||||||
|         "hotelName": "Marrakesch", |         hotelName: 'Marrakesch', | ||||||
|         "description": "Genießen Sie den Blick auf die Berge", |         description: 'Genießen Sie den Blick auf die Berge', | ||||||
|         "price": 145.5, |         price: 145.5, | ||||||
|         "imageUrl": "assets/img/2.jpg", |         imageUrl: 'assets/img/2.jpg', | ||||||
|         "rating": 5, |         rating: 5, | ||||||
|         "tags": ["test"] |         tags: ['test'], | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         "id": 3, |         id: 3, | ||||||
|         "hotelName": "Abuja neuer Palast", |         hotelName: 'Abuja neuer Palast', | ||||||
|         "description": "Kompletter Aufenthalt mit Autoservice", |         description: 'Kompletter Aufenthalt mit Autoservice', | ||||||
|         "price": 120.12, |         price: 120.12, | ||||||
|         "imageUrl": "assets/img/3.jpg", |         imageUrl: 'assets/img/3.jpg', | ||||||
|         "rating": 4, |         rating: 4, | ||||||
|         "tags": ["test"] |         tags: ['test'], | ||||||
|       }, |       }, | ||||||
|       { |       { | ||||||
|         "id": 4, |         id: 4, | ||||||
|         "hotelName": "Kapstadt Stadt", |         hotelName: 'Kapstadt Stadt', | ||||||
|         "description": "Wunderschönes Ambiente für Ihren Aufenthalt", |         description: 'Wunderschönes Ambiente für Ihren Aufenthalt', | ||||||
|         "price": 135.12, |         price: 135.12, | ||||||
|         "imageUrl": "assets/img/4.jpg", |         imageUrl: 'assets/img/4.jpg', | ||||||
|         "rating": 2.5, |         rating: 2.5, | ||||||
|         "tags": ["test"] |         tags: ['test'], | ||||||
|       } |       }, | ||||||
|     ]); |     ]); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1 +1,6 @@ | ||||||
| <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"> | <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" | ||||||
|  | /> | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| import { CommonModule } from "@angular/common"; | import { CommonModule } from '@angular/common'; | ||||||
| import { Component, NgModule } from "@angular/core"; | import { Component, NgModule } from '@angular/core'; | ||||||
| import { FormsModule, NgForm, NgModel } from "@angular/forms"; | import { FormsModule, NgForm, NgModel } from '@angular/forms'; | ||||||
| import { Input, Output } from "@angular/core"; | import { Input, Output } from '@angular/core'; | ||||||
| import { EventEmitter } from "@angular/core"; | import { EventEmitter } from '@angular/core'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-search', |   selector: 'app-search', | ||||||
|  | @ -11,7 +11,7 @@ import { EventEmitter } from "@angular/core"; | ||||||
|   imports: [FormsModule], |   imports: [FormsModule], | ||||||
| }) | }) | ||||||
| export class SearchComponent { | export class SearchComponent { | ||||||
|   @Input() public input: string = ""; |   @Input() public input: string = ''; | ||||||
|   @Output() inputChange = new EventEmitter<string>(); |   @Output() inputChange = new EventEmitter<string>(); | ||||||
| 
 | 
 | ||||||
|   public update(e: string) { |   public update(e: string) { | ||||||
|  |  | ||||||
|  | @ -10,7 +10,6 @@ import { Hotel } from '../HotelItem/hotel'; | ||||||
|  * @implements {InMemoryDbService} |  * @implements {InMemoryDbService} | ||||||
|  */ |  */ | ||||||
| export class HotelData implements InMemoryDbService { | export class HotelData implements InMemoryDbService { | ||||||
| 
 |  | ||||||
|   createDb(): Record<string, Hotel[]> { |   createDb(): Record<string, Hotel[]> { | ||||||
|     const hotels: Hotel[] = [ |     const hotels: Hotel[] = [ | ||||||
|       { |       { | ||||||
|  | @ -20,39 +19,43 @@ export class HotelData implements InMemoryDbService { | ||||||
|         price: 230.5, |         price: 230.5, | ||||||
|         imageUrl: 'assets/img/1.jpg', |         imageUrl: 'assets/img/1.jpg', | ||||||
|         rating: 3.5, |         rating: 3.5, | ||||||
|         tags: ['nouveau'] |         tags: ['nouveau'], | ||||||
|       }, { |       }, | ||||||
|  |       { | ||||||
|         id: 2, |         id: 2, | ||||||
|         hotelName: 'Marakech', |         hotelName: 'Marakech', | ||||||
|         description: 'Profitez de la vue sur les montagnes', |         description: 'Profitez de la vue sur les montagnes', | ||||||
|         price: 145.5, |         price: 145.5, | ||||||
|         imageUrl: 'assets/img/2.jpg', |         imageUrl: 'assets/img/2.jpg', | ||||||
|         rating: 5, |         rating: 5, | ||||||
|         tags: ['nouveau'] |         tags: ['nouveau'], | ||||||
|       }, { |       }, | ||||||
|  |       { | ||||||
|         id: 3, |         id: 3, | ||||||
|         hotelName: 'Abudja new look palace', |         hotelName: 'Abudja new look palace', | ||||||
|         description: 'Séjour complet avec service de voitures', |         description: 'Séjour complet avec service de voitures', | ||||||
|         price: 120.12, |         price: 120.12, | ||||||
|         imageUrl: 'assets/img/3.jpg', |         imageUrl: 'assets/img/3.jpg', | ||||||
|         rating: 4, |         rating: 4, | ||||||
|         tags: ['nouveau'] |         tags: ['nouveau'], | ||||||
|       }, { |       }, | ||||||
|  |       { | ||||||
|         id: 4, |         id: 4, | ||||||
|         hotelName: 'Cape town city', |         hotelName: 'Cape town city', | ||||||
|         description: 'Magnifique cadre pour votre séjour', |         description: 'Magnifique cadre pour votre séjour', | ||||||
|         price: 135.12, |         price: 135.12, | ||||||
|         imageUrl: 'assets/img/4.jpg', |         imageUrl: 'assets/img/4.jpg', | ||||||
|         rating: 2.5, |         rating: 2.5, | ||||||
|         tags: ['nouveau'] |         tags: ['nouveau'], | ||||||
|       } |       }, | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     return { hotels }; |     return { hotels }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   genId(hotels: Hotel[]): number { |   genId(hotels: Hotel[]): number { | ||||||
|     return hotels.length > 0 ? Math.max(...hotels.map(hotel => hotel.id)) + 1 : 1; |     return hotels.length > 0 | ||||||
|  |       ? Math.max(...hotels.map((hotel) => hotel.id)) + 1 | ||||||
|  |       : 1; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -24,6 +24,8 @@ describe('AppComponent', () => { | ||||||
|     const fixture = TestBed.createComponent(AppComponent); |     const fixture = TestBed.createComponent(AppComponent); | ||||||
|     fixture.detectChanges(); |     fixture.detectChanges(); | ||||||
|     const compiled = fixture.nativeElement as HTMLElement; |     const compiled = fixture.nativeElement as HTMLElement; | ||||||
|     expect(compiled.querySelector('h1')?.textContent).toContain('Hello, hotel-manager'); |     expect(compiled.querySelector('h1')?.textContent).toContain( | ||||||
|  |       'Hello, hotel-manager', | ||||||
|  |     ); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,13 @@ | ||||||
| import { Component, Injectable } from '@angular/core'; | import { Component, Injectable } from '@angular/core'; | ||||||
| import { HotelItem } from './HotelItem/HotelItem.component'; | import { HotelItem } from './HotelItem/HotelItem.component'; | ||||||
| import { SearchComponent } from './Search/search.component'; | import { SearchComponent } from './Search/search.component'; | ||||||
| import { AsyncPipe, NgFor, NgForOf, NgIf, UpperCasePipe } from '@angular/common'; | import { | ||||||
|  |   AsyncPipe, | ||||||
|  |   NgFor, | ||||||
|  |   NgForOf, | ||||||
|  |   NgIf, | ||||||
|  |   UpperCasePipe, | ||||||
|  | } from '@angular/common'; | ||||||
| import { TextPipe } from '../text.pipe'; | 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 { inject } from '@angular/core'; | ||||||
|  | @ -10,15 +16,23 @@ import { HttpClient } from '@angular/common/http'; | ||||||
| import { Hotel } from './HotelItem/hotel'; | 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, | ||||||
|  |     NgFor, | ||||||
|  |     NgForOf, | ||||||
|  |     NgIf, | ||||||
|  |     HotelItem, | ||||||
|  |     SearchComponent, | ||||||
|  |     UpperCasePipe, | ||||||
|  |     TextPipe, | ||||||
|  |     AsyncPipe, | ||||||
|  |   ], | ||||||
|   templateUrl: './app.component.html', |   templateUrl: './app.component.html', | ||||||
|   providers: [HotelService], |   providers: [HotelService], | ||||||
|   styleUrl: './app.component.css' |   styleUrl: './app.component.css', | ||||||
| }) | }) | ||||||
| export class AppComponent { | export class AppComponent {} | ||||||
| } |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -1,4 +1,8 @@ | ||||||
| import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core'; | import { | ||||||
|  |   ApplicationConfig, | ||||||
|  |   importProvidersFrom, | ||||||
|  |   provideZoneChangeDetection, | ||||||
|  | } from '@angular/core'; | ||||||
| import { provideRouter } from '@angular/router'; | import { provideRouter } from '@angular/router'; | ||||||
| import localeDe from '@angular/common/locales/de'; | import localeDe from '@angular/common/locales/de'; | ||||||
| import localeCn from '@angular/common/locales/zh-Hans'; | import localeCn from '@angular/common/locales/zh-Hans'; | ||||||
|  | @ -16,5 +20,6 @@ export const appConfig: ApplicationConfig = { | ||||||
|     provideZoneChangeDetection({ eventCoalescing: true }), |     provideZoneChangeDetection({ eventCoalescing: true }), | ||||||
|     provideRouter(routes), |     provideRouter(routes), | ||||||
|     provideHttpClient(), |     provideHttpClient(), | ||||||
|     importProvidersFrom(InMemoryWebApiModule.forRoot(HotelData))] |     importProvidersFrom(InMemoryWebApiModule.forRoot(HotelData)), | ||||||
|  |   ], | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -6,19 +6,19 @@ import { NewHotelComponent } from './new-hotel/new-hotel.component'; | ||||||
| 
 | 
 | ||||||
| export const routes: Routes = [ | export const routes: Routes = [ | ||||||
|   { |   { | ||||||
|     path: "", |     path: '', | ||||||
|     component: HotelListComponent, |     component: HotelListComponent, | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     path: "hotels/:id", |     path: 'hotels/:id', | ||||||
|     component: HotelDetailsComponent, |     component: HotelDetailsComponent, | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     path: "testing", |     path: 'testing', | ||||||
|     component: TestComponent, |     component: TestComponent, | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     path: "new", |     path: 'new', | ||||||
|     component: NewHotelComponent, |     component: NewHotelComponent, | ||||||
|   } |   }, | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|  | @ -1,25 +1,11 @@ | ||||||
| import { AbstractControl, FormGroup, ValidationErrors, ValidatorFn } from "@angular/forms"; | import { | ||||||
|  |   AbstractControl, | ||||||
|  |   FormGroup, | ||||||
|  |   ValidationErrors, | ||||||
|  |   ValidatorFn, | ||||||
|  | } from '@angular/forms'; | ||||||
| 
 | 
 | ||||||
| export class CrossValidator { | export class CrossValidator { | ||||||
|   public onlyAllowNameAndDescriptionSame() { |  | ||||||
|     return (control: AbstractControl): ValidationErrors | null => { |  | ||||||
|       if (!(control instanceof FormGroup)) { |  | ||||||
|         return null; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       const name = control.get('name')?.value; |  | ||||||
|       const description = control.get('description')?.value; |  | ||||||
| 
 |  | ||||||
|       console.log(name, description); |  | ||||||
| 
 |  | ||||||
|       const error = name !== description ? { mismatch: true } : null; |  | ||||||
| 
 |  | ||||||
|       console.log(error); |  | ||||||
| 
 |  | ||||||
|       return error; |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public crossValidate() { |   public crossValidate() { | ||||||
|     return (control: AbstractControl): ValidationErrors | null => { |     return (control: AbstractControl): ValidationErrors | null => { | ||||||
|       if (!(control instanceof FormGroup)) { |       if (!(control instanceof FormGroup)) { | ||||||
|  | @ -28,7 +14,23 @@ export class CrossValidator { | ||||||
| 
 | 
 | ||||||
|       const contactType = control.get('contactType')?.value; |       const contactType = control.get('contactType')?.value; | ||||||
| 
 | 
 | ||||||
|       if (contactType == "None") return null; |       if (contactType == ('None' || null)) return null; | ||||||
|  | 
 | ||||||
|  |       let error = null; | ||||||
|  |       if (contactType == 'Email') { | ||||||
|  |         error = | ||||||
|  |           control.get('email')?.value != control.get('emailConfirmation')?.value | ||||||
|  |             ? { mismatchEmail: true } | ||||||
|  |             : null; | ||||||
|  |       } else { | ||||||
|  |         error = | ||||||
|  |           control.get('phone')?.value != control.get('phoneConfirmation')?.value | ||||||
|  |             ? { mismatchPhone: true } | ||||||
|  |             : null; | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       console.log('Error: ', error); | ||||||
|  |       return error; | ||||||
|  |     }; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,9 +8,8 @@ describe('HotelDetailsComponent', () => { | ||||||
| 
 | 
 | ||||||
|   beforeEach(async () => { |   beforeEach(async () => { | ||||||
|     await TestBed.configureTestingModule({ |     await TestBed.configureTestingModule({ | ||||||
|       imports: [HotelDetailsComponent] |       imports: [HotelDetailsComponent], | ||||||
|     }) |     }).compileComponents(); | ||||||
|     .compileComponents(); |  | ||||||
| 
 | 
 | ||||||
|     fixture = TestBed.createComponent(HotelDetailsComponent); |     fixture = TestBed.createComponent(HotelDetailsComponent); | ||||||
|     component = fixture.componentInstance; |     component = fixture.componentInstance; | ||||||
|  |  | ||||||
|  | @ -11,35 +11,51 @@ 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, HotelFormComponent, NgIf, NgForOf], |   imports: [ | ||||||
|  |     CurrencyPipe, | ||||||
|  |     StarRatingComponent, | ||||||
|  |     HotelItem, | ||||||
|  |     HotelFormComponent, | ||||||
|  |     NgIf, | ||||||
|  |     NgForOf, | ||||||
|  |   ], | ||||||
|   templateUrl: './hotel-details.component.html', |   templateUrl: './hotel-details.component.html', | ||||||
|   styleUrl: './hotel-details.component.css' |   styleUrl: './hotel-details.component.css', | ||||||
| }) | }) | ||||||
| export class HotelDetailsComponent implements OnInit { | export class HotelDetailsComponent implements OnInit { | ||||||
|   public hotel: any; |   public hotel: any; | ||||||
| 
 | 
 | ||||||
|   constructor(private route: ActivatedRoute, private http: HttpClient, private router: Router) { } |   constructor( | ||||||
|  |     private route: ActivatedRoute, | ||||||
|  |     private http: HttpClient, | ||||||
|  |     private router: Router, | ||||||
|  |   ) {} | ||||||
| 
 | 
 | ||||||
|   delete(): void { |   delete(): void { | ||||||
|     if (confirm("Are u sure u want to delete this hotel")) { |     if (confirm('Are u sure u want to delete this hotel')) { | ||||||
|       this.http.delete<Hotel>("/api/hotels/" + this.route.snapshot.paramMap.get("id")).subscribe(); |       this.http | ||||||
|       this.router.navigate(["/"]); |         .delete<Hotel>('/api/hotels/' + this.route.snapshot.paramMap.get('id')) | ||||||
|  |         .subscribe(); | ||||||
|  |       this.router.navigate(['/']); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   ngOnInit(): void { |   ngOnInit(): void { | ||||||
|     const routeParams = this.route.snapshot.paramMap; |     const routeParams = this.route.snapshot.paramMap; | ||||||
|     const hotelId = routeParams.get("id"); |     const hotelId = routeParams.get('id'); | ||||||
| 
 | 
 | ||||||
|     this.http.get<Hotel>("api/hotels/" + hotelId).pipe( |     this.http | ||||||
|  |       .get<Hotel>('api/hotels/' + hotelId) | ||||||
|  |       .pipe( | ||||||
|         catchError(() => { |         catchError(() => { | ||||||
|         alert("Not Found"); |           alert('Not Found'); | ||||||
|         this.router.navigate(["/"]); |           this.router.navigate(['/']); | ||||||
| 
 | 
 | ||||||
|           return EMPTY; |           return EMPTY; | ||||||
|       }) |         }), | ||||||
|     ).subscribe(res => { |       ) | ||||||
|  |       .subscribe((res) => { | ||||||
|         this.hotel = res; |         this.hotel = res; | ||||||
|     }) |       }); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,18 +1,47 @@ | ||||||
| <p>hotel-form works!</p> | <p>hotel-form works!</p> | ||||||
| <h1>{{errorMsg}}</h1> | <h1>{{ errorMsg }}</h1> | ||||||
| <form [formGroup]="hotelForm"> | <form [formGroup]="hotelForm"> | ||||||
|   <label for="name">Name</label> |   <label for="name">Name</label> | ||||||
|   <div class="text-red-500 text-5xl font-bold" *ngIf="errorMessages['name']">{{ errorMessages['name'] }}</div> |   <div class="text-red-500 text-5xl font-bold" *ngIf="errorMessages['name']"> | ||||||
|   <input type="text" class="border-red-500 text-3xl border-3 rounded-full bg-gray-500 font-bold p-3 m-3 border-8" id="name" formControlName="name"> |     {{ errorMessages["name"] }} | ||||||
|  |   </div> | ||||||
|  |   <input | ||||||
|  |     type="text" | ||||||
|  |     class="border-red-500 text-3xl border-3 rounded-full bg-gray-500 font-bold p-3 m-3 border-8" | ||||||
|  |     id="name" | ||||||
|  |     formControlName="name" | ||||||
|  |   /> | ||||||
|   <label for="description">Description</label> |   <label for="description">Description</label> | ||||||
|   <div class="text-red" *ngIf="errorMessages['description']">{{ errorMessages['description'] }}</div> |   <div class="text-red" *ngIf="errorMessages['description']"> | ||||||
|   <input type="text" class="input-field" [class.border-8]='hotelForm.get("description")?.invalid' id="description" |     {{ errorMessages["description"] }} | ||||||
|     formControlName="description"> |   </div> | ||||||
|  |   <input | ||||||
|  |     type="text" | ||||||
|  |     class="input-field" | ||||||
|  |     [class.border-8]="hotelForm.get('description')?.invalid" | ||||||
|  |     id="description" | ||||||
|  |     formControlName="description" | ||||||
|  |   /> | ||||||
|   <label for="price">Price</label> |   <label for="price">Price</label> | ||||||
|   <div class="text-red" *ngIf="errorMessages['price']">{{ errorMessages['price'] }}</div> |   <div class="text-red" *ngIf="errorMessages['price']"> | ||||||
|   <input type="number" class="input-field" |     {{ errorMessages["price"] }} | ||||||
|     [class.border-8]='hotelForm.get("price")?.invalid && hotelForm.get("price")?.touched' id="price" |   </div> | ||||||
|     formControlName="price"> |   <input | ||||||
|   <button class="submit-button" (click)="submit()" [disabled]="!hotelForm.valid" type="submit">😃 Submit</button> |     type="number" | ||||||
|  |     class="input-field" | ||||||
|  |     [class.border-8]=" | ||||||
|  |       hotelForm.get('price')?.invalid && hotelForm.get('price')?.touched | ||||||
|  |     " | ||||||
|  |     id="price" | ||||||
|  |     formControlName="price" | ||||||
|  |   /> | ||||||
|  |   <button | ||||||
|  |     class="submit-button" | ||||||
|  |     (click)="submit()" | ||||||
|  |     [disabled]="!hotelForm.valid" | ||||||
|  |     type="submit" | ||||||
|  |   > | ||||||
|  |     😃 Submit | ||||||
|  |   </button> | ||||||
|   <a class="back-button" routerLink="/">😭 Cancel</a> |   <a class="back-button" routerLink="/">😭 Cancel</a> | ||||||
| </form> | </form> | ||||||
|  |  | ||||||
|  | @ -8,9 +8,8 @@ describe('HotelFormComponent', () => { | ||||||
| 
 | 
 | ||||||
|   beforeEach(async () => { |   beforeEach(async () => { | ||||||
|     await TestBed.configureTestingModule({ |     await TestBed.configureTestingModule({ | ||||||
|       imports: [HotelFormComponent] |       imports: [HotelFormComponent], | ||||||
|     }) |     }).compileComponents(); | ||||||
|     .compileComponents(); |  | ||||||
| 
 | 
 | ||||||
|     fixture = TestBed.createComponent(HotelFormComponent); |     fixture = TestBed.createComponent(HotelFormComponent); | ||||||
|     component = fixture.componentInstance; |     component = fixture.componentInstance; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,12 @@ | ||||||
| import { Component, Input } from '@angular/core'; | import { Component, Input } from '@angular/core'; | ||||||
| import { Hotel } from '../HotelItem/hotel'; | import { Hotel } from '../HotelItem/hotel'; | ||||||
| import { AbstractControl, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; | import { | ||||||
|  |   AbstractControl, | ||||||
|  |   FormControl, | ||||||
|  |   FormGroup, | ||||||
|  |   ReactiveFormsModule, | ||||||
|  |   Validators, | ||||||
|  | } from '@angular/forms'; | ||||||
| import { NgFor, NgIf } from '@angular/common'; | import { NgFor, NgIf } from '@angular/common'; | ||||||
| import { HttpClient } from '@angular/common/http'; | import { HttpClient } from '@angular/common/http'; | ||||||
| import { Router, RouterLink } from '@angular/router'; | import { Router, RouterLink } from '@angular/router'; | ||||||
|  | @ -10,43 +16,50 @@ import { Router, RouterLink } from '@angular/router'; | ||||||
|   standalone: true, |   standalone: true, | ||||||
|   imports: [ReactiveFormsModule, NgIf, NgFor, RouterLink], |   imports: [ReactiveFormsModule, NgIf, NgFor, RouterLink], | ||||||
|   templateUrl: './hotel-form.component.html', |   templateUrl: './hotel-form.component.html', | ||||||
|   styleUrl: './hotel-form.component.css' |   styleUrl: './hotel-form.component.css', | ||||||
| }) | }) | ||||||
| export class HotelFormComponent { | export class HotelFormComponent { | ||||||
|   @Input() public hotel!: Hotel; |   @Input() public hotel!: Hotel; | ||||||
|   public hotelForm!: FormGroup |   public hotelForm!: FormGroup; | ||||||
|   public errorMsg: string = 'testing'; |   public errorMsg: string = 'testing'; | ||||||
|   errorMessages: Record<string, string> = {}; |   errorMessages: Record<string, string> = {}; | ||||||
| 
 | 
 | ||||||
|   constructor(public http: HttpClient, public router: Router) { |   constructor( | ||||||
| 
 |     public http: HttpClient, | ||||||
|   } |     public router: Router, | ||||||
|  |   ) {} | ||||||
| 
 | 
 | ||||||
|   private validationErrorMessages: Record<string, string> = { |   private validationErrorMessages: Record<string, string> = { | ||||||
|     required: "This field is required", |     required: 'This field is required', | ||||||
|     minlength: "The value is too short", |     minlength: 'The value is too short', | ||||||
|     maxlength: "The value is too long", |     maxlength: 'The value is too long', | ||||||
|     pattern: "The value format is incorrect" |     pattern: 'The value format is incorrect', | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   setMessage(value: AbstractControl): void { |   setMessage(value: AbstractControl): void { | ||||||
|     if ((value.touched || value.dirty) && value.errors) { |     if ((value.touched || value.dirty) && value.errors) { | ||||||
|       this.errorMsg = Object.keys(value.errors).map(key => { |       this.errorMsg = Object.keys(value.errors) | ||||||
|  |         .map((key) => { | ||||||
|           console.log(this.validationErrorMessages[key]); |           console.log(this.validationErrorMessages[key]); | ||||||
|           return this.validationErrorMessages[key] || `Unknown error: ${key}`; |           return this.validationErrorMessages[key] || `Unknown error: ${key}`; | ||||||
|       }).join(' '); |         }) | ||||||
|  |         .join(' '); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   updateErrorMessages(): void { |   updateErrorMessages(): void { | ||||||
|     this.errorMessages = {}; |     this.errorMessages = {}; | ||||||
| 
 | 
 | ||||||
|     Object.keys(this.hotelForm.controls).forEach(field => { |     Object.keys(this.hotelForm.controls).forEach((field) => { | ||||||
|       const control = this.hotelForm.get(field); |       const control = this.hotelForm.get(field); | ||||||
| 
 | 
 | ||||||
|       if (control && control.errors) { |       if (control && control.errors) { | ||||||
|         this.errorMessages[field] = Object.keys(control.errors) |         this.errorMessages[field] = Object.keys(control.errors) | ||||||
|           .map(errorKey => this.validationErrorMessages[errorKey] || `Unknown error: ${errorKey}`) |           .map( | ||||||
|  |             (errorKey) => | ||||||
|  |               this.validationErrorMessages[errorKey] || | ||||||
|  |               `Unknown error: ${errorKey}`, | ||||||
|  |           ) | ||||||
|           .join(' '); |           .join(' '); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|  | @ -67,11 +80,11 @@ export class HotelFormComponent { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   submit(): void { |   submit(): void { | ||||||
|     this.hotel.hotelName = this.hotelForm.get("name")?.value; |     this.hotel.hotelName = this.hotelForm.get('name')?.value; | ||||||
|     this.hotel.description = this.hotelForm.get("description")?.value; |     this.hotel.description = this.hotelForm.get('description')?.value; | ||||||
|     this.hotel.price = this.hotelForm.get("price")?.value; |     this.hotel.price = this.hotelForm.get('price')?.value; | ||||||
|     console.log(this.hotelForm.value); |     console.log(this.hotelForm.value); | ||||||
|     this.http.put("/api/hotels/" + this.hotel.id, this.hotel).subscribe(); |     this.http.put('/api/hotels/' + this.hotel.id, this.hotel).subscribe(); | ||||||
|     this.router.navigate(["/"]); |     this.router.navigate(['/']); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,10 +1,13 @@ | ||||||
| <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> | ||||||
|   <a class="submit-button" routerLink="/new">CREATE NEW HOTEL</a> |   <a class="submit-button" routerLink="/new">CREATE NEW HOTEL</a> | ||||||
|   <app-search [(input)]="search"></app-search> |   <app-search [(input)]="search"></app-search> | ||||||
|   @if (hotels[0].hotelName) { |   @if (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> | ||||||
|  |  | ||||||
|  | @ -8,9 +8,8 @@ describe('HotelListComponent', () => { | ||||||
| 
 | 
 | ||||||
|   beforeEach(async () => { |   beforeEach(async () => { | ||||||
|     await TestBed.configureTestingModule({ |     await TestBed.configureTestingModule({ | ||||||
|       imports: [HotelListComponent] |       imports: [HotelListComponent], | ||||||
|     }) |     }).compileComponents(); | ||||||
|     .compileComponents(); |  | ||||||
| 
 | 
 | ||||||
|     fixture = TestBed.createComponent(HotelListComponent); |     fixture = TestBed.createComponent(HotelListComponent); | ||||||
|     component = fixture.componentInstance; |     component = fixture.componentInstance; | ||||||
|  |  | ||||||
|  | @ -17,41 +17,50 @@ interface User { | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-hotel-list', |   selector: 'app-hotel-list', | ||||||
|   standalone: true, |   standalone: true, | ||||||
|   imports: [UpperCasePipe, TextPipe, SearchComponent, HotelItem, NgFor, NgIf, RouterLink], |   imports: [ | ||||||
|  |     UpperCasePipe, | ||||||
|  |     TextPipe, | ||||||
|  |     SearchComponent, | ||||||
|  |     HotelItem, | ||||||
|  |     NgFor, | ||||||
|  |     NgIf, | ||||||
|  |     RouterLink, | ||||||
|  |   ], | ||||||
|   templateUrl: './hotel-list.component.html', |   templateUrl: './hotel-list.component.html', | ||||||
|   styleUrl: './hotel-list.component.css' |   styleUrl: './hotel-list.component.css', | ||||||
| }) | }) | ||||||
| export class HotelListComponent { | export class HotelListComponent { | ||||||
|   public search: string = ""; |   public search: string = ''; | ||||||
|   public hotelService: HotelService = inject(HotelService); |   public hotelService: HotelService = inject(HotelService); | ||||||
|   public response: any = null; |   public response: any = null; | ||||||
|   public hotels: Array<Hotel> = [{} as Hotel]; |   public hotels: Array<Hotel> = [{} as Hotel]; | ||||||
| 
 | 
 | ||||||
|   constructor (private http: HttpClient) { |   constructor(private http: HttpClient) {} | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|     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 = [ |     const users = [ | ||||||
|       { name: "Max", age: 21 }, |       { name: 'Max', age: 21 }, | ||||||
|       { name: "Peter", age: 31 }, |       { name: 'Peter', age: 31 }, | ||||||
|       { name: "Hans", age: 13 }, |       { name: 'Hans', age: 13 }, | ||||||
|       { name: "Klaus", age: 51 }, |       { name: 'Klaus', age: 51 }, | ||||||
|       { name: "Dieter", age: 1 }, |       { name: 'Dieter', age: 1 }, | ||||||
|       { name: "Jan-Marlon", age: 3 }, |       { name: 'Jan-Marlon', age: 3 }, | ||||||
|     ] |     ]; | ||||||
| 
 | 
 | ||||||
|     const stream: Observable<User> = from(users); |     const stream: Observable<User> = from(users); | ||||||
| 
 | 
 | ||||||
|     stream.pipe( |     stream | ||||||
|  |       .pipe( | ||||||
|         filter((user) => user.age > 18), |         filter((user) => user.age > 18), | ||||||
|         scan((acc, user) => acc + user.age, 0), |         scan((acc, user) => acc + user.age, 0), | ||||||
|         map((ageSum, index) => ageSum / (index + 1)), |         map((ageSum, index) => ageSum / (index + 1)), | ||||||
|         last(), |         last(), | ||||||
|     ).subscribe(console.log); |       ) | ||||||
|  |       .subscribe(console.log); | ||||||
| 
 | 
 | ||||||
|     // const stream: Observable<number | string> = from([5, 1, 2, 12, 5, 14, 17, 5, "testing"]);
 |     // const stream: Observable<number | string> = from([5, 1, 2, 12, 5, 14, 17, 5, "testing"]);
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,59 +1,120 @@ | ||||||
| <p>Create Hotel</p> | <p>Create Hotel</p> | ||||||
| 
 | 
 | ||||||
| <form [formGroup]="hotelForm"> | <form [formGroup]="hotelForm"> | ||||||
|   <div class="text-red-500" *ngIf="errorMessages['form']">{{ errorMessages['form'] }}</div> |   <div class="text-red-500 bg-white m-3" *ngIf="errorMessages['form']"> | ||||||
|  |     {{ errorMessages["form"] }} | ||||||
|  |   </div> | ||||||
| 
 | 
 | ||||||
|   <label for="name">Name</label> |   <label for="name">Name</label> | ||||||
|   <div class="text-red-500" *ngIf="errorMessages['name']">{{ errorMessages['name'] }}</div> |   <div class="text-red-500" *ngIf="errorMessages['name']"> | ||||||
|   <input type="text" class="border-red-500" |     {{ errorMessages["name"] }} | ||||||
|     [class.border-8]='hotelForm.get("name")?.invalid && hotelForm.get("name")?.touched' id="name" |   </div> | ||||||
|     formControlName="name"> |   <input | ||||||
|  |     type="text" | ||||||
|  |     class="border-red-500" | ||||||
|  |     [class.border-8]=" | ||||||
|  |       hotelForm.get('name')?.invalid && hotelForm.get('name')?.touched | ||||||
|  |     " | ||||||
|  |     id="name" | ||||||
|  |     formControlName="name" | ||||||
|  |   /> | ||||||
|   <label for="description">Description</label> |   <label for="description">Description</label> | ||||||
|   <div class="text-red-500" *ngIf="errorMessages['description']">{{ errorMessages['description'] }}</div> |   <div class="text-red-500" *ngIf="errorMessages['description']"> | ||||||
|   <input type="text" class="border-red-500" |     {{ errorMessages["description"] }} | ||||||
|     [class.border-8]='hotelForm.get("description")?.invalid && hotelForm.get("description")?.touched' id="description" |   </div> | ||||||
|     formControlName="description"> |   <input | ||||||
|  |     type="text" | ||||||
|  |     class="border-red-500" | ||||||
|  |     [class.border-8]=" | ||||||
|  |       hotelForm.get('description')?.invalid && | ||||||
|  |       hotelForm.get('description')?.touched | ||||||
|  |     " | ||||||
|  |     id="description" | ||||||
|  |     formControlName="description" | ||||||
|  |   /> | ||||||
|   <label for="imageUrl">Image</label> |   <label for="imageUrl">Image</label> | ||||||
|   <div class="text-red-500" *ngIf="errorMessages['imageUrl']">{{ errorMessages['imageUrl'] }}</div> |   <div class="text-red-500" *ngIf="errorMessages['imageUrl']"> | ||||||
|   <input type="url" class="border-red-500" |     {{ errorMessages["imageUrl"] }} | ||||||
|     [class.border-8]='hotelForm.get("imageUel")?.invalid && hotelForm.get("imageUrl")?.touched' id="imageUrl" |   </div> | ||||||
|     formControlName="imageUrl"> |   <input | ||||||
|  |     type="url" | ||||||
|  |     class="border-red-500" | ||||||
|  |     [class.border-8]=" | ||||||
|  |       hotelForm.get('imageUel')?.invalid && hotelForm.get('imageUrl')?.touched | ||||||
|  |     " | ||||||
|  |     id="imageUrl" | ||||||
|  |     formControlName="imageUrl" | ||||||
|  |   /> | ||||||
|   <label for="price">Price</label> |   <label for="price">Price</label> | ||||||
|   <div class="text-red-500" *ngIf="errorMessages['price']">{{ errorMessages['price'] }}</div> |   <div class="text-red-500" *ngIf="errorMessages['price']"> | ||||||
|   <input type="number" class="border-red-500" [class.border-8]='hotelForm.get("price")?.invalid' id="price" |     {{ errorMessages["price"] }} | ||||||
|     formControlName="price"> |   </div> | ||||||
|  |   <input | ||||||
|  |     type="number" | ||||||
|  |     class="border-red-500" | ||||||
|  |     [class.border-8]="hotelForm.get('price')?.invalid" | ||||||
|  |     id="price" | ||||||
|  |     formControlName="price" | ||||||
|  |   /> | ||||||
|   <label for="rating">Rating</label> |   <label for="rating">Rating</label> | ||||||
|   <div class="text-red-500" *ngIf="errorMessages['rating']">{{ errorMessages['rating'] }}</div> |   <div class="text-red-500" *ngIf="errorMessages['rating']"> | ||||||
|   <input type="rating" class="border-red-500" [class.border-8]='hotelForm.get("rating")?.invalid' id="rating" |     {{ errorMessages["rating"] }} | ||||||
|     formControlName="rating"> |   </div> | ||||||
|  |   <input | ||||||
|  |     type="rating" | ||||||
|  |     class="border-red-500" | ||||||
|  |     [class.border-8]="hotelForm.get('rating')?.invalid" | ||||||
|  |     id="rating" | ||||||
|  |     formControlName="rating" | ||||||
|  |   /> | ||||||
|   <button class="submit-button" (click)="addTag()">Add Tag</button> |   <button class="submit-button" (click)="addTag()">Add Tag</button> | ||||||
|   @for (tag of getTags().controls; track tag) { |   @for (tag of getTags().controls; track tag) { | ||||||
|   <input type="tag" class="border-red-500" [class.border-8]='hotelForm.get("tag")?.invalid' id="tag" |     <input | ||||||
|     formControlName="tag"> |       type="tag" | ||||||
|  |       class="border-red-500" | ||||||
|  |       [class.border-8]="hotelForm.get('tag')?.invalid" | ||||||
|  |       id="tag" | ||||||
|  |       formControlName="tag" | ||||||
|  |     /> | ||||||
|     <button (click)="deleteTag(tag)" class="delete-button">delete</button> |     <button (click)="deleteTag(tag)" class="delete-button">delete</button> | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   <input type="radio" value='None' formControlName="contactType">None |   <input type="radio" value="None" formControlName="contactType" />None | ||||||
|   <input type="radio" value='Email' formControlName="contactType">Email |   <input type="radio" value="Email" formControlName="contactType" />Email | ||||||
|   <input type="radio" value='SMS' formControlName="contactType">SMS |   <input type="radio" value="SMS" formControlName="contactType" />SMS | ||||||
| 
 | 
 | ||||||
|   @if (hotelForm.get("contactType")?.value == "Email") { |   @if (hotelForm.get("contactType")?.value == "Email") { | ||||||
|     <div class="mt-3"> |     <div class="mt-3"> | ||||||
|       <label for="email">Email</label> |       <label for="email">Email</label> | ||||||
|     <input type="email" formControlName="email" id="email"> |       <input type="email" formControlName="email" id="email" /> | ||||||
|       <label for="emailConfirmation">Email Confirmation</label> |       <label for="emailConfirmation">Email Confirmation</label> | ||||||
|     <input type="email" formControlName="emailConfirmation" id="emailConfirmation"> |       <input | ||||||
|  |         type="email" | ||||||
|  |         formControlName="emailConfirmation" | ||||||
|  |         id="emailConfirmation" | ||||||
|  |       /> | ||||||
|     </div> |     </div> | ||||||
|   } |   } | ||||||
|   @if (hotelForm.get("contactType")?.value == "SMS") { |   @if (hotelForm.get("contactType")?.value == "SMS") { | ||||||
|     <div class="mt-3"> |     <div class="mt-3"> | ||||||
|       <label for="phone">Phone Number</label> |       <label for="phone">Phone Number</label> | ||||||
|     <input type="tel" formControlName="phone" id="phone"> |       <input type="tel" formControlName="phone" id="phone" /> | ||||||
|       <label for="phoneConfirmation">Phone Number Confirmation</label> |       <label for="phoneConfirmation">Phone Number Confirmation</label> | ||||||
|     <input type="tel" formControlName="phoneConfirmation" id="phoneConfirmation"> |       <input | ||||||
|  |         type="tel" | ||||||
|  |         formControlName="phoneConfirmation" | ||||||
|  |         id="phoneConfirmation" | ||||||
|  |       /> | ||||||
|     </div> |     </div> | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   <button class="submit-button" (click)="submit()" [disabled]="!hotelForm.valid" type="submit">Submit</button> |   <button | ||||||
|  |     class="submit-button" | ||||||
|  |     (click)="submit()" | ||||||
|  |     [disabled]="!hotelForm.valid" | ||||||
|  |     type="submit" | ||||||
|  |   > | ||||||
|  |     Submit | ||||||
|  |   </button> | ||||||
|   <a routerLink="/" class="delete-button">Cancel</a> |   <a routerLink="/" class="delete-button">Cancel</a> | ||||||
| </form> | </form> | ||||||
|  |  | ||||||
|  | @ -8,9 +8,8 @@ describe('NewHotelComponent', () => { | ||||||
| 
 | 
 | ||||||
|   beforeEach(async () => { |   beforeEach(async () => { | ||||||
|     await TestBed.configureTestingModule({ |     await TestBed.configureTestingModule({ | ||||||
|       imports: [NewHotelComponent] |       imports: [NewHotelComponent], | ||||||
|     }) |     }).compileComponents(); | ||||||
|     .compileComponents(); |  | ||||||
| 
 | 
 | ||||||
|     fixture = TestBed.createComponent(NewHotelComponent); |     fixture = TestBed.createComponent(NewHotelComponent); | ||||||
|     component = fixture.componentInstance; |     component = fixture.componentInstance; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,13 @@ | ||||||
| import { HttpClient } from '@angular/common/http'; | import { HttpClient } from '@angular/common/http'; | ||||||
| import { Component } from '@angular/core'; | import { Component } from '@angular/core'; | ||||||
| import { AbstractControl, FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; | import { | ||||||
|  |   AbstractControl, | ||||||
|  |   FormArray, | ||||||
|  |   FormControl, | ||||||
|  |   FormGroup, | ||||||
|  |   ReactiveFormsModule, | ||||||
|  |   Validators, | ||||||
|  | } from '@angular/forms'; | ||||||
| import { Router, RouterLink } from '@angular/router'; | import { Router, RouterLink } from '@angular/router'; | ||||||
| import { Hotel } from '../HotelItem/hotel'; | import { Hotel } from '../HotelItem/hotel'; | ||||||
| import { NgFor, NgIf } from '@angular/common'; | import { NgFor, NgIf } from '@angular/common'; | ||||||
|  | @ -11,41 +18,46 @@ import { CrossValidator } from '../crossValidator.service'; | ||||||
|   standalone: true, |   standalone: true, | ||||||
|   imports: [ReactiveFormsModule, NgIf, NgFor, RouterLink], |   imports: [ReactiveFormsModule, NgIf, NgFor, RouterLink], | ||||||
|   templateUrl: './new-hotel.component.html', |   templateUrl: './new-hotel.component.html', | ||||||
|   styleUrl: './new-hotel.component.css' |   styleUrl: './new-hotel.component.css', | ||||||
| }) | }) | ||||||
| export class NewHotelComponent { | export class NewHotelComponent { | ||||||
|   public hotelForm!: FormGroup |   public hotelForm!: FormGroup; | ||||||
|   public hotel!: Hotel |   public hotel!: Hotel; | ||||||
|   errorMessages: Record<string, string> = {}; |   errorMessages: Record<string, string> = {}; | ||||||
| 
 | 
 | ||||||
|   private validationErrorMessages: Record<string, string> = { |   private validationErrorMessages: Record<string, string> = { | ||||||
|     required: "This field is required", |     required: 'This field is required', | ||||||
|     minlength: "The value is too short", |     minlength: 'The value is too short', | ||||||
|     maxlength: "The value is too long", |     maxlength: 'The value is too long', | ||||||
|     pattern: "The value format is incorrect" |     pattern: 'The value format is incorrect', | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   private fullFormValidationErrorMessages: Record<string, string> = { |   private fullFormValidationErrorMessages: Record<string, string> = { | ||||||
|     mismatch: "The name and description have to be the same", |     mismatch: 'The name and description have to be the same', | ||||||
|   } |   }; | ||||||
| 
 | 
 | ||||||
|   updateErrorMessages(): void { |   updateErrorMessages(): void { | ||||||
|     this.errorMessages = {}; |     this.errorMessages = {}; | ||||||
| 
 | 
 | ||||||
|     Object.keys(this.hotelForm.controls).forEach(field => { |     Object.keys(this.hotelForm.controls).forEach((field) => { | ||||||
|       const control = this.hotelForm.get(field); |       const control = this.hotelForm.get(field); | ||||||
| 
 | 
 | ||||||
|       if (control && control.errors && control.touched) { |       if (control && control.errors && control.touched) { | ||||||
|         this.errorMessages[field] = Object.keys(control.errors) |         this.errorMessages[field] = Object.keys(control.errors) | ||||||
|           .map(errorKey => this.validationErrorMessages[errorKey] || `Unknown error: ${errorKey}`) |           .map( | ||||||
|  |             (errorKey) => | ||||||
|  |               this.validationErrorMessages[errorKey] || | ||||||
|  |               `Unknown error: ${errorKey}`, | ||||||
|  |           ) | ||||||
|           .join(' '); |           .join(' '); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     if (this.hotelForm.errors) { |     if (this.hotelForm.errors) { | ||||||
|       Object.keys(this.hotelForm.errors).forEach(errorKey => { |       Object.keys(this.hotelForm.errors).forEach((errorKey) => { | ||||||
|         const errorMessage = this.fullFormValidationErrorMessages[errorKey] || `Unknown error: ${errorKey}`; |         const errorMessage = | ||||||
|  |           this.fullFormValidationErrorMessages[errorKey] || | ||||||
|  |           `Unknown error: ${errorKey}`; | ||||||
|         this.errorMessages['form'] = errorMessage; |         this.errorMessages['form'] = errorMessage; | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|  | @ -60,27 +72,29 @@ export class NewHotelComponent { | ||||||
|       tags.push(new FormControl(tag, [Validators.required])); |       tags.push(new FormControl(tag, [Validators.required])); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |     this.hotelForm = new FormGroup( | ||||||
|     this.hotelForm = new FormGroup({ |       { | ||||||
|       name: new FormControl("", Validators.required), |         name: new FormControl('', Validators.required), | ||||||
|       description: new FormControl("", Validators.required), |         description: new FormControl('', Validators.required), | ||||||
|         price: new FormControl(0, Validators.required), |         price: new FormControl(0, Validators.required), | ||||||
|       imageUrl: new FormControl("", Validators.required), |         imageUrl: new FormControl('', Validators.required), | ||||||
|         rating: new FormControl(0, Validators.required), |         rating: new FormControl(0, Validators.required), | ||||||
|         contactType: new FormControl('None'), |         contactType: new FormControl('None'), | ||||||
|         email: new FormControl(''), |         email: new FormControl(''), | ||||||
|         emailConfirmation: new FormControl(''), |         emailConfirmation: new FormControl(''), | ||||||
|         phone: new FormControl(''), |         phone: new FormControl(''), | ||||||
|         phoneConfirmation: new FormControl(''), |         phoneConfirmation: new FormControl(''), | ||||||
|       tags: new FormArray(tags) |         tags: new FormArray(tags), | ||||||
|     }, { validators: new CrossValidator().onlyAllowNameAndDescriptionSame() }) |       }, | ||||||
|  |       { validators: new CrossValidator().crossValidate() }, | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     this.hotelForm.valueChanges.subscribe(() => this.updateErrorMessages()); |     this.hotelForm.valueChanges.subscribe(() => this.updateErrorMessages()); | ||||||
|     this.hotelForm.valueChanges.subscribe(() => this.formUpdated()); |     this.hotelForm.valueChanges.subscribe(() => this.formUpdated()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   formUpdated() { |   formUpdated() { | ||||||
|     console.log(this.hotelForm.get("contactType")?.value); |     console.log(this.hotelForm.get('contactType')?.value); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getTags() { |   getTags() { | ||||||
|  | @ -91,27 +105,27 @@ export class NewHotelComponent { | ||||||
|     this.getTags().removeAt(this.getTags().controls.indexOf(tagElement)); |     this.getTags().removeAt(this.getTags().controls.indexOf(tagElement)); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|   addTag() { |   addTag() { | ||||||
|     this.getTags().push(new FormControl('', [Validators.required])); |     this.getTags().push(new FormControl('', [Validators.required])); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   constructor(public http: HttpClient, public router: Router) { |   constructor( | ||||||
| 
 |     public http: HttpClient, | ||||||
|   } |     public router: Router, | ||||||
|  |   ) {} | ||||||
| 
 | 
 | ||||||
|   submit(): void { |   submit(): void { | ||||||
|     console.log(this.hotelForm.value); |     console.log(this.hotelForm.value); | ||||||
|     const hotel: Hotel = { |     const hotel: Hotel = { | ||||||
|       id: 0, |       id: 0, | ||||||
|       hotelName: this.hotelForm.get("name")?.value, |       hotelName: this.hotelForm.get('name')?.value, | ||||||
|       description: this.hotelForm.get("description")?.value, |       description: this.hotelForm.get('description')?.value, | ||||||
|       price: this.hotelForm.get("price")?.value, |       price: this.hotelForm.get('price')?.value, | ||||||
|       imageUrl: this.hotelForm.get("imageUrl")?.value, |       imageUrl: this.hotelForm.get('imageUrl')?.value, | ||||||
|       rating: this.hotelForm.get("rating")?.value, |       rating: this.hotelForm.get('rating')?.value, | ||||||
|       tags: this.hotelForm.get("tags")?.value |       tags: this.hotelForm.get('tags')?.value, | ||||||
|     } |     }; | ||||||
|     this.http.post("/api/hotels/", hotel).subscribe(); |     this.http.post('/api/hotels/', hotel).subscribe(); | ||||||
|     this.router.navigate(["/"]); |     this.router.navigate(['/']); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,9 +8,8 @@ describe('StarRatingComponent', () => { | ||||||
| 
 | 
 | ||||||
|   beforeEach(async () => { |   beforeEach(async () => { | ||||||
|     await TestBed.configureTestingModule({ |     await TestBed.configureTestingModule({ | ||||||
|       imports: [StarRatingComponent] |       imports: [StarRatingComponent], | ||||||
|     }) |     }).compileComponents(); | ||||||
|     .compileComponents(); |  | ||||||
| 
 | 
 | ||||||
|     fixture = TestBed.createComponent(StarRatingComponent); |     fixture = TestBed.createComponent(StarRatingComponent); | ||||||
|     component = fixture.componentInstance; |     component = fixture.componentInstance; | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ import { Component, Input } from '@angular/core'; | ||||||
|   standalone: true, |   standalone: true, | ||||||
|   imports: [NgClass, NgFor], |   imports: [NgClass, NgFor], | ||||||
|   templateUrl: './star-rating.component.html', |   templateUrl: './star-rating.component.html', | ||||||
|   styleUrl: './star-rating.component.css' |   styleUrl: './star-rating.component.css', | ||||||
| }) | }) | ||||||
| export class StarRatingComponent { | export class StarRatingComponent { | ||||||
|   @Input() public rating: number = 0; |   @Input() public rating: number = 0; | ||||||
|  |  | ||||||
|  | @ -2,9 +2,14 @@ | ||||||
| 
 | 
 | ||||||
| <form [formGroup]="loginForm"> | <form [formGroup]="loginForm"> | ||||||
|   <label for="username">Username</label> |   <label for="username">Username</label> | ||||||
|   <input type="text" id="username" autocomplete="username" formControlName="username"> |   <input | ||||||
|  |     type="text" | ||||||
|  |     id="username" | ||||||
|  |     autocomplete="username" | ||||||
|  |     formControlName="username" | ||||||
|  |   /> | ||||||
|   <label for="password">Password</label> |   <label for="password">Password</label> | ||||||
|   <input type="password" id="password" formControlName="password"> |   <input type="password" id="password" formControlName="password" /> | ||||||
|   <button (click)="submit()" type="submit">Login</button> |   <button (click)="submit()" type="submit">Login</button> | ||||||
|   <button type="reset" (click)="reset()">Reset</button> |   <button type="reset" (click)="reset()">Reset</button> | ||||||
| </form> | </form> | ||||||
|  |  | ||||||
|  | @ -8,9 +8,8 @@ describe('TestComponent', () => { | ||||||
| 
 | 
 | ||||||
|   beforeEach(async () => { |   beforeEach(async () => { | ||||||
|     await TestBed.configureTestingModule({ |     await TestBed.configureTestingModule({ | ||||||
|       imports: [TestComponent] |       imports: [TestComponent], | ||||||
|     }) |     }).compileComponents(); | ||||||
|     .compileComponents(); |  | ||||||
| 
 | 
 | ||||||
|     fixture = TestBed.createComponent(TestComponent); |     fixture = TestBed.createComponent(TestComponent); | ||||||
|     component = fixture.componentInstance; |     component = fixture.componentInstance; | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; | ||||||
|   standalone: true, |   standalone: true, | ||||||
|   imports: [ReactiveFormsModule], |   imports: [ReactiveFormsModule], | ||||||
|   templateUrl: './test.component.html', |   templateUrl: './test.component.html', | ||||||
|   styleUrl: './test.component.css' |   styleUrl: './test.component.css', | ||||||
| }) | }) | ||||||
| export class TestComponent { | export class TestComponent { | ||||||
|   public loginForm!: FormGroup; |   public loginForm!: FormGroup; | ||||||
|  | @ -19,8 +19,8 @@ export class TestComponent { | ||||||
| 
 | 
 | ||||||
|   setUpLoginForm(): FormGroup { |   setUpLoginForm(): FormGroup { | ||||||
|     return new FormGroup({ |     return new FormGroup({ | ||||||
|       username: new FormControl("Jan"), |       username: new FormControl('Jan'), | ||||||
|       password: new FormControl('') |       password: new FormControl(''), | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| import {Pipe, PipeTransform} from '@angular/core'; | import { Pipe, PipeTransform } from '@angular/core'; | ||||||
| 
 | 
 | ||||||
| @Pipe({ | @Pipe({ | ||||||
|   name: 'jkcurrency', |   name: 'jkcurrency', | ||||||
|   standalone: true |   standalone: true, | ||||||
| }) | }) | ||||||
| export class CurrencyPipe implements PipeTransform { | export class CurrencyPipe implements PipeTransform { | ||||||
|   transform(value: string): string { |   transform(value: string): string { | ||||||
|     return value.replaceAll("L", "P"); |     return value.replaceAll('L', 'P'); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,14 +1,14 @@ | ||||||
| <!doctype html> | <!doctype html> | ||||||
| <html lang="en"> | <html lang="en"> | ||||||
| <head> |   <head> | ||||||
|   <meta charset="utf-8"> |     <meta charset="utf-8" /> | ||||||
|     <title>HotelManager</title> |     <title>HotelManager</title> | ||||||
|   <base href="/"> |     <base href="/" /> | ||||||
|   <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" /> | ||||||
| </head> |   </head> | ||||||
| <body class="bg-pink-600 p-32"> |   <body class="bg-pink-600 p-32"> | ||||||
|     <app-root></app-root> |     <app-root></app-root> | ||||||
| </body> |   </body> | ||||||
| </html> | </html> | ||||||
|  |  | ||||||
|  | @ -2,5 +2,6 @@ import { bootstrapApplication } from '@angular/platform-browser'; | ||||||
| import { appConfig } from './app/app.config'; | import { appConfig } from './app/app.config'; | ||||||
| import { AppComponent } from './app/app.component'; | import { AppComponent } from './app/app.component'; | ||||||
| 
 | 
 | ||||||
| bootstrapApplication(AppComponent, appConfig) | bootstrapApplication(AppComponent, appConfig).catch((err) => | ||||||
|   .catch((err) => console.error(err)); |   console.error(err), | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | @ -8,17 +8,17 @@ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .input-field { | .input-field { | ||||||
|   @apply border-red-500 text-3xl rounded-full bg-gray-500 font-bold p-3 m-3 border-8 |   @apply border-red-500 text-3xl rounded-full bg-gray-500 font-bold p-3 m-3 border-8; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .back-button { | .back-button { | ||||||
|   @apply border-black rounded bg-blue-500 border-[30px] m-3 text-3xl font-bold p-3 |   @apply border-black rounded bg-blue-500 border-[30px] m-3 text-3xl font-bold p-3; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .submit-button { | .submit-button { | ||||||
|   @apply border-black rounded-full bg-green-500 border-[30px] m-3 text-3xl font-bold p-3 |   @apply border-black rounded-full bg-green-500 border-[30px] m-3 text-3xl font-bold p-3; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .delete-button { | .delete-button { | ||||||
|   @apply border-black rounded bg-red-500 border-[30px] m-3 text-3xl font-bold p-3 |   @apply border-black rounded bg-red-500 border-[30px] m-3 text-3xl font-bold p-3; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| import {Pipe, PipeTransform} from '@angular/core'; | import { Pipe, PipeTransform } from '@angular/core'; | ||||||
| 
 | 
 | ||||||
| @Pipe({ | @Pipe({ | ||||||
|   name: 'text', |   name: 'text', | ||||||
|   standalone: true |   standalone: true, | ||||||
| }) | }) | ||||||
| export class TextPipe implements PipeTransform { | export class TextPipe implements PipeTransform { | ||||||
|   transform(value: string): string { |   transform(value: string): string { | ||||||
|     return value.replaceAll("L", "P"); |     return value.replaceAll('L', 'P'); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,11 +1,9 @@ | ||||||
| /** @type {import('tailwindcss').Config} */ | /** @type {import('tailwindcss').Config} */ | ||||||
| 
 | 
 | ||||||
| const colors = require('tailwindcss/colors'); | const colors = require("tailwindcss/colors"); | ||||||
| 
 | 
 | ||||||
| module.exports = { | module.exports = { | ||||||
|   content: [ |   content: ["./src/**/*.{html,ts}"], | ||||||
|     "./src/**/*.{html,ts}", |  | ||||||
|   ], |  | ||||||
|   theme: { |   theme: { | ||||||
|     extend: { |     extend: { | ||||||
|       colors: { |       colors: { | ||||||
|  | @ -13,16 +11,15 @@ module.exports = { | ||||||
|       }, |       }, | ||||||
|       keyframes: { |       keyframes: { | ||||||
|         shake: { |         shake: { | ||||||
|           '0%, 100%': { transform: 'translateX(0)' }, |           "0%, 100%": { transform: "translateX(0)" }, | ||||||
|           '25%': { transform: 'translateX(-30px)' }, |           "25%": { transform: "translateX(-30px)" }, | ||||||
|           '75%': { transform: 'translateX(30px)' }, |           "75%": { transform: "translateX(30px)" }, | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|       animation: { |       animation: { | ||||||
|         shake: 'shake 10s ease-in-out infinite', |         shake: "shake 10s ease-in-out infinite", | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|   plugins: [], |   plugins: [], | ||||||
| } | }; | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -6,10 +6,6 @@ | ||||||
|     "outDir": "./out-tsc/app", |     "outDir": "./out-tsc/app", | ||||||
|     "types": [] |     "types": [] | ||||||
|   }, |   }, | ||||||
|   "files": [ |   "files": ["src/main.ts"], | ||||||
|     "src/main.ts" |   "include": ["src/**/*.d.ts"] | ||||||
|   ], |  | ||||||
|   "include": [ |  | ||||||
|     "src/**/*.d.ts" |  | ||||||
|   ] |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,10 +18,7 @@ | ||||||
|     "importHelpers": true, |     "importHelpers": true, | ||||||
|     "target": "ES2022", |     "target": "ES2022", | ||||||
|     "module": "ES2022", |     "module": "ES2022", | ||||||
|     "lib": [ |     "lib": ["ES2022", "dom"] | ||||||
|       "ES2022", |  | ||||||
|       "dom" |  | ||||||
|     ] |  | ||||||
|   }, |   }, | ||||||
|   "angularCompilerOptions": { |   "angularCompilerOptions": { | ||||||
|     "enableI18nLegacyMessageIdFormat": false, |     "enableI18nLegacyMessageIdFormat": false, | ||||||
|  |  | ||||||
|  | @ -4,12 +4,7 @@ | ||||||
|   "extends": "./tsconfig.json", |   "extends": "./tsconfig.json", | ||||||
|   "compilerOptions": { |   "compilerOptions": { | ||||||
|     "outDir": "./out-tsc/spec", |     "outDir": "./out-tsc/spec", | ||||||
|     "types": [ |     "types": ["jasmine"] | ||||||
|       "jasmine" |  | ||||||
|     ] |  | ||||||
|   }, |   }, | ||||||
|   "include": [ |   "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] | ||||||
|     "src/**/*.spec.ts", |  | ||||||
|     "src/**/*.d.ts" |  | ||||||
|   ] |  | ||||||
| } | } | ||||||
|  |  | ||||||
		Reference in a new issue