Refactor order process to fix flicker bug
This commit is contained in:
@@ -1,195 +1,195 @@
|
|||||||
<div class="w-full h-full relative">
|
<div class="w-full h-full relative">
|
||||||
|
|
||||||
@if (!performance() && (loadingService.loading$ | async)){
|
@if (!performance() && (loadingService.loading$ | async)){
|
||||||
<div class="w-full h-full flex items-center justify-center">
|
<div class="w-full h-full flex items-center justify-center">
|
||||||
<mat-progress-spinner
|
|
||||||
mode="indeterminate"
|
|
||||||
diameter="50"
|
|
||||||
></mat-progress-spinner>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@else if (performance()) {
|
|
||||||
|
|
||||||
<div class="absolute top-18 z-20 w-full px-6">
|
|
||||||
<app-performance-info
|
|
||||||
class="w-full h-10"
|
|
||||||
[performance]="performance()!"
|
|
||||||
></app-performance-info>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if(isSubmitting) {
|
|
||||||
<div class="absolute top-55 z-25 w-full px-6 my-auto">
|
|
||||||
<mat-progress-spinner
|
<mat-progress-spinner
|
||||||
class="m-auto"
|
|
||||||
mode="indeterminate"
|
mode="indeterminate"
|
||||||
diameter="100"
|
diameter="50"
|
||||||
></mat-progress-spinner>
|
></mat-progress-spinner>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@else if (performance()) {
|
||||||
|
|
||||||
<mat-stepper orientation="horizontal" linear="true" [disableRipple]="true" (selectionChange)="onStepChange($event)" #stepper>
|
<div class="absolute top-18 z-20 w-full px-6">
|
||||||
<mat-step>
|
<app-performance-info
|
||||||
<ng-template matStepLabel>Warenkorb</ng-template>
|
class="w-full h-10"
|
||||||
|
[performance]="performance()!"
|
||||||
|
></app-performance-info>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="performance-info-space"></div>
|
@if(isSubmitting()) {
|
||||||
|
<div class="absolute top-55 z-25 w-full px-6 my-auto">
|
||||||
|
<mat-progress-spinner
|
||||||
|
class="m-auto"
|
||||||
|
mode="indeterminate"
|
||||||
|
diameter="100"
|
||||||
|
></mat-progress-spinner>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Seat-Selection Overview -->
|
<mat-stepper orientation="horizontal" linear="true" [disableRipple]="true" (selectionChange)="onStepChange($event)" #stepper>
|
||||||
<div class="mb-4 p-2">
|
<mat-step>
|
||||||
@for (seatCategory of seatCategories(); track $index) {
|
<ng-template matStepLabel>Warenkorb</ng-template>
|
||||||
<div class="h-2"></div>
|
|
||||||
<app-seat-selection [seatCategory]="seatCategory"></app-seat-selection>
|
|
||||||
}
|
|
||||||
@empty {
|
|
||||||
<app-no-seats-in-hall></app-no-seats-in-hall>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<mat-divider></mat-divider>
|
<div class="performance-info-space"></div>
|
||||||
|
|
||||||
<!-- Total Price -->
|
<!-- Seat-Selection Overview -->
|
||||||
<div class="flex justify-between p-2 mt-1 items-baseline">
|
<div class="mb-4 p-2">
|
||||||
<p class="font-semibold text-lg">
|
@for (seatCategory of seatCategories(); track $index) {
|
||||||
Tickets gesamt:
|
<div class="h-2"></div>
|
||||||
</p>
|
<app-seat-selection [seatCategory]="seatCategory"></app-seat-selection>
|
||||||
<p class="font-semibold text-2xl bg-linear-to-r from-indigo-500 to-pink-600 bg-clip-text text-transparent">
|
}
|
||||||
{{ getPriceDisplay(totalPrice()) }}
|
@empty {
|
||||||
</p>
|
<app-no-seats-in-hall></app-no-seats-in-hall>
|
||||||
</div>
|
}
|
||||||
|
|
||||||
<!-- Buttons -->
|
|
||||||
<div class="flex space-x-5 mt-10">
|
|
||||||
<button mat-button matButton="outlined" matStepperNext class="w-1/2" [disabled]="totalSeats()==0" (click)="reservationClicked()">Reservieren</button>
|
|
||||||
<button mat-button matButton="filled" matStepperNext class="w-1/2" [disabled]="totalSeats()==0" (click)="purchaseClicked()">Kaufen</button>
|
|
||||||
</div>
|
|
||||||
</mat-step>
|
|
||||||
|
|
||||||
<mat-step [stepControl]="dataForm">
|
|
||||||
<form [formGroup]="dataForm">
|
|
||||||
<ng-template matStepLabel>Anschrift</ng-template>
|
|
||||||
|
|
||||||
<div class="performance-info-space"></div>
|
|
||||||
|
|
||||||
@if (seatsReserved && !isSubmitting) {
|
|
||||||
<div class="h-4"></div>
|
|
||||||
@if (successful) {
|
|
||||||
<app-reservation-success [order]="this.createdOrder"></app-reservation-success>
|
|
||||||
} @else {
|
|
||||||
<app-reservation-failed></app-reservation-failed>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@else {
|
|
||||||
|
|
||||||
<!-- Name -->
|
|
||||||
<mat-form-field class="w-full mt-8">
|
|
||||||
<mat-label>Name</mat-label>
|
|
||||||
<input matInput formControlName="name" placeholder="Max Mustermann" />
|
|
||||||
@if (fData['name'].hasError('minlength')) { <mat-error>Mindestens 3 Zeichen</mat-error> }
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<!-- E-Mail -->
|
|
||||||
<mat-form-field class="w-full mt-2">
|
|
||||||
<mat-label>E-Mail Adresse</mat-label>
|
|
||||||
<input matInput formControlName="email" placeholder="max.mustermann@edu.fhdw.de" />
|
|
||||||
@if (fData['email'].hasError('email')) { <mat-error>Ungültige E-Mail-Adresse</mat-error> }
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<!-- Checkbox -->
|
|
||||||
<div class="w-full my-4">
|
|
||||||
<mat-checkbox required formControlName="accept" class="checkbox-invalid" [class]="{ 'checkbox-invalid': submitted && fData['accept'].hasError('required') }">
|
|
||||||
Ich akzeptiere die AGB und die Datenbestimmung
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Buttons -->
|
<mat-divider></mat-divider>
|
||||||
<div class="flex space-x-5 mt-10">
|
|
||||||
<button type="button" mat-button matButton="outlined" (click)="stepper.reset()" class="w-1/3" [disabled]="isSubmitting">Zurück</button>
|
|
||||||
<button type="submit" mat-button matButton="filled" (click)="nextPhaseButtonClicked(stepper)" class="w-2/3" [disabled]="isSubmitting">{{ secondPhaseButtonText }}</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
}
|
<!-- Total Price -->
|
||||||
|
<div class="flex justify-between p-2 mt-1 items-baseline">
|
||||||
</form>
|
<p class="font-semibold text-lg">
|
||||||
</mat-step>
|
Tickets gesamt:
|
||||||
<mat-step [stepControl]="paymentForm">
|
</p>
|
||||||
<form [formGroup]="paymentForm" (ngSubmit)="onSubmit()">
|
<p class="font-semibold text-2xl bg-linear-to-r from-indigo-500 to-pink-600 bg-clip-text text-transparent">
|
||||||
<ng-template matStepLabel>Zahlung</ng-template>
|
{{ getPriceDisplay(totalPrice()) }}
|
||||||
|
|
||||||
<div class="performance-info-space"></div>
|
|
||||||
|
|
||||||
@if (seatsPurchased && !isSubmitting) {
|
|
||||||
<div class="h-4"></div>
|
|
||||||
@if (successful) {
|
|
||||||
<app-purchase-success [tickets]="createdTickets"></app-purchase-success>
|
|
||||||
} @else {
|
|
||||||
<app-purchase-failed></app-purchase-failed>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@else {
|
|
||||||
|
|
||||||
<!-- Card Number -->
|
|
||||||
<mat-form-field class="w-full mt-8">
|
|
||||||
<mat-label>Kartennummer</mat-label>
|
|
||||||
<input
|
|
||||||
matInput
|
|
||||||
formControlName="cardNumber"
|
|
||||||
mask="0000 0000 0000 0000"
|
|
||||||
placeholder="1111 2222 3333 4444"
|
|
||||||
/>
|
|
||||||
@if (fPayment['cardNumber'].hasError('pattern')) { <mat-error>Ungültige Kartennummer</mat-error> }
|
|
||||||
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<!-- Card Name -->
|
|
||||||
<mat-form-field class="w-full">
|
|
||||||
<mat-label>Name des Besitzers</mat-label>
|
|
||||||
<input matInput formControlName="cardName" />
|
|
||||||
@if (fPayment['cardName'].hasError('minlength')) { <mat-error>Mindestens 3 Zeichen</mat-error> }
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<!-- Expiry & CVV -->
|
|
||||||
<div class="flex space-x-4">
|
|
||||||
<mat-form-field class="flex-1">
|
|
||||||
<mat-label>Gültig bis (MM/YY)</mat-label>
|
|
||||||
<input
|
|
||||||
matInput
|
|
||||||
formControlName="expiry"
|
|
||||||
mask="00/00"
|
|
||||||
placeholder="MM/YY"
|
|
||||||
[dropSpecialCharacters]="false"
|
|
||||||
/>
|
|
||||||
@if (fPayment['expiry'].hasError('pattern')) { <mat-error>Ungültiges Format</mat-error> }
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field class="flex-1">
|
|
||||||
<mat-label>CVV</mat-label>
|
|
||||||
<input matInput type="password" maxlength="4" formControlName="cvv" />
|
|
||||||
@if (fPayment['cvv'].hasError('pattern')) { <mat-error>3–4 Ziffernt</mat-error> }
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Info -->
|
|
||||||
<div class="flex w-full space-x-2 mt-2 items-center">
|
|
||||||
<mat-icon class="material-symbols-outlined opacity-50" style="font-size: 32px; width: 32px; height: 32px">
|
|
||||||
encrypted
|
|
||||||
</mat-icon>
|
|
||||||
<p class="text-sm opacity-75">
|
|
||||||
Ihre Zahlung wird sicher über unsere Partner verarbeitet.<br>Wir speichern keine Zahlungsinformationen.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<div class="flex space-x-4 mt-8">
|
<div class="flex space-x-5 mt-10">
|
||||||
<button mat-stroked-button color="primary" matStepperPrevious type="button" [disabled]="isSubmitting" class="w-1/3">
|
<button mat-button matButton="outlined" matStepperNext class="w-1/2" [disabled]="totalSeats()==0" (click)="reservationClicked()">Reservieren</button>
|
||||||
Zurück
|
<button mat-button matButton="filled" matStepperNext class="w-1/2" [disabled]="totalSeats()==0" (click)="purchaseClicked()">Kaufen</button>
|
||||||
</button>
|
|
||||||
<button mat-flat-button color="accent" matStepperNext type="submit" [disabled]="isSubmitting" (click)="makePurchase()" class="w-2/3">
|
|
||||||
{{ getPriceDisplay(totalPrice()) }} jetzt bezahlen
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
</mat-step>
|
||||||
</form>
|
|
||||||
</mat-step>
|
|
||||||
|
|
||||||
</mat-stepper>
|
<mat-step [stepControl]="dataForm">
|
||||||
}
|
<form [formGroup]="dataForm">
|
||||||
|
<ng-template matStepLabel>Anschrift</ng-template>
|
||||||
|
|
||||||
|
<div class="performance-info-space"></div>
|
||||||
|
|
||||||
|
@if (isReservationSuccess() && !isSubmitting()) {
|
||||||
|
<div class="h-4"></div>
|
||||||
|
<app-reservation-success [order]="createdOrder()!"></app-reservation-success>
|
||||||
|
}
|
||||||
|
@else if (isReservationError() && !isSubmitting()) {
|
||||||
|
<div class="h-4"></div>
|
||||||
|
<app-reservation-failed></app-reservation-failed>
|
||||||
|
}
|
||||||
|
@else {
|
||||||
|
|
||||||
|
<!-- Name -->
|
||||||
|
<mat-form-field class="w-full mt-8">
|
||||||
|
<mat-label>Name</mat-label>
|
||||||
|
<input matInput formControlName="name" placeholder="Max Mustermann" />
|
||||||
|
@if (fData['name'].hasError('minlength')) { <mat-error>Mindestens 3 Zeichen</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- E-Mail -->
|
||||||
|
<mat-form-field class="w-full mt-2">
|
||||||
|
<mat-label>E-Mail Adresse</mat-label>
|
||||||
|
<input matInput formControlName="email" placeholder="max.mustermann@edu.fhdw.de" />
|
||||||
|
@if (fData['email'].hasError('email')) { <mat-error>Ungültige E-Mail-Adresse</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Checkbox -->
|
||||||
|
<div class="w-full my-4">
|
||||||
|
<mat-checkbox required formControlName="accept" class="checkbox-invalid" [class.checkbox-invalid]="submitted() && fData['accept'].hasError('required')">
|
||||||
|
Ich akzeptiere die AGB und die Datenbestimmung
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<div class="flex space-x-5 mt-10">
|
||||||
|
<button type="button" mat-button matButton="outlined" (click)="stepper.reset()" class="w-1/3" [disabled]="isSubmitting()">Zurück</button>
|
||||||
|
<button type="submit" mat-button matButton="filled" (click)="nextPhaseButtonClicked(stepper)" class="w-2/3" [disabled]="isSubmitting()">{{ secondPhaseButtonText() }}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step [stepControl]="paymentForm">
|
||||||
|
<form [formGroup]="paymentForm" (ngSubmit)="makePurchase()">
|
||||||
|
<ng-template matStepLabel>Zahlung</ng-template>
|
||||||
|
|
||||||
|
<div class="performance-info-space"></div>
|
||||||
|
|
||||||
|
@if (isPurchaseSuccess() && !isSubmitting()) {
|
||||||
|
<div class="h-4"></div>
|
||||||
|
<app-purchase-success [tickets]="createdTickets()"></app-purchase-success>
|
||||||
|
}
|
||||||
|
@else if (isPurchaseError() && !isSubmitting()) {
|
||||||
|
<div class="h-4"></div>
|
||||||
|
<app-purchase-failed></app-purchase-failed>
|
||||||
|
}
|
||||||
|
@else {
|
||||||
|
|
||||||
|
<!-- Card Number -->
|
||||||
|
<mat-form-field class="w-full mt-8">
|
||||||
|
<mat-label>Kartennummer</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
formControlName="cardNumber"
|
||||||
|
mask="0000 0000 0000 0000"
|
||||||
|
placeholder="1111 2222 3333 4444"
|
||||||
|
/>
|
||||||
|
@if (fPayment['cardNumber'].hasError('pattern')) { <mat-error>Ungültige Kartennummer</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Card Name -->
|
||||||
|
<mat-form-field class="w-full">
|
||||||
|
<mat-label>Name des Besitzers</mat-label>
|
||||||
|
<input matInput formControlName="cardName" />
|
||||||
|
@if (fPayment['cardName'].hasError('minlength')) { <mat-error>Mindestens 3 Zeichen</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Expiry & CVV -->
|
||||||
|
<div class="flex space-x-4">
|
||||||
|
<mat-form-field class="flex-1">
|
||||||
|
<mat-label>Gültig bis (MM/YY)</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
formControlName="expiry"
|
||||||
|
mask="00/00"
|
||||||
|
placeholder="MM/YY"
|
||||||
|
[dropSpecialCharacters]="false"
|
||||||
|
/>
|
||||||
|
@if (fPayment['expiry'].hasError('pattern')) { <mat-error>Ungültiges Format</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field class="flex-1">
|
||||||
|
<mat-label>CVV</mat-label>
|
||||||
|
<input matInput type="password" maxlength="4" formControlName="cvv" />
|
||||||
|
@if (fPayment['cvv'].hasError('pattern')) { <mat-error>3–4 Ziffernt</mat-error> }
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Info -->
|
||||||
|
<div class="flex w-full space-x-2 mt-2 items-center">
|
||||||
|
<mat-icon class="material-symbols-outlined opacity-50" style="font-size: 32px; width: 32px; height: 32px">
|
||||||
|
encrypted
|
||||||
|
</mat-icon>
|
||||||
|
<p class="text-sm opacity-75">
|
||||||
|
Ihre Zahlung wird sicher über unsere Partner verarbeitet.<br>Wir speichern keine Zahlungsinformationen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<div class="flex space-x-4 mt-8">
|
||||||
|
<button mat-stroked-button color="primary" matStepperPrevious type="button" [disabled]="isSubmitting()" class="w-1/3">
|
||||||
|
Zurück
|
||||||
|
</button>
|
||||||
|
<button mat-flat-button color="accent" matStepperNext type="submit" [disabled]="isSubmitting()" class="w-2/3">
|
||||||
|
{{ getPriceDisplay(totalPrice()) }} jetzt bezahlen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
</mat-stepper>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
import { SelectedSeatsService } from './../selected-seats.service';
|
import { SelectedSeatsService } from './../selected-seats.service';
|
||||||
import { LoadingService } from './../loading.service';
|
import { LoadingService } from './../loading.service';
|
||||||
import { Bestellung, Eintrittskarte, Sitzkategorie, Sitzplatz, Vorstellung } from '@infinimotion/model-frontend';
|
import { Bestellung, Eintrittskarte, Sitzkategorie, Sitzplatz, Vorstellung } from '@infinimotion/model-frontend';
|
||||||
import { Component, computed, inject, input } from '@angular/core';
|
import { Component, computed, DestroyRef, inject, input, signal } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
||||||
import { HttpService } from '../http.service';
|
import { HttpService } from '../http.service';
|
||||||
import { catchError, tap, finalize } from 'rxjs';
|
import { catchError, tap, finalize, switchMap, map, EMPTY, forkJoin } from 'rxjs';
|
||||||
import { MatStepper } from '@angular/material/stepper';
|
import { MatStepper } from '@angular/material/stepper';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
|
type OrderState =
|
||||||
|
| { status: 'idle' }
|
||||||
|
| { status: 'submitting' }
|
||||||
|
| { status: 'reservation-success'; order: Bestellung }
|
||||||
|
| { status: 'reservation-error'; error: any }
|
||||||
|
| { status: 'purchase-success'; tickets: Eintrittskarte[] }
|
||||||
|
| { status: 'purchase-error'; error: any };
|
||||||
|
|
||||||
|
type SubmissionMode = 'reservation' | 'purchase';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-order',
|
selector: 'app-order',
|
||||||
@@ -15,182 +26,207 @@ import { MatStepper } from '@angular/material/stepper';
|
|||||||
styleUrl: './order.component.css'
|
styleUrl: './order.component.css'
|
||||||
})
|
})
|
||||||
export class OrderComponent {
|
export class OrderComponent {
|
||||||
paymentForm!: FormGroup;
|
private fb = inject(FormBuilder);
|
||||||
dataForm!: FormGroup;
|
private httpService = inject(HttpService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
submitted = false;
|
readonly loadingService = inject(LoadingService);
|
||||||
|
readonly selectedSeatsService = inject(SelectedSeatsService);
|
||||||
|
|
||||||
performance = input<Vorstellung>();
|
performance = input<Vorstellung>();
|
||||||
seatCategories = input.required<Sitzkategorie[]>();
|
seatCategories = input.required<Sitzkategorie[]>();
|
||||||
|
|
||||||
loadingService = inject(LoadingService);
|
paymentForm!: FormGroup;
|
||||||
private httpService = inject(HttpService)
|
dataForm!: FormGroup;
|
||||||
private selectedSeatsService = inject(SelectedSeatsService);
|
|
||||||
|
orderState = signal<OrderState>({ status: 'idle' });
|
||||||
|
submissionMode = signal<SubmissionMode | null>(null);
|
||||||
|
submitted = signal(false);
|
||||||
|
|
||||||
confetti: any;
|
confetti: any;
|
||||||
|
|
||||||
constructor(private fb: FormBuilder) {}
|
totalPrice = this.selectedSeatsService.totalPrice;
|
||||||
|
totalSeats = this.selectedSeatsService.totalSeats;
|
||||||
|
|
||||||
|
isSubmitting = computed(() => this.orderState().status === 'submitting');
|
||||||
|
|
||||||
|
secondPhaseButtonText = computed(() => {
|
||||||
|
const mode = this.submissionMode();
|
||||||
|
if (!mode) return 'Loading...';
|
||||||
|
|
||||||
|
if (mode === 'reservation') {
|
||||||
|
return this.totalSeats() > 1 ? 'Sitzplätze reservieren' : 'Sitzplatz reservieren';
|
||||||
|
}
|
||||||
|
return 'Weiter zur Zahlung';
|
||||||
|
});
|
||||||
|
|
||||||
|
isReservationSuccess = computed(() =>
|
||||||
|
this.orderState().status === 'reservation-success'
|
||||||
|
);
|
||||||
|
|
||||||
|
isPurchaseSuccess = computed(() =>
|
||||||
|
this.orderState().status === 'purchase-success'
|
||||||
|
);
|
||||||
|
|
||||||
|
isReservationError = computed(() =>
|
||||||
|
this.orderState().status === 'reservation-error'
|
||||||
|
);
|
||||||
|
|
||||||
|
isPurchaseError = computed(() =>
|
||||||
|
this.orderState().status === 'purchase-error'
|
||||||
|
);
|
||||||
|
|
||||||
|
createdOrder = computed(() => {
|
||||||
|
const state = this.orderState();
|
||||||
|
return state.status === 'reservation-success' ? state.order : null;
|
||||||
|
});
|
||||||
|
|
||||||
|
createdTickets = computed(() => {
|
||||||
|
const state = this.orderState();
|
||||||
|
return state.status === 'purchase-success' ? state.tickets : [];
|
||||||
|
});
|
||||||
|
|
||||||
// Form-Validation
|
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
this.dataForm = this.fb.group({
|
||||||
|
name: ['', [Validators.required, Validators.minLength(3)]],
|
||||||
|
email: ['', [Validators.required, Validators.email]],
|
||||||
|
accept: ['', Validators.requiredTrue],
|
||||||
|
});
|
||||||
this.paymentForm = this.fb.group({
|
this.paymentForm = this.fb.group({
|
||||||
cardNumber: ['', [Validators.required, Validators.pattern(/^\d{16}$/)]],
|
cardNumber: ['', [Validators.required, Validators.pattern(/^\d{16}$/)]],
|
||||||
cardName: ['', [Validators.required, Validators.minLength(3)]],
|
cardName: ['', [Validators.required, Validators.minLength(3)]],
|
||||||
expiry: ['', [Validators.required, Validators.pattern(/^(0[1-9]|1[0-2])\/\d{2}$/)]],
|
expiry: ['', [Validators.required, Validators.pattern(/^(0[1-9]|1[0-2])\/\d{2}$/)]],
|
||||||
cvv: ['', [Validators.required, Validators.pattern(/^\d{3,4}$/)]],
|
cvv: ['', [Validators.required, Validators.pattern(/^\d{3,4}$/)]],
|
||||||
});
|
});
|
||||||
this.dataForm = this.fb.group({
|
|
||||||
name: ['', [Validators.required, Validators.minLength(3)]],
|
|
||||||
email: ['', [Validators.required, Validators.email]],
|
|
||||||
accept: ['', Validators.requiredTrue],
|
|
||||||
});
|
|
||||||
this.confetti = (await import('canvas-confetti')).default;
|
this.confetti = (await import('canvas-confetti')).default;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fData() { return this.dataForm.controls; }
|
get fData() { return this.dataForm.controls; }
|
||||||
get fPayment() { return this.paymentForm.controls; }
|
get fPayment() { return this.paymentForm.controls; }
|
||||||
|
|
||||||
onSubmit() {
|
onStepChange(event: StepperSelectionEvent) {
|
||||||
if (this.dataForm.invalid) return;
|
this.submitted.set(false);
|
||||||
if (this.paymentForm.invalid) return;
|
this.selectedSeatsService.setSeatSelectable(event.selectedIndex === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStepChange(event: StepperSelectionEvent) {
|
reservationClicked() {
|
||||||
this.submitted = false;
|
this.submissionMode.set('reservation');
|
||||||
|
}
|
||||||
|
|
||||||
if(event.selectedIndex != 0) {
|
purchaseClicked() {
|
||||||
this.selectedSeatsService.setSeatIsSelectableFalse()
|
this.submissionMode.set('purchase');
|
||||||
} else {
|
|
||||||
this.selectedSeatsService.setSeatIsSelectableTrue()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextPhaseButtonClicked(stepper: MatStepper) {
|
nextPhaseButtonClicked(stepper: MatStepper) {
|
||||||
this.submitted = true;
|
this.submitted.set(true);
|
||||||
if (this.dataForm.invalid) return;
|
|
||||||
|
|
||||||
if (this.submissionMode === "reservation") {
|
if (this.dataForm.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.submissionMode() === 'reservation') {
|
||||||
this.makeReservation();
|
this.makeReservation();
|
||||||
} else if (this.submissionMode === "purchase") {
|
} else if (this.submissionMode() === 'purchase') {
|
||||||
stepper.next();
|
stepper.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
totalPrice = computed(() =>
|
|
||||||
this.selectedSeatsService.getSelectedSeatsList().reduce((sum, seat) => sum + seat.row.category.price, 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
totalSeats = computed(() =>
|
|
||||||
this.selectedSeatsService.getSelectedSeatsList().length
|
|
||||||
);
|
|
||||||
|
|
||||||
getPriceDisplay(price: number): string {
|
|
||||||
return `${(price / 100).toFixed(2)} €`;
|
|
||||||
}
|
|
||||||
|
|
||||||
isSubmitting: boolean = false;
|
|
||||||
secondPhaseButtonText: string = "Loading..."
|
|
||||||
submissionMode!: 'reservation' | 'purchase';
|
|
||||||
|
|
||||||
seatsReserved: boolean = false;
|
|
||||||
seatsPurchased: boolean = false;
|
|
||||||
successful: boolean = false;
|
|
||||||
|
|
||||||
reservationClicked() {
|
|
||||||
this.submissionMode = "reservation";
|
|
||||||
if (this.totalSeats() > 1) {
|
|
||||||
this.secondPhaseButtonText = "Sitzplätze reservieren"
|
|
||||||
} else {
|
|
||||||
this.secondPhaseButtonText = "Sitzplatz reservieren"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
purchaseClicked() {
|
|
||||||
this.submissionMode = "purchase"
|
|
||||||
this.secondPhaseButtonText = "Weiter zur Zahlung"
|
|
||||||
}
|
|
||||||
|
|
||||||
makeReservation() {
|
makeReservation() {
|
||||||
|
this.orderState.set({ status: 'submitting' });
|
||||||
this.loadingService.show();
|
this.loadingService.show();
|
||||||
this.disableInputs()
|
this.disableForms();
|
||||||
|
|
||||||
const order = this.generateNewOrderObject(this.dataForm.value.email, false);
|
const order = this.generateNewOrderObject(this.dataForm.value.email, false);
|
||||||
const seats = this.selectedSeatsService.getSelectedSeatsList();
|
const seats = this.selectedSeatsService.selectedSeats();
|
||||||
const performance = this.performance()!;
|
const performance = this.performance()!;
|
||||||
this.sendToBackend(order, seats, performance);
|
|
||||||
this.seatsReserved = true;
|
this.submitOrder(order, seats, performance, 'reservation');
|
||||||
}
|
}
|
||||||
|
|
||||||
makePurchase() {
|
makePurchase() {
|
||||||
|
if (this.paymentForm.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.orderState.set({ status: 'submitting' });
|
||||||
this.loadingService.show();
|
this.loadingService.show();
|
||||||
this.disableInputs()
|
this.disableForms();
|
||||||
|
|
||||||
const order = this.generateNewOrderObject(this.dataForm.value.email, true);
|
const order = this.generateNewOrderObject(this.dataForm.value.email, true);
|
||||||
const seats = this.selectedSeatsService.getSelectedSeatsList();
|
const seats = this.selectedSeatsService.selectedSeats();
|
||||||
const performance = this.performance()!;
|
const performance = this.performance()!;
|
||||||
this.sendToBackend(order, seats, performance);
|
|
||||||
this.seatsPurchased = true;
|
this.submitOrder(order, seats, performance, 'purchase');
|
||||||
}
|
}
|
||||||
|
|
||||||
createdOrder!: Bestellung;
|
|
||||||
createdTickets!: Eintrittskarte[];
|
|
||||||
|
|
||||||
sendToBackend(order: Bestellung, seats: Sitzplatz[], performance: Vorstellung) {
|
submitOrder(order: Bestellung, seats: Sitzplatz[], performance: Vorstellung, mode: SubmissionMode) {
|
||||||
this.httpService.addOrder(order).pipe(
|
this.httpService.addOrder(order).pipe(
|
||||||
tap(createdOrder => {
|
// Order erstellen
|
||||||
this.createdOrder = createdOrder;
|
switchMap(createdOrder => {
|
||||||
|
|
||||||
const ticketCreations = seats.map(seat => {
|
// Tickets parallel erstellen
|
||||||
|
const ticketObservables = seats.map(seat => {
|
||||||
const ticket = this.generateNewTicketObject(performance, seat, createdOrder);
|
const ticket = this.generateNewTicketObject(performance, seat, createdOrder);
|
||||||
return this.httpService.addTicket(ticket);
|
return this.httpService.addTicket(ticket);
|
||||||
});
|
});
|
||||||
|
|
||||||
Promise.all(ticketCreations.map(obs => obs.toPromise()))
|
// Warten bis alles fertig sind
|
||||||
.then(async createdTickets => {
|
return forkJoin(ticketObservables).pipe(
|
||||||
this.createdTickets = createdTickets.filter(
|
tap(createdTickets => {
|
||||||
(ticket): ticket is Eintrittskarte => ticket !== undefined
|
// Success Handling
|
||||||
);
|
if (mode === 'reservation') {
|
||||||
|
this.orderState.set({
|
||||||
|
status: 'reservation-success',
|
||||||
|
order: createdOrder
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.orderState.set({
|
||||||
|
status: 'purchase-success',
|
||||||
|
tickets: createdTickets
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.successful = true;
|
this.selectedSeatsService.commit();
|
||||||
this.selectedSeatsService.setCommitedTrue();
|
|
||||||
this.loadingService.hide();
|
this.loadingService.hide();
|
||||||
|
this.showConfetti();
|
||||||
this.confetti({
|
|
||||||
particleCount: 100,
|
|
||||||
angle: 0,
|
|
||||||
spread: 180,
|
|
||||||
origin: { x: -0.1, y: 0.75 }
|
|
||||||
});
|
|
||||||
this.confetti({
|
|
||||||
particleCount: 100,
|
|
||||||
angle: 180,
|
|
||||||
spread: 180,
|
|
||||||
origin: { x: 1.1, y: 0.75 }
|
|
||||||
});
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
);
|
||||||
this.loadingService.showError(err);
|
|
||||||
this.successful = false;
|
|
||||||
console.error('Fehler beim Anlegen der Eintrittskarten', err);
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
catchError(err => {
|
catchError(err => {
|
||||||
|
// Error handling
|
||||||
|
console.error('Fehler beim Anlegen der Bestellung/Tickets:', err);
|
||||||
this.loadingService.showError(err);
|
this.loadingService.showError(err);
|
||||||
this.successful = false;
|
|
||||||
console.error('Fehler beim Anlegen der Bestellung', err);
|
if (mode === 'reservation') {
|
||||||
return [];
|
this.orderState.set({ status: 'reservation-error', error: err });
|
||||||
|
} else {
|
||||||
|
this.orderState.set({ status: 'purchase-error', error: err });
|
||||||
|
}
|
||||||
|
|
||||||
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
finalize(() => {
|
finalize(() => {
|
||||||
this.enableInputs();
|
this.enableForms();
|
||||||
})
|
}),
|
||||||
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe();
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private showConfetti() {
|
||||||
|
this.confetti({
|
||||||
|
particleCount: 100,
|
||||||
|
angle: 0,
|
||||||
|
spread: 180,
|
||||||
|
origin: { x: -0.1, y: 0.75 }
|
||||||
|
});
|
||||||
|
this.confetti({
|
||||||
|
particleCount: 100,
|
||||||
|
angle: 180,
|
||||||
|
spread: 180,
|
||||||
|
origin: { x: 1.1, y: 0.75 }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private generateCode(length: number = 6): string {
|
private generateCode(length: number = 6): string {
|
||||||
const chars = "ABCDEFGHJKLMNPQRSUVWXYZ23456789";
|
const chars = "ABCDEFGHJKLMNPQRSUVWXYZ23456789";
|
||||||
@@ -206,7 +242,7 @@ export class OrderComponent {
|
|||||||
|
|
||||||
private generateNewOrderObject(mail: string, isBooked: boolean): Bestellung {
|
private generateNewOrderObject(mail: string, isBooked: boolean): Bestellung {
|
||||||
return{
|
return{
|
||||||
id: 0, // Wird durch Backend gesetzt
|
id: 0, // Wird durch das Backend gesetzt
|
||||||
mail: mail,
|
mail: mail,
|
||||||
code: this.generateCode(length=6),
|
code: this.generateCode(length=6),
|
||||||
reserved: new Date(),
|
reserved: new Date(),
|
||||||
@@ -217,7 +253,7 @@ export class OrderComponent {
|
|||||||
|
|
||||||
private generateNewTicketObject(show: Vorstellung, seat: Sitzplatz, order: Bestellung): Eintrittskarte {
|
private generateNewTicketObject(show: Vorstellung, seat: Sitzplatz, order: Bestellung): Eintrittskarte {
|
||||||
return {
|
return {
|
||||||
id: 0, // Wird durch Backend gesetzt
|
id: 0, // Wird durch das Backend gesetzt
|
||||||
code: 'T' + this.generateCode(length=7),
|
code: 'T' + this.generateCode(length=7),
|
||||||
show: show,
|
show: show,
|
||||||
seat: seat,
|
seat: seat,
|
||||||
@@ -225,16 +261,17 @@ export class OrderComponent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private disableInputs() {
|
private disableForms(): void {
|
||||||
this.dataForm.disable();
|
this.dataForm.disable();
|
||||||
this.paymentForm.disable();
|
this.paymentForm.disable();
|
||||||
this.isSubmitting = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enableInputs() {
|
private enableForms(): void {
|
||||||
this.dataForm.enable();
|
this.dataForm.enable();
|
||||||
this.paymentForm.enable();
|
this.paymentForm.enable();
|
||||||
this.isSubmitting = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPriceDisplay(price: number): string {
|
||||||
|
return `${(price / 100).toFixed(2)} €`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
warning
|
warning
|
||||||
</mat-icon>
|
</mat-icon>
|
||||||
<h1 class="text-xl font-bold">Kauf fehlgeschlagen!</h1>
|
<h1 class="text-xl font-bold">Kauf fehlgeschlagen!</h1>
|
||||||
<p class="text-center">Leider konnten Ihre Sitzplätze nicht gebucht werden.<br>Dies kann passieren, wenn andere Nutzer gleichzeitig versucht haben, dieselben Sitzplätze zu kaufen.</p>
|
<p class="text-center">Leider konnten Ihre Sitzplätze nicht gekauft werden. Dies kann passieren, wenn andere Nutzer zeitgleich versucht haben, dieselben Sitzplätze zu kaufen.</p>
|
||||||
|
|
||||||
<button mat-button matButton="filled" class="error-button mt-4 w-80">Erneut versuchen</button>
|
<button mat-button matButton="filled" class="error-button mt-4 w-80">Erneut versuchen</button>
|
||||||
<button routerLink="/schedule" mat-button matButton="outlined" color="accent" class="error-button w-80">Zurück zur Programmauswahl</button>
|
<button routerLink="/schedule" mat-button matButton="outlined" color="accent" class="error-button w-80">Zurück zur Programmauswahl</button>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
warning
|
warning
|
||||||
</mat-icon>
|
</mat-icon>
|
||||||
<h1 class="text-xl font-bold">Reservierung fehlgeschlagen!</h1>
|
<h1 class="text-xl font-bold">Reservierung fehlgeschlagen!</h1>
|
||||||
<p class="text-center">Leider konnten Ihre Sitzplätze nicht reserviert werden.<br>Dies kann passieren, wenn andere Nutzer gleichzeitig versucht haben, dieselben Sitzplätze zu reservieren.</p>
|
<p class="text-center">Leider konnten Ihre Sitzplätze nicht reserviert werden. Dies kann passieren, wenn andere Nutzer gleichzeitig versucht haben, dieselben Sitzplätze zu reservieren.</p>
|
||||||
|
|
||||||
<button mat-button matButton="filled" class="error-button mt-4 w-80">Erneut versuchen</button>
|
<button mat-button matButton="filled" class="error-button mt-4 w-80">Erneut versuchen</button>
|
||||||
<button routerLink="/schedule" mat-button matButton="outlined" color="accent" class="error-button w-80">Zurück zur Programmauswahl</button>
|
<button routerLink="/schedule" mat-button matButton="outlined" color="accent" class="error-button w-80">Zurück zur Programmauswahl</button>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export class SeatSelectionComponent {
|
|||||||
SelectedSeatsService = inject(SelectedSeatsService);
|
SelectedSeatsService = inject(SelectedSeatsService);
|
||||||
|
|
||||||
selectedSeatsByCategory = computed(() =>
|
selectedSeatsByCategory = computed(() =>
|
||||||
this.SelectedSeatsService.getSelectedSeatsByCategory(this.seatCategory().id).length
|
this.SelectedSeatsService.getSeatsByCategory(this.seatCategory().id).length
|
||||||
);
|
);
|
||||||
|
|
||||||
totalCategoryPrice = computed(() =>
|
totalCategoryPrice = computed(() =>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class SeatComponent{
|
|||||||
|
|
||||||
getSeatStateColor(): string {
|
getSeatStateColor(): string {
|
||||||
if (this.isSelectedAndAvaliable()) {
|
if (this.isSelectedAndAvaliable()) {
|
||||||
return this.seatService.getCommited()? '#00c951' : '#6366f1';
|
return this.seatService.committed()? '#00c951' : '#6366f1';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.seatService.getSeatIsSelectable()) {
|
if (!this.seatService.getSeatIsSelectable()) {
|
||||||
@@ -29,9 +29,9 @@ export class SeatComponent{
|
|||||||
|
|
||||||
switch (this.state()) {
|
switch (this.state()) {
|
||||||
case TheaterSeatState.RESERVED:
|
case TheaterSeatState.RESERVED:
|
||||||
return this.seatService.getDebug()? '#f7e8c3' : '#c0c0c0';
|
return this.seatService.debug()? '#f7e8c3' : '#c0c0c0';
|
||||||
case TheaterSeatState.BOOKED:
|
case TheaterSeatState.BOOKED:
|
||||||
return this.seatService.getDebug()? '#ffc9c9' : '#c0c0c0';
|
return this.seatService.debug()? '#ffc9c9' : '#c0c0c0';
|
||||||
default:
|
default:
|
||||||
case TheaterSeatState.AVAILABLE:
|
case TheaterSeatState.AVAILABLE:
|
||||||
return '#1B1B23';
|
return '#1B1B23';
|
||||||
|
|||||||
@@ -1,69 +1,61 @@
|
|||||||
import { Injectable, signal } from '@angular/core';
|
import { computed, Injectable, signal } from '@angular/core';
|
||||||
import {Sitzplatz} from '@infinimotion/model-frontend';
|
import {Sitzplatz} from '@infinimotion/model-frontend';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class SelectedSeatsService {
|
export class SelectedSeatsService {
|
||||||
private selectedSeatsSignal = signal<Sitzplatz[]>([]);
|
|
||||||
private seatIsSelectable: boolean = true;
|
|
||||||
private commited = false;
|
|
||||||
private debug = false;
|
|
||||||
|
|
||||||
get selectedSeats() {
|
private selectedSeatsSignal = signal<Sitzplatz[]>([]);
|
||||||
return this.selectedSeatsSignal;
|
private seatIsSelectableSignal = signal(true);
|
||||||
}
|
private committedSignal = signal(false);
|
||||||
|
private debugSignal = signal(false);
|
||||||
|
|
||||||
|
readonly selectedSeats = this.selectedSeatsSignal.asReadonly();
|
||||||
|
readonly seatIsSelectable = this.seatIsSelectableSignal.asReadonly();
|
||||||
|
readonly committed = this.committedSignal.asReadonly();
|
||||||
|
readonly debug = this.debugSignal.asReadonly();
|
||||||
|
|
||||||
|
readonly totalSeats = computed(() => this.selectedSeats().length);
|
||||||
|
readonly totalPrice = computed(() => this.selectedSeats().reduce((sum, seat) => sum + seat.row.category.price, 0));
|
||||||
|
|
||||||
pushSelectedSeat(selectedSeat: Sitzplatz): void {
|
pushSelectedSeat(selectedSeat: Sitzplatz): void {
|
||||||
this.selectedSeatsSignal.update(seats => [...seats, selectedSeat]);
|
this.selectedSeatsSignal.update(seats => [...seats, selectedSeat]);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeSelectedSeat(selectedSeat: Sitzplatz): void {
|
removeSelectedSeat(selectedSeat: Sitzplatz): void {
|
||||||
this.selectedSeatsSignal.update(seats =>
|
this.selectedSeatsSignal.update(seats => seats.filter(seat => seat.id !== selectedSeat.id));
|
||||||
seats.filter(seat => seat.id !== selectedSeat.id)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedSeatsList(): Sitzplatz[] {
|
getSeatsByCategory(categoryId: number): Sitzplatz[] {
|
||||||
return this.selectedSeatsSignal();
|
return this.selectedSeats().filter(seat => seat.row.category.id === categoryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedSeatsByCategory(categoryId: number): Sitzplatz[] {
|
clearSelection(): void {
|
||||||
return this.selectedSeatsSignal().filter(seat => seat.row.category.id === categoryId);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearSelectedSeatsList(): void {
|
|
||||||
this.selectedSeatsSignal.set([]);
|
this.selectedSeatsSignal.set([]);
|
||||||
this.commited = false;
|
this.committedSignal.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSeatIsSelectable(): boolean{
|
getSeatIsSelectable(): boolean{
|
||||||
return this.seatIsSelectable;
|
return this.seatIsSelectable();
|
||||||
}
|
}
|
||||||
|
|
||||||
setSeatIsSelectableTrue(): void {
|
setSeatSelectable(selectable: boolean): void {
|
||||||
this.seatIsSelectable = true;
|
this.seatIsSelectableSignal.set(selectable);
|
||||||
this.commited = false;
|
if (selectable) {
|
||||||
|
this.committedSignal.set(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSeatIsSelectableFalse(): void {
|
commit(): void {
|
||||||
this.seatIsSelectable = false;
|
this.committedSignal.set(true);
|
||||||
}
|
|
||||||
|
|
||||||
getCommited(): boolean {
|
|
||||||
return this.commited;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCommitedTrue(): void {
|
|
||||||
this.commited = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDebug(): boolean {
|
|
||||||
return this.debug;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleDebug(): void {
|
toggleDebug(): void {
|
||||||
this.debug = !this.debug;
|
this.debugSignal.update(debug => !debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSeatSelected(seatId: number): boolean {
|
||||||
|
return this.selectedSeats().some(seat => seat.id === seatId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ export class TheaterOverlayComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.showId = Number(this.route.snapshot.paramMap.get('id')!);
|
this.showId = Number(this.route.snapshot.paramMap.get('id')!);
|
||||||
this.selectedSeatService.clearSelectedSeatsList();
|
this.selectedSeatService.clearSelection();
|
||||||
this.selectedSeatService.setSeatIsSelectableTrue();
|
this.selectedSeatService.setSeatSelectable(true);
|
||||||
this.loadPerformanceAndSeats();
|
this.loadPerformanceAndSeats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user