Files
frontend/src/app/theater-layout-designer/theater-layout-designer.component.ts
Piet Ostendorp 39c7003ddd Add success snackbar on theater save
Introduces a success snackbar notification when the theater layout is successfully saved in the designer component. Also adds a custom style for the success snackbar in the theme.
2025-11-28 11:04:34 +01:00

229 lines
6.7 KiB
TypeScript

import { Component, inject, OnInit } from '@angular/core';
import { Kinosaal, Sitzkategorie, Sitzplatz } from '@infinimotion/model-frontend';
import { TheaterSeatState } from '../model/theater-seat-state.model';
import { SelectedSeatsService } from '../selected-seats.service';
import { HttpService } from '../http.service';
import { firstValueFrom } from 'rxjs';
import { TheaterOverlayComponent } from '../theater-overlay/theater-overlay.component';
import { ActivatedRoute, Router } from '@angular/router';
import { LoadingService } from '../loading.service';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'app-theater-layout-designer',
standalone: false,
templateUrl: './theater-layout-designer.component.html',
styleUrl: './theater-layout-designer.component.css',
})
export class TheaterLayoutDesignerComponent implements OnInit {
public static seatsPerRow: { seat: Sitzplatz | null; state: TheaterSeatState | null }[][] = [];
addRow: { seat: Sitzplatz | null; state: TheaterSeatState | null }[] = [];
protected selectedSeatsService = inject(SelectedSeatsService);
protected http = inject(HttpService);
protected route = inject(ActivatedRoute);
protected loading = inject(LoadingService);
protected router = inject(Router);
constructor(private snackBar: MatSnackBar) {}
getHallId() {
return parseInt(this.route.snapshot.paramMap.get('hallId') ?? '-1');
}
async ngOnInit() {
let hallId = this.getHallId();
if (hallId == -1) {
return;
}
let seats = await firstValueFrom(this.http.getSeatsByHallId(hallId));
let rows: { seat: Sitzplatz | null; state: TheaterSeatState | null }[][] = [];
const categoryMap = new Map<number, Sitzkategorie>();
seats.forEach((seat) => {
if (!rows[seat.row.position]) {
rows[seat.row.position] = [];
}
rows[seat.row.position].push({
seat: seat,
state: TheaterSeatState.RESERVED,
});
if (seat.row.category && !categoryMap.has(seat.row.category.id)) {
categoryMap.set(seat.row.category.id, seat.row.category);
}
});
TheaterLayoutDesignerComponent.seatsPerRow = TheaterOverlayComponent.fillSeatsPerRow(rows);
let rowCounter = 0;
TheaterLayoutDesignerComponent.seatsPerRow.forEach(
(row) => (rowCounter = TheaterLayoutDesignerComponent.fillRowWithSettings(rowCounter, row))
);
let realCategories = await firstValueFrom(this.http.getSeatCategories());
this.addRow = realCategories.map((category) => {
return {
seat: {
id: -2,
position: 1,
row: {
id: 0,
hall: { id: hallId } as Kinosaal,
category: category,
position: -1,
},
},
state: TheaterSeatState.AVAILABLE,
};
});
this.addRow.splice(0, 0, {
seat: {
id: -2,
position: 1,
row: {
id: -1,
hall: { id: hallId } as Kinosaal,
category: { id: -1, name: '', price: -1, icon: 'check_box_outline_blank' },
position: -1,
},
},
state: TheaterSeatState.AVAILABLE,
});
}
seatsPerRow() {
return TheaterLayoutDesignerComponent.seatsPerRow;
}
public static fillRowWithSettings(
rowCounter: number,
row: {
seat: Sitzplatz | null;
state: TheaterSeatState | null;
}[]
) {
if (!row[0].seat) {
return rowCounter + 1;
}
let categories: Sitzkategorie[] = [
{ id: 0, name: '', price: -1, icon: 'check_box_outline_blank' },
{ id: 1, name: '', price: -1, icon: 'add' },
];
categories.forEach((category) => {
row.push({
seat: {
id: -1,
position: row.length,
row: {
id: -1,
hall: undefined as unknown as Kinosaal,
position: rowCounter,
category: category,
},
},
state: TheaterSeatState.AVAILABLE,
});
});
return rowCounter + 1;
}
static addSeatDesigner(selectedSeat: Sitzplatz): void {
let row = TheaterLayoutDesignerComponent.seatsPerRow[selectedSeat.row.position];
if (selectedSeat.row.category.id == 0) {
row.splice(row.length - 2, 0, {
seat: null,
state: null,
});
} else {
row.splice(row.length - 2, 0, {
seat: { id: 0, position: row.length - 1, row: row[0].seat!.row },
state: TheaterSeatState.RESERVED,
});
}
}
static addRowDesigner(selectedSeat: Sitzplatz): void {
let rows = TheaterLayoutDesignerComponent.seatsPerRow;
let firstSeat = {
seat: { id: 0, position: 1, row: { ...selectedSeat.row } },
state: TheaterSeatState.RESERVED,
};
firstSeat.seat!.row.position = rows.length + 1;
if (selectedSeat.row.id == -1) {
rows.push([{ seat: null, state: null }]);
} else {
rows.push([firstSeat]);
}
TheaterLayoutDesignerComponent.fillRowWithSettings(rows.length - 1, rows[rows.length - 1]);
}
public static interceptSeatSelection(selectedSeat: Sitzplatz) {
if (selectedSeat.id == -1) {
this.addSeatDesigner(selectedSeat);
} else if (selectedSeat.id == -2) {
this.addRowDesigner(selectedSeat);
} else {
console.log('Fehler: unerwartete Seat-ID: ' + selectedSeat.id);
}
}
async save() {
this.loading.show();
try {
for (let row of TheaterLayoutDesignerComponent.seatsPerRow) {
let seatRow;
if (!row[0].seat) {
continue;
} else if (row[0].seat.row.id === 0) {
seatRow = await firstValueFrom(
this.http.createSeatRow(row[0].seat.row)
);
} else {
seatRow = row[0].seat.row;
}
for (const { seat } of row) {
if (seat && seat.id === 0) {
seat.row = seatRow;
const createdSeat = await firstValueFrom(this.http.createSeat(seat));
seat.id = createdSeat.id;
}
}
}
this.snackBar.open(
'Kinosaal wurde erfolgreich aktualisiert.',
'Schließen',
{
duration: 5000,
panelClass: ['success-snackbar'],
horizontalPosition: 'right',
verticalPosition: 'top'
}
);
this.loading.hide();
} catch (err) {
console.error('Fehler beim Speichern des Kinosaals', err);
this.loading.showError(err);
}
}
hallName: string = '';
async navigate() {
let halls = await firstValueFrom(this.http.getAllKinosaal());
let hall = halls.filter((hall) => hall.name == this.hallName);
if (hall.length == 0) {
hall[0] = await firstValueFrom(this.http.addKinosaal({ name: this.hallName }));
}
this.router.navigate(['/admin/designer', hall[0].id]);
}
}