Compare commits

..

1 commit

45 changed files with 370 additions and 568 deletions

View file

@ -16,7 +16,9 @@
"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": ["zone.js"], "polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"assets": [ "assets": [
{ {
@ -25,7 +27,9 @@
}, },
"src/assets" "src/assets"
], ],
"styles": ["src/styles.css"], "styles": [
"src/styles.css"
],
"scripts": [] "scripts": []
}, },
"configurations": { "configurations": {
@ -70,7 +74,10 @@
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular-devkit/build-angular:karma",
"options": { "options": {
"polyfills": ["zone.js", "zone.js/testing"], "polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json", "tsConfig": "tsconfig.spec.json",
"assets": [ "assets": [
{ {
@ -78,7 +85,9 @@
"input": "public" "input": "public"
} }
], ],
"styles": ["src/styles.css"], "styles": [
"src/styles.css"
],
"scripts": [] "scripts": []
} }
} }

BIN
bun.lockb

Binary file not shown.

View file

@ -1,4 +1,6 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["local>Renovate/renovate-config"] "extends": [
"local>Renovate/renovate-config"
]
} }

View file

@ -1,6 +1 @@
<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>
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>

View file

@ -1,4 +1,5 @@
import { Component } from '@angular/core'; import { Component } from "@angular/core";
@Component({ @Component({
selector: 'app-button', selector: 'app-button',

View file

@ -1,12 +1,4 @@
<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"> <p [class.hidden]="!isBroke" class="text-red-500 mb-4">I'm broke. Now I'm about as poor as Jan-Marlon.</p>
I'm broke. Now I'm about as poor as Jan-Marlon. <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>
</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>

View file

@ -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',

View file

@ -1,37 +1,8 @@
<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"> <p class="text-lg font-medium text-green-600 mb-4">Price: {{hotel.price | currency : getCurrencyCode(selectedLanguage) : "symbol" : "2.2-2" : selectedLanguage}}</p>
Price: <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>
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 <img src="{{hotel.imageUrl}}" alt="Hotel" class="w-full rounded-full h-auto animate-shake shadow-lg mb-4">
src="{{ hotel.imageUrl }}" <a *ngIf="!isDetail" routerLink="/hotels/{{hotel.id}}" class="submit-button text-blue-600 hover:text-blue-800 hover:underline mb-4 block">Details</a>
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
>

View file

@ -1,27 +1,20 @@
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: [ imports: [ChildComponent, CurrencyPipe, FormsModule, StarRatingComponent, NgIf, RouterLink, NgFor],
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;
@ -45,19 +38,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"
}, }
]; ];
} }

View file

@ -1,8 +1,9 @@
<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>

View file

@ -1,5 +1,6 @@
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',

View file

@ -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"]
}, }
]); ]);
} }
} }

View file

@ -1,6 +1 @@
<input <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">
[(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"
/>

View file

@ -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) {

View file

@ -10,6 +10,7 @@ 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[] = [
{ {
@ -19,43 +20,39 @@ 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 return hotels.length > 0 ? Math.max(...hotels.map(hotel => hotel.id)) + 1 : 1;
? Math.max(...hotels.map((hotel) => hotel.id)) + 1
: 1;
} }
} }

View file

@ -24,8 +24,6 @@ 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( expect(compiled.querySelector('h1')?.textContent).toContain('Hello, hotel-manager');
'Hello, hotel-manager',
);
}); });
}); });

View file

@ -1,13 +1,7 @@
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 { import { AsyncPipe, NgFor, NgForOf, NgIf, UpperCasePipe } from '@angular/common';
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';
@ -16,23 +10,15 @@ 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: [ imports: [RouterOutlet, NgFor, NgForOf, NgIf, HotelItem, SearchComponent, UpperCasePipe, TextPipe, AsyncPipe],
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 {
}

View file

@ -1,8 +1,4 @@
import { import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
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';
@ -20,6 +16,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))]
],
}; };

View file

@ -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,
}, }
]; ];

View file

@ -1,11 +1,25 @@
import { import { AbstractControl, FormGroup, ValidationErrors, ValidatorFn } from "@angular/forms";
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)) {
@ -14,23 +28,7 @@ export class CrossValidator {
const contactType = control.get('contactType')?.value; const contactType = control.get('contactType')?.value;
if (contactType == ('None' || null)) return null; if (contactType == "None") 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;
};
} }
} }

View file

@ -8,8 +8,9 @@ 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;

View file

