diff --git a/src/app/app-module.ts b/src/app/app-module.ts
index 2794819..a8554ec 100644
--- a/src/app/app-module.ts
+++ b/src/app/app-module.ts
@@ -54,7 +54,6 @@ import { MovieImportNoSearchResultComponent } from './movie-import-no-search-res
import { MovieImportSearchInfoComponent } from './movie-import-search-info/movie-import-search-info.component';
import { LoginDialog } from './login/login.dialog';
import { PerformanceInfoComponent } from './performance-info/performance-info.component';
-import { ShoppingCartComponent } from './shopping-cart/shopping-cart.component';
import { OrderComponent } from './order/order.component';
import { SeatSelectionComponent } from './seat-selection/seat-selection.component';
import { NoSeatsInHallComponent } from './no-seats-in-hall/no-seats-in-hall.component';
@@ -65,6 +64,7 @@ import { PurchaseFailedComponent } from './purchase-failed/purchase-failed.compo
import { TicketSmallComponent } from './ticket-small/ticket-small.component';
import { TicketListComponent } from './ticket-list/ticket-list.component';
import { ZoomWarningComponent } from './zoom-warning/zoom-warning.component';
+import { SelectionConflictInfoComponent } from './selection-conflict-info/selection-conflict-info.component';
@NgModule({
@@ -98,7 +98,6 @@ import { ZoomWarningComponent } from './zoom-warning/zoom-warning.component';
MovieImportSearchInfoComponent,
LoginDialog,
PerformanceInfoComponent,
- ShoppingCartComponent,
OrderComponent,
SeatSelectionComponent,
NoSeatsInHallComponent,
@@ -109,6 +108,7 @@ import { ZoomWarningComponent } from './zoom-warning/zoom-warning.component';
TicketSmallComponent,
TicketListComponent,
ZoomWarningComponent,
+ SelectionConflictInfoComponent,
],
imports: [
AppRoutingModule,
diff --git a/src/app/order/order.component.html b/src/app/order/order.component.html
index 7b5b71d..5e8dffd 100644
--- a/src/app/order/order.component.html
+++ b/src/app/order/order.component.html
@@ -33,6 +33,10 @@
@for (seatCategory of seatCategories(); track $index) {
diff --git a/src/app/order/order.component.ts b/src/app/order/order.component.ts
index 04717de..4d2665d 100644
--- a/src/app/order/order.component.ts
+++ b/src/app/order/order.component.ts
@@ -1,11 +1,11 @@
import { SelectedSeatsService } from './../selected-seats.service';
import { LoadingService } from './../loading.service';
import { Bestellung, Eintrittskarte, Sitzkategorie, Sitzplatz, Vorstellung } from '@infinimotion/model-frontend';
-import { Component, computed, DestroyRef, EventEmitter, inject, input, output, Output, signal, ViewChild } from '@angular/core';
+import { Component, computed, DestroyRef, inject, input, output, signal } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { StepperSelectionEvent } from '@angular/cdk/stepper';
import { HttpService } from '../http.service';
-import { catchError, tap, finalize, switchMap, map, EMPTY, forkJoin } from 'rxjs';
+import { catchError, tap, finalize, EMPTY } from 'rxjs';
import { MatStepper } from '@angular/material/stepper';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
diff --git a/src/app/selected-seats.service.ts b/src/app/selected-seats.service.ts
index 18a84ff..2fe8456 100644
--- a/src/app/selected-seats.service.ts
+++ b/src/app/selected-seats.service.ts
@@ -1,5 +1,5 @@
import { computed, Injectable, signal } from '@angular/core';
-import {Sitzplatz} from '@infinimotion/model-frontend';
+import { Sitzplatz } from '@infinimotion/model-frontend';
@Injectable({
providedIn: 'root',
@@ -10,21 +10,25 @@ export class SelectedSeatsService {
private seatIsSelectableSignal = signal(true);
private committedSignal = signal(false);
private debugSignal = signal(false);
+ private hadConflictSignal = signal(false);
readonly selectedSeats = this.selectedSeatsSignal.asReadonly();
readonly seatIsSelectable = this.seatIsSelectableSignal.asReadonly();
readonly committed = this.committedSignal.asReadonly();
readonly debug = this.debugSignal.asReadonly();
+ readonly hadConflict = this.hadConflictSignal.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 {
this.selectedSeatsSignal.update(seats => [...seats, selectedSeat]);
+ this.setConflict(false);
}
removeSelectedSeat(selectedSeat: Sitzplatz): void {
this.selectedSeatsSignal.update(seats => seats.filter(seat => seat.id !== selectedSeat.id));
+ this.setConflict(false);
}
getSeatsByCategory(categoryId: number): Sitzplatz[] {
@@ -58,4 +62,8 @@ export class SelectedSeatsService {
isSeatSelected(seatId: number): boolean {
return this.selectedSeats().some(seat => seat.id === seatId);
}
+
+ setConflict(value: boolean): void {
+ this.hadConflictSignal.set(value);
+ }
}
diff --git a/src/app/shopping-cart/shopping-cart.component.css b/src/app/selection-conflict-info/selection-conflict-info.component.css
similarity index 100%
rename from src/app/shopping-cart/shopping-cart.component.css
rename to src/app/selection-conflict-info/selection-conflict-info.component.css
diff --git a/src/app/selection-conflict-info/selection-conflict-info.component.html b/src/app/selection-conflict-info/selection-conflict-info.component.html
new file mode 100644
index 0000000..d6f9b7e
--- /dev/null
+++ b/src/app/selection-conflict-info/selection-conflict-info.component.html
@@ -0,0 +1,16 @@
+
+
+
+
+ warning
+
+
+
+
+
+
Sitzplatz aus dem Warenkorb entfernt!
+
+
Leider ist der von Ihnen gewählte Sitzplazt nicht mehr verfügbar. Bitte wählen Sie einen anderen.
+
+
+
diff --git a/src/app/selection-conflict-info/selection-conflict-info.component.ts b/src/app/selection-conflict-info/selection-conflict-info.component.ts
new file mode 100644
index 0000000..57ed39c
--- /dev/null
+++ b/src/app/selection-conflict-info/selection-conflict-info.component.ts
@@ -0,0 +1,11 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-selection-conflict-info',
+ standalone: false,
+ templateUrl: './selection-conflict-info.component.html',
+ styleUrl: './selection-conflict-info.component.css',
+})
+export class SelectionConflictInfoComponent {
+
+}
diff --git a/src/app/shopping-cart/shopping-cart.component.html b/src/app/shopping-cart/shopping-cart.component.html
deleted file mode 100644
index 5aff33e..0000000
--- a/src/app/shopping-cart/shopping-cart.component.html
+++ /dev/null
@@ -1 +0,0 @@
-
shopping-cart works!
diff --git a/src/app/shopping-cart/shopping-cart.component.ts b/src/app/shopping-cart/shopping-cart.component.ts
deleted file mode 100644
index 382aba1..0000000
--- a/src/app/shopping-cart/shopping-cart.component.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
- selector: 'app-shopping-cart',
- standalone: false,
- templateUrl: './shopping-cart.component.html',
- styleUrl: './shopping-cart.component.css'
-})
-export class ShoppingCartComponent {
-
-}
diff --git a/src/app/theater-overlay/theater-overlay.component.ts b/src/app/theater-overlay/theater-overlay.component.ts
index 961fb45..8fc9a88 100644
--- a/src/app/theater-overlay/theater-overlay.component.ts
+++ b/src/app/theater-overlay/theater-overlay.component.ts
@@ -7,7 +7,6 @@ import { TheaterSeatState } from '../model/theater-seat-state.model';
import { ActivatedRoute } from '@angular/router';
import { SelectedSeatsService } from '../selected-seats.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
-import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar, MatSnackBarRef, TextOnlySnackBar } from '@angular/material/snack-bar';
const POLLING_INTERVAL_MS = 5 * 1000;
@@ -24,14 +23,14 @@ export class TheaterOverlayComponent implements OnInit, OnDestroy {
private route = inject(ActivatedRoute);
private destroyRef = inject(DestroyRef);
private selectedSeatService = inject(SelectedSeatsService);
- private dialog = inject(MatDialog);
readonly loading = inject(LoadingService);
showId!: number;
orderId?: string;
- seatsPerRow = signal<{ seat: Sitzplatz | null, state: TheaterSeatState | null }[][]>([]);
performance: Vorstellung | undefined;
+ blockedSeats: Sitzplatz[] | undefined;
+ seatsPerRow = signal<{ seat: Sitzplatz | null, state: TheaterSeatState | null }[][]>([]);
seatCategories: Sitzkategorie[] = [];
snackBarRef: MatSnackBarRef
| undefined
@@ -169,6 +168,21 @@ export class TheaterOverlayComponent implements OnInit, OnDestroy {
}).pipe(
tap(({ performance, seats }) => {
this.performance = performance;
+
+ if (this.blockedSeats && !this.equalSeats(this.blockedSeats, seats.reserved)) {
+ console.info('[TheaterOverlay] External booking detected. Checking for conflicts.');
+
+ const conflicts = this.getConflictingSeats(seats.reserved);
+ if (conflicts.length > 0) {
+ console.info('[TheaterOverlay] Conflicts! Updating shopping cart.');
+ conflicts.forEach(seat => this.selectedSeatService.removeSelectedSeat(seat));
+ this.selectedSeatService.setConflict(true);
+ }
+
+ this.selectedSeatService.selectedSeats
+ }
+ this.blockedSeats = seats.reserved;
+
this.seatsPerRow.set(this.converter(seats));
if (this.isInitialLoad()) {
@@ -197,6 +211,28 @@ export class TheaterOverlayComponent implements OnInit, OnDestroy {
);
}
+ private equalSeats(a: Sitzplatz[], b: Sitzplatz[]): boolean {
+ if (a === b) return true;
+ if (a == null || b == null) return false;
+ if (a.length !== b.length) return false;
+
+ // Arrays kopieren und sortieren
+ const sortedA = [...a].sort((a, b) => a.id - b.id);
+ const sortedB = [...b].sort((a, b) => a.id - b.id);
+
+ for (let i = 0; i < sortedA.length; ++i) {
+ if (sortedA[i].id !== sortedB[i].id) return false;
+ }
+ return true;
+ }
+
+ private getConflictingSeats(blockedSeats: Sitzplatz[]): Sitzplatz[] {
+ const blockedIds = new Set(blockedSeats.map(bs => bs.id));
+ return this.selectedSeatService.selectedSeats().filter(
+ selectedSeat => blockedIds.has(selectedSeat.id)
+ );
+ }
+
private converter(resp: { seats: Sitzplatz[], reserved: Sitzplatz[], booked: Sitzplatz[] }): {
seat: Sitzplatz | null,
state: TheaterSeatState | null