import { Component, inject, OnInit, DestroyRef, ViewChild } from '@angular/core'; import { AbstractControl, FormBuilder, FormGroup, FormGroupDirective, ValidationErrors, Validators } from '@angular/forms'; import { MAT_DATE_LOCALE } from '@angular/material/core'; import { provideNativeDateAdapter } from '@angular/material/core'; import { HttpService } from '../http.service'; import { LoadingService } from '../loading.service'; import { catchError, forkJoin, map, Observable, of, startWith } from 'rxjs'; import { Film, Kinosaal, Plan, Sitzplatz, Vorstellung } from '@infinimotion/model-frontend'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatSnackBar } from '@angular/material/snack-bar'; @Component({ selector: 'app-performance-scheduling', standalone: false, templateUrl: './performance-scheduling.component.html', styleUrl: './performance-scheduling.component.css', providers: [ provideNativeDateAdapter(), { provide: MAT_DATE_LOCALE, useValue: 'de-DE' } ] }) export class PerformanceSchedulingComponent implements OnInit { private fb = inject(FormBuilder); private http = inject(HttpService); private destroyRef = inject(DestroyRef); public loading = inject(LoadingService); constructor(private snackBar: MatSnackBar) {} @ViewChild('formDirectivePerformance') formDirectivePerformance!: FormGroupDirective; @ViewChild('formDirectiveSeries') formDirectiveSeries!: FormGroupDirective; minDate = new Date(); movies!: Film[]; filteredMoviesPerformance!: Observable; filteredMoviesPlan!: Observable; halls: Kinosaal[] = []; hallSeatMap = new Map(); weekdays = [ { label: 'Montag', value: 1 }, { label: 'Dienstag', value: 2 }, { label: 'Mittwoch', value: 3 }, { label: 'Donnerstag', value: 4 }, { label: 'Freitag', value: 5 }, { label: 'Samstag', value: 6 }, { label: 'Sonntag', value: 7 } ]; weekdayFilter = (date: Date | null): boolean => { if (!date) return false; const selectedWeekday = this.seriesForm.get('weekday')?.value; if (!selectedWeekday) return true; const weekday = date.getDay() === 0 ? 7 : date.getDay(); return weekday === selectedWeekday; }; performanceForm!: FormGroup; seriesForm!: FormGroup; ngOnInit() { this.performanceForm = this.fb.group({ movie: ['', [Validators.required, Validators.minLength(3)]], hall: ['', Validators.required], date: [null, Validators.required], time: ['', Validators.required] }, { validators: this.dateTimeInFutureValidator() }); this.seriesForm = this.fb.group({ movie: ['', [Validators.required, Validators.minLength(3)]], hall: ['', Validators.required], weekday: ['', Validators.required], first: [null, Validators.required], time: ['', Validators.required] }, { validators: this.dateTimeInFutureValidator() }); // Wochentagüberprüfung const weekdayControl = this.seriesForm.get('weekday'); const dateControl = this.seriesForm.get('first'); weekdayControl?.valueChanges.subscribe(() => { const currentDate = dateControl?.value; if (currentDate && !this.weekdayFilter(currentDate)) { dateControl?.setValue(null); } dateControl?.updateValueAndValidity(); }); dateControl?.valueChanges.subscribe((date: Date | null) => { if (!date) return; const weekday = date.getDay() === 0 ? 7 : date.getDay(); weekdayControl?.setValue(weekday, { emitEvent: false }); }); this.loadData(); } dateTimeInFutureValidator() { return (formGroup: AbstractControl): ValidationErrors | null => { const dateControl = formGroup.get('date'); const timeControl = formGroup.get('time'); if (!dateControl?.value || !timeControl?.value) { return null; } const selectedDateTime = this.combineDateAndTime(dateControl.value, timeControl.value); const now = new Date(); if (selectedDateTime <= now) { return { dateTimeInPast: true }; } return null; }; } hasDateTimeError(formGroup: AbstractControl): boolean { return formGroup.hasError('dateTimeInPast') && formGroup.get('date')?.touched === true && formGroup.get('time')?.touched === true; } hallName(formGroup: AbstractControl): string | undefined { const hall: Kinosaal | null = formGroup.get('hall')?.value ?? null; return hall?.name; } displayMovie(movie: Film | null): string { return movie ? movie.title : ''; } loadData() { const moviesRequest = this.http.getMovies(); const seatsRequest = this.http.getAllSeats(); this.loading.show(); this.hallSeatMap.clear(); this.halls = []; forkJoin([moviesRequest, seatsRequest]).subscribe({ next: ([movies, seats]) => { this.movies = movies.sort((a, b) => a.title.localeCompare(b.title)); seats.forEach((seat) => { const hallId = seat.row.hall.id; if (!this.hallSeatMap.has(hallId)) { this.halls.push(seat.row.hall); this.hallSeatMap.set(hallId, []); } this.hallSeatMap.get(hallId)?.push(seat); }); this.filteredMoviesPerformance = this.performanceForm.get('movie')!.valueChanges.pipe( startWith(''), map(value => this._filterMovies(value)) ); this.filteredMoviesPlan = this.seriesForm.get('movie')!.valueChanges.pipe( startWith(''), map(value => this._filterMovies(value)) ); this.loading.hide() }, error: (err) => { console.error('Fehler beim Laden der Filme/Kinosäle', err); this.loading.showError(err); }, }); } private _filterMovies(value: string | Film): Film[] { const filterValue = typeof value === 'string' ? value.toLowerCase() : value?.title.toLowerCase() ?? ''; return this.movies.filter(movie => movie.title.toLowerCase().includes(filterValue)); } onPerformanceSubmit() { if (!this.performanceForm.valid) { return; } this.loading.show(); const formValue = this.performanceForm.value; const datetime = this.combineDateAndTime(formValue.date, formValue.time); const performance = this.generateNewPerformanceObject(datetime, formValue.hall, formValue.movie) this.http.createPerformance(performance).pipe( map(performance => { this.loading.hide(); this.formDirectivePerformance.resetForm(); this.snackBar.open( `Vorstellung wurde erfolgreich unter der ID ${performance.id} angelegt.`, 'Schließen', { duration: 5000, panelClass: ['success-snackbar'], horizontalPosition: 'right', verticalPosition: 'top' } ); }), catchError(err => { this.loading.showError(err); this.performanceForm.setErrors({ serverError: true }); console.log('Fehler beim Anlegen der Vorstellung', err); return of(null); }), takeUntilDestroyed(this.destroyRef) ).subscribe(); } private generateNewPerformanceObject(start: Date, hall: Kinosaal, movie: Film): Vorstellung { return { id: 0, // Wird durch das Backend gesetzt start: start, hall: hall, movie: movie }; } private combineDateAndTime(date: Date, time: string): Date { const [hours, minutes] = time.split(':').map(Number); const combined = new Date(date); combined.setHours(hours, minutes, 0, 0); return combined; } onPlanSubmit() { if (!this.seriesForm.valid) { return; } this.loading.show(); const formValue = this.seriesForm.value; const series = this.generateNewSeriesObject(formValue.weekday, formValue.time, formValue.first, formValue.hall, formValue.movie) this.http.createPerformanceSeries(series).pipe( map(performance => { this.loading.hide(); this.formDirectiveSeries.resetForm(); this.seriesForm.reset({ movie: '', hall: '', weekday: '', first: null, time: '' }); this.snackBar.open( `Vorstellungsplan wurde erfolgreich unter der ID ${performance.id} angelegt.`, 'Schließen', { duration: 5000, panelClass: ['success-snackbar'], horizontalPosition: 'right', verticalPosition: 'top' } ); }), catchError(err => { this.loading.showError(err); this.seriesForm.setErrors({ serverError: true }); console.log('Fehler beim Anlegen des Plans', err); return of(null); }), takeUntilDestroyed(this.destroyRef) ).subscribe(); } private generateNewSeriesObject(weekday: number, time: Date, first: Date, hall: Kinosaal, movie: Film): Plan { return { id: 0, // Wird durch das Backend gesetzt weekday: weekday, time: time, first: first, hall: hall, movie: movie }; } }