@ -11,51 +11,35 @@ import { HotelFormComponent } from '../hotel-form/hotel-form.component';
@Component({ @Component({
selector: 'app-hotel-details', selector: 'app-hotel-details',
standalone: true, standalone: true,
imports: [ imports: [CurrencyPipe, StarRatingComponent, HotelItem, HotelFormComponent, NgIf, NgForOf],
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( constructor(private route: ActivatedRoute, private http: HttpClient, private router: Router) { }
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 this.http.delete<Hotel>("/api/hotels/" + this.route.snapshot.paramMap.get("id")).subscribe();
.delete<Hotel>('/api/hotels/' + this.route.snapshot.paramMap.get('id')) this.router.navigate(["/"]);
.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 this.http.get<Hotel>("api/hotels/" + hotelId).pipe(
.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;
}); })
} }
} }

View file

@ -1,47 +1,18 @@
<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']"> <div class="text-red-500 text-5xl font-bold" *ngIf="errorMessages['name']">{{ errorMessages['name'] }}</div>
{{ 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">
</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']"> <div class="text-red" *ngIf="errorMessages['description']">{{ errorMessages['description'] }}</div>
{{ errorMessages["description"] }} <input type="text" class="input-field" [class.border-8]='hotelForm.get("description")?.invalid' id="description"
</div> formControlName="description">
<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']"> <div class="text-red" *ngIf="errorMessages['price']">{{ errorMessages['price'] }}</div>
{{ errorMessages["price"] }} <input type="number" class="input-field"
</div> [class.border-8]='hotelForm.get("price")?.invalid && hotelForm.get("price")?.touched' id="price"
<input formControlName="price">
type="number" <button class="submit-button" (click)="submit()" [disabled]="!hotelForm.valid" type="submit">😃 Submit</button>
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>

View file

@ -8,8 +8,9 @@ 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;

View file

@ -1,12 +1,6 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { Hotel } from '../HotelItem/hotel'; import { Hotel } from '../HotelItem/hotel';
import { import { AbstractControl, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
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';
@ -16,50 +10,43 @@ 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( constructor(public http: HttpClient, public router: Router) {
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) this.errorMsg = Object.keys(value.errors).map(key => {
.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( .map(errorKey => this.validationErrorMessages[errorKey] || `Unknown error: ${errorKey}`)
(errorKey) =>
this.validationErrorMessages[errorKey] ||
`Unknown error: ${errorKey}`,
)
.join(' '); .join(' ');
} }
}); });
@ -80,11 +67,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(["/"]);
} }
} }

View file

@ -1,13 +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>
<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 <app-hotel-item *ngIf="hotel.hotelName.toLowerCase().includes(search.toLowerCase())" [hotel]="hotel"></app-hotel-item>
*ngIf="hotel.hotelName.toLowerCase().includes(search.toLowerCase())"
[hotel]="hotel"
></app-hotel-item>
</div> </div>
} }
</div> </div>

View file

@ -8,8 +8,9 @@ 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;

View file

@ -17,50 +17,41 @@ interface User {
@Component({ @Component({
selector: 'app-hotel-list', selector: 'app-hotel-list',
standalone: true, standalone: true,
imports: [ imports: [UpperCasePipe, TextPipe, SearchComponent, HotelItem, NgFor, NgIf, RouterLink],
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 stream.pipe(
.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"]);

View file

@ -1,120 +1,59 @@
<p>Create Hotel</p> <p>Create Hotel</p>
<form [formGroup]="hotelForm"> <form [formGroup]="hotelForm">
<div class="text-red-500 bg-white m-3" *ngIf="errorMessages['form']"> <div class="text-red-500" *ngIf="errorMessages['form']">{{ errorMessages['form'] }}</div>
{{ errorMessages["form"] }}
</div>
<label for="name">Name</label> <label for="name">Name</label>
<div class="text-red-500" *ngIf="errorMessages['name']"> <div class="text-red-500" *ngIf="errorMessages['name']">{{ errorMessages['name'] }}</div>
{{ errorMessages["name"] }} <input type="text" class="border-red-500"
</div> [class.border-8]='hotelForm.get("name")?.invalid && hotelForm.get("name")?.touched' id="name"
<input formControlName="name">
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']"> <div class="text-red-500" *ngIf="errorMessages['description']">{{ errorMessages['description'] }}</div>
{{ errorMessages["description"] }} <input type="text" class="border-red-500"
</div> [class.border-8]='hotelForm.get("description")?.invalid && hotelForm.get("description")?.touched' id="description"
<input formControlName="description">
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']"> <div class="text-red-500" *ngIf="errorMessages['imageUrl']">{{ errorMessages['imageUrl'] }}</div>
{{ errorMessages["imageUrl"] }} <input type="url" class="border-red-500"
</div> [class.border-8]='hotelForm.get("imageUel")?.invalid && hotelForm.get("imageUrl")?.touched' id="imageUrl"
<input formControlName="imageUrl">
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']"> <div class="text-red-500" *ngIf="errorMessages['price']">{{ errorMessages['price'] }}</div>
{{ errorMessages["price"] }} <input type="number" class="border-red-500" [class.border-8]='hotelForm.get("price")?.invalid' id="price"
</div> formControlName="price">
<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']"> <div class="text-red-500" *ngIf="errorMessages['rating']">{{ errorMessages['rating'] }}</div>
{{ errorMessages["rating"] }} <input type="rating" class="border-red-500" [class.border-8]='hotelForm.get("rating")?.invalid' id="rating"
</div> formControlName="rating">
<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 <input type="tag" class="border-red-500" [class.border-8]='hotelForm.get("tag")?.invalid' id="tag"
type="tag" formControlName="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 <input type="email" formControlName="emailConfirmation" id="emailConfirmation">
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 <input type="tel" formControlName="phoneConfirmation" id="phoneConfirmation">
type="tel"
formControlName="phoneConfirmation"
id="phoneConfirmation"
/>
</div> </div>
} }
<button <button class="submit-button" (click)="submit()" [disabled]="!hotelForm.valid" type="submit">Submit</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>

View file

@ -8,8 +8,9 @@ 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;

View file

@ -1,13 +1,6 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { import { AbstractControl, FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
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';
@ -18,48 +11,41 @@ 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",
mismatchEmail: 'The Emails must be the same.', }
mismatchPhone: 'The Phone numbers must 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( .map(errorKey => this.validationErrorMessages[errorKey] || `Unknown error: ${errorKey}`)
(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 = const errorMessage = this.fullFormValidationErrorMessages[errorKey] || `Unknown error: ${errorKey}`;
this.fullFormValidationErrorMessages[errorKey] ||
`Unknown error: ${errorKey}`;
this.errorMessages['form'] = errorMessage; this.errorMessages['form'] = errorMessage;
}); });
} }
@ -74,29 +60,27 @@ 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() {
@ -107,27 +91,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( constructor(public http: HttpClient, public router: Router) {
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(["/"]);
} }
} }

View file

@ -8,8 +8,9 @@ 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;

View file

@ -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;

View file

@ -2,14 +2,9 @@
<form [formGroup]="loginForm"> <form [formGroup]="loginForm">
<label for="username">Username</label> <label for="username">Username</label>
<input <input type="text" id="username" autocomplete="username" formControlName="username">
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>

View file

@ -8,8 +8,9 @@ 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;

View file

@ -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('')
}); });
} }

View file

@ -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");
} }
} }

View file

@ -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>

View file

@ -2,6 +2,5 @@ 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).catch((err) => bootstrapApplication(AppComponent, appConfig)
console.error(err), .catch((err) => console.error(err));
);

View file

@ -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
} }

View file

@ -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");
} }
} }

View file

@ -1,9 +1,11 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
const colors = require("tailwindcss/colors"); const colors = require('tailwindcss/colors');
module.exports = { module.exports = {
content: ["./src/**/*.{html,ts}"], content: [
"./src/**/*.{html,ts}",
],
theme: { theme: {
extend: { extend: {
colors: { colors: {
@ -11,15 +13,16 @@ 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: [],
}; }

View file

@ -6,6 +6,10 @@
"outDir": "./out-tsc/app", "outDir": "./out-tsc/app",
"types": [] "types": []
}, },
"files": ["src/main.ts"], "files": [
"include": ["src/**/*.d.ts"] "src/main.ts"
],
"include": [
"src/**/*.d.ts"
]
} }

View file

@ -18,7 +18,10 @@
"importHelpers": true, "importHelpers": true,
"target": "ES2022", "target": "ES2022",
"module": "ES2022", "module": "ES2022",
"lib": ["ES2022", "dom"] "lib": [
"ES2022",
"dom"
]
}, },
"angularCompilerOptions": { "angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false, "enableI18nLegacyMessageIdFormat": false,

View file

@ -4,7 +4,12 @@
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./out-tsc/spec", "outDir": "./out-tsc/spec",
"types": ["jasmine"] "types": [
"jasmine"
]
}, },
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"] "include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
} }