From 98626d11ed68e46978fbe46115b3c1eb01b1011e Mon Sep 17 00:00:00 2001 From: Piet Ostendorp Date: Thu, 30 Oct 2025 01:38:43 +0100 Subject: [PATCH] Make movie schedule components functional Introduces MovieGroup and Performance models for better type safety and data handling. Refactors movie-related components to use Angular signals (input/computed) and updates templates to bind data dynamically. Updates HttpService to support Vorstellung API endpoints. The schedule component now loads and groups performances by date and movie, passing structured data to child components for rendering. --- src/app/http.service.ts | 35 ++++++++- src/app/model/movie-group.model.ts | 7 ++ src/app/model/performance.model.ts | 6 ++ .../movie-category.component.html | 2 +- .../movie-category.component.ts | 8 +- .../movie-duration.component.html | 2 +- .../movie-duration.component.ts | 8 +- .../movie-performance.component.html | 6 +- .../movie-performance.component.ts | 37 ++++++++- .../movie-poster/movie-poster.component.html | 8 +- .../movie-poster/movie-poster.component.ts | 5 +- .../movie-rating/movie-rating.component.html | 4 +- .../movie-rating/movie-rating.component.ts | 29 +++---- .../movie-schedule-info.component.html | 12 +-- .../movie-schedule-info.component.ts | 11 ++- .../movie-schedule-times.component.html | 11 ++- .../movie-schedule-times.component.ts | 5 +- src/app/schedule/schedule.component.html | 10 +-- src/app/schedule/schedule.component.ts | 76 +++++++++++++++++-- 19 files changed, 213 insertions(+), 69 deletions(-) create mode 100644 src/app/model/movie-group.model.ts create mode 100644 src/app/model/performance.model.ts diff --git a/src/app/http.service.ts b/src/app/http.service.ts index fb35901..bdc03ae 100644 --- a/src/app/http.service.ts +++ b/src/app/http.service.ts @@ -1,4 +1,4 @@ -import { Kinosaal } from '@infinimotion/model-frontend'; +import { Kinosaal, Vorstellung } from '@infinimotion/model-frontend'; import { HttpClient } from "@angular/common/http"; import { inject, Injectable } from "@angular/core"; import { Observable } from "rxjs"; @@ -6,7 +6,10 @@ import { Observable } from "rxjs"; @Injectable({providedIn: 'root'}) export class HttpService { private http = inject(HttpClient); - private baseUrl = 'https://infinimotion.de/api/'; + private baseUrl = '/api/'; + + + /* Kinosaal APIs */ /* GET /api/kinosaal */ getAllKinosaal(): Observable { @@ -32,4 +35,32 @@ export class HttpService { deleteKinosaal(id: number): Observable { return this.http.delete(`${this.baseUrl}kinosaal/${id}`); } + + + /* Vorstellung APIs */ + + /* GET /api/vorstellung */ + getPerformaces(): Observable { + return this.http.get(`${this.baseUrl}vorstellung`); + } + + /* GET /api/vorstellung/{id} */ + getPerformaceById(id: number): Observable { + return this.http.get(`${this.baseUrl}vorstellung/${id}`); + } + + /* POST /api/vorstellung */ + addPerformace(vorstellung: Omit): Observable { + return this.http.post(`${this.baseUrl}vorstellung`, vorstellung); + } + + /* PUT /api/vorstellung/{id} */ + updatePerformace(id: number, vorstellung: Partial): Observable { + return this.http.put(`${this.baseUrl}vorstellung/${id}`, vorstellung); + } + + /* DELETE /api/vorstellung/{id} */ + deletePerformace(id: number): Observable { + return this.http.delete(`${this.baseUrl}vorstellung/${id}`); + } } diff --git a/src/app/model/movie-group.model.ts b/src/app/model/movie-group.model.ts new file mode 100644 index 0000000..5e39fe5 --- /dev/null +++ b/src/app/model/movie-group.model.ts @@ -0,0 +1,7 @@ +import { Film } from '@infinimotion/model-frontend'; +import { Performance } from './performance.model'; + +export interface MovieGroup { + movie: Film; + performances: Performance[]; +} diff --git a/src/app/model/performance.model.ts b/src/app/model/performance.model.ts new file mode 100644 index 0000000..f9b29d3 --- /dev/null +++ b/src/app/model/performance.model.ts @@ -0,0 +1,6 @@ +export class Performance { + id!: number; + hall!: string; + start!: Date; + utilisation?: number; +} diff --git a/src/app/movie-category/movie-category.component.html b/src/app/movie-category/movie-category.component.html index 16df8eb..72757f5 100644 --- a/src/app/movie-category/movie-category.component.html +++ b/src/app/movie-category/movie-category.component.html @@ -1,3 +1,3 @@ - {{ getCategoryText() }} + {{ category() }} diff --git a/src/app/movie-category/movie-category.component.ts b/src/app/movie-category/movie-category.component.ts index 4b07e4a..888b365 100644 --- a/src/app/movie-category/movie-category.component.ts +++ b/src/app/movie-category/movie-category.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; @Component({ selector: 'app-movie-category', @@ -7,9 +7,5 @@ import { Component, Input } from '@angular/core'; styleUrl: './movie-category.component.css' }) export class MovieCategoryComponent { - @Input() category: string = '-'; - - getCategoryText(): string { - return this.category; - } + category = input('-'); } diff --git a/src/app/movie-duration/movie-duration.component.html b/src/app/movie-duration/movie-duration.component.html index e41a4eb..73845a3 100644 --- a/src/app/movie-duration/movie-duration.component.html +++ b/src/app/movie-duration/movie-duration.component.html @@ -1,4 +1,4 @@ - {{ getDurationText() }} + {{ durationText() }} diff --git a/src/app/movie-duration/movie-duration.component.ts b/src/app/movie-duration/movie-duration.component.ts index e6a389f..4349449 100644 --- a/src/app/movie-duration/movie-duration.component.ts +++ b/src/app/movie-duration/movie-duration.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input, computed } from '@angular/core'; @Component({ selector: 'app-movie-duration', @@ -7,9 +7,7 @@ import { Component, Input } from '@angular/core'; styleUrl: './movie-duration.component.css' }) export class MovieDurationComponent { - @Input() duration: number = 0; + duration = input(0); - getDurationText(): string { - return `${this.duration} Min.`; - } + durationText = computed(() => `${this.duration()} Min.`); } diff --git a/src/app/movie-performance/movie-performance.component.html b/src/app/movie-performance/movie-performance.component.html index 82dcac3..033cbae 100644 --- a/src/app/movie-performance/movie-performance.component.html +++ b/src/app/movie-performance/movie-performance.component.html @@ -1,14 +1,14 @@
-

Kino 1

+

{{ hall() }}

- 15:30 + {{ startTime() }}

-
+

Tickets

diff --git a/src/app/movie-performance/movie-performance.component.ts b/src/app/movie-performance/movie-performance.component.ts index 296204f..ff7ba26 100644 --- a/src/app/movie-performance/movie-performance.component.ts +++ b/src/app/movie-performance/movie-performance.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, input, computed } from '@angular/core'; @Component({ selector: 'app-movie-performance', @@ -7,5 +7,40 @@ import { Component } from '@angular/core'; styleUrl: './movie-performance.component.css' }) export class MoviePerformanceComponent { + id = input.required(); + hall = input.required(); + start = input.required(); + utilisation = input(); + + startTime = computed(() => + this.start().toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }) + ); + + utilisationBackground = computed(() => { + const u = this.utilisation() ?? -1; + + const color_map = new Map([ + [100, '#da1414'], + [75, '#e76800'], + [50, '#efbe12'], + [25, '#8bbb00'], + [0, '#10a20b'] + ]); + + for (const [threshold, color] of color_map) { + if (u >= threshold) { + return color; + } + } + + return '#424242'; + }); + + fullStyle = computed(() => { + if (this.utilisation() === 100) { + return 'line-through'; + } + return ''; + }); } diff --git a/src/app/movie-poster/movie-poster.component.html b/src/app/movie-poster/movie-poster.component.html index 494579e..f7e3ea2 100644 --- a/src/app/movie-poster/movie-poster.component.html +++ b/src/app/movie-poster/movie-poster.component.html @@ -1,8 +1,8 @@
- Movie Poster + Movie Poster
- - - + + +
diff --git a/src/app/movie-poster/movie-poster.component.ts b/src/app/movie-poster/movie-poster.component.ts index 3e23ff5..33b1ccf 100644 --- a/src/app/movie-poster/movie-poster.component.ts +++ b/src/app/movie-poster/movie-poster.component.ts @@ -1,4 +1,5 @@ -import { Component } from '@angular/core'; +import { Component, input } from '@angular/core'; +import { Film } from '@infinimotion/model-frontend'; @Component({ selector: 'app-movie-poster', @@ -7,5 +8,5 @@ import { Component } from '@angular/core'; styleUrl: './movie-poster.component.css' }) export class MoviePosterComponent { - + movie = input.required(); } diff --git a/src/app/movie-rating/movie-rating.component.html b/src/app/movie-rating/movie-rating.component.html index fd273c8..a8e4c76 100644 --- a/src/app/movie-rating/movie-rating.component.html +++ b/src/app/movie-rating/movie-rating.component.html @@ -1,3 +1,3 @@ - - {{ getRatingText() }} + + {{ ratingText() }} diff --git a/src/app/movie-rating/movie-rating.component.ts b/src/app/movie-rating/movie-rating.component.ts index 1e87bc1..0643e68 100644 --- a/src/app/movie-rating/movie-rating.component.ts +++ b/src/app/movie-rating/movie-rating.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input, computed } from '@angular/core'; @Component({ selector: 'app-movie-rating', @@ -7,23 +7,16 @@ import { Component, Input } from '@angular/core'; styleUrl: './movie-rating.component.css' }) export class MovieRatingComponent { - @Input() rating: number = 0; + rating = input(0); - getRatingColor(): string { - if (this.rating >= 18) { - return 'bg-red-500'; - } else if (this.rating >= 16) { - return 'bg-blue-500'; - } else if (this.rating >= 12) { - return 'bg-green-500'; - } else if (this.rating >= 6) { - return 'bg-yellow-300'; - } else { - return 'bg-white'; - } - } + ratingColor = computed(() => { + const r = this.rating(); + if (r >= 18) return 'bg-red-500'; + if (r >= 16) return 'bg-blue-500'; + if (r >= 12) return 'bg-green-500'; + if (r >= 6) return 'bg-yellow-300'; + return 'bg-white'; + }); - getRatingText(): string { - return `FSK ${this.rating}`; - } + ratingText = computed(() => `FSK ${this.rating()}`); } diff --git a/src/app/movie-schedule-info/movie-schedule-info.component.html b/src/app/movie-schedule-info/movie-schedule-info.component.html index d8ac521..fd18684 100644 --- a/src/app/movie-schedule-info/movie-schedule-info.component.html +++ b/src/app/movie-schedule-info/movie-schedule-info.component.html @@ -1,13 +1,13 @@ -
- +
+
-

Avengers: Endgame

-

- Long Movie description Long Movie description Long Movie description Long Movie description Long Movie description Long Movie descriptionLong Movie description Long Movie description Long Movie description +

{{ movie.title }}

+

+ {{ movie.description }}

- +
diff --git a/src/app/movie-schedule-info/movie-schedule-info.component.ts b/src/app/movie-schedule-info/movie-schedule-info.component.ts index 79b208b..71804a9 100644 --- a/src/app/movie-schedule-info/movie-schedule-info.component.ts +++ b/src/app/movie-schedule-info/movie-schedule-info.component.ts @@ -1,4 +1,5 @@ -import { Component } from '@angular/core'; +import { Component, input } from '@angular/core'; +import { MovieGroup } from '../model/movie-group.model'; @Component({ selector: 'app-movie-schedule-info', @@ -7,5 +8,13 @@ import { Component } from '@angular/core'; styleUrl: './movie-schedule-info.component.css' }) export class MovieScheduleInfoComponent { + readonly movieGroup = input.required(); + get movie() { + return this.movieGroup().movie; + } + + get performances() { + return this.movieGroup().performances; + } } diff --git a/src/app/movie-schedule-times/movie-schedule-times.component.html b/src/app/movie-schedule-times/movie-schedule-times.component.html index b46a6bb..b7b9720 100644 --- a/src/app/movie-schedule-times/movie-schedule-times.component.html +++ b/src/app/movie-schedule-times/movie-schedule-times.component.html @@ -1,5 +1,10 @@
- - - + @for (perf of performances(); track $index) { + + + }
diff --git a/src/app/movie-schedule-times/movie-schedule-times.component.ts b/src/app/movie-schedule-times/movie-schedule-times.component.ts index f15fb30..700eba1 100644 --- a/src/app/movie-schedule-times/movie-schedule-times.component.ts +++ b/src/app/movie-schedule-times/movie-schedule-times.component.ts @@ -1,4 +1,5 @@ -import { Component } from '@angular/core'; +import { Performance } from './../model/performance.model'; +import { Component, input } from '@angular/core'; @Component({ selector: 'app-movie-schedule-times', @@ -7,5 +8,5 @@ import { Component } from '@angular/core'; styleUrl: './movie-schedule-times.component.css' }) export class MovieScheduleTimesComponent { - + performances = input.required(); } diff --git a/src/app/schedule/schedule.component.html b/src/app/schedule/schedule.component.html index 0dfee55..cfcd93a 100644 --- a/src/app/schedule/schedule.component.html +++ b/src/app/schedule/schedule.component.html @@ -1,12 +1,12 @@ - @for (dateInfo of dates; track dateInfo.date; let i = $index) { + @for (dateInfo of dates; track dateInfo.date; let i = $index) { - @if (getMovieCount(i)> 0) { - @for (movie of [].constructor(getMovieCount(i)); track movie) { - + @if (getMovieCount(i) > 0) { + @for (group of dateInfo.performances; track group.movie.id) { + } } @else { - + } } diff --git a/src/app/schedule/schedule.component.ts b/src/app/schedule/schedule.component.ts index a69c67a..ec6b934 100644 --- a/src/app/schedule/schedule.component.ts +++ b/src/app/schedule/schedule.component.ts @@ -1,4 +1,8 @@ -import { Component } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; +import { HttpService } from '../http.service'; +import { Vorstellung } from '@infinimotion/model-frontend'; +import { Performance } from '../model/performance.model'; +import { MovieGroup } from '../model/movie-group.model'; @Component({ selector: 'app-schedule', @@ -6,16 +10,23 @@ import { Component } from '@angular/core'; templateUrl: './schedule.component.html', styleUrl: './schedule.component.css' }) -export class ScheduleComponent { -dates: { label: string; date: Date }[] = []; +export class ScheduleComponent implements OnInit { + dates: { label: string; date: Date; performances: MovieGroup[] }[] = []; + performaces: Vorstellung[] = []; + + private http = inject(HttpService); constructor() { this.generateDates(); } + ngOnInit() { + this.loadPerformances(); + } + generateDates() { const today = new Date(); - for (let i = 0; i < 31; i++) { + for (let i = 0; i < 14; i++) { const date = new Date(today); date.setDate(today.getDate() + i); @@ -28,12 +39,63 @@ dates: { label: string; date: Date }[] = []; label = date.toLocaleDateString('de-DE', { weekday: 'short' }) + '. ' + date.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit'}); } - this.dates.push({ label, date }); + this.dates.push({ label, date, performances: []}); + } + } + + loadPerformances() { + this.http.getPerformaces().subscribe({ + next: (data) => { + this.performaces = Array.isArray(data) ? data : [data]; + this.assignPerformancesToDates(); + }, + error: (err) => console.error('Fehler beim Laden der Performances', err), + }); + } + + assignPerformancesToDates() { + + // Gruppieren nach Datum + const groupedByDate: { [key: string]: Vorstellung[] } = {}; + for (const vorstellung of this.performaces) { + const dateKey = new Date(vorstellung.start).toDateString(); + if (!groupedByDate[dateKey]) { + groupedByDate[dateKey] = []; + } + groupedByDate[dateKey].push(vorstellung); + } + + // Gruppieren nach Film + for (const dateInfo of this.dates) { + const dateKey = dateInfo.date.toDateString(); + const dailyPerformances: Vorstellung[] = groupedByDate[dateKey] || []; + + const movieMap = new Map(); + + for (const perf of dailyPerformances) { + const movieId = perf.movie.id; + if (!movieMap.has(movieId)) { + movieMap.set(movieId, { movie: perf.movie, performances: [] }); + } + + const performance: Performance = { + id: perf.id, + hall: perf.hall.name, + start: new Date(perf.start), + // utilisation: 0 // TODO: perf.utilisation einrichten + }; + movieMap.get(movieId)!.performances.push(performance); + } + + // MovieGroups erstellen + dateInfo.performances = Array.from(movieMap.values()).map((entry) => ({ + movie: entry.movie, + performances: entry.performances.sort((a, b) => a.start.getTime() - b.start.getTime()), + })) as MovieGroup[]; } } getMovieCount(index: number): number { - // Hier kannst du später die echten Filmzahlen zurückgeben - return index === 0 ? 10 : index === 1 ? 0 : 4; + return this.dates[index].performances.length; } }