From 4f5a8e966120ad84394842d575aacb238e07c7b7 Mon Sep 17 00:00:00 2001 From: Piet Ostendorp Date: Fri, 7 Nov 2025 01:57:42 +0100 Subject: [PATCH] Add IMDb movie importer feature and unify header Introduces a new movie importer feature allowing admins to search and import movies from IMDb, including new components for search, result display, and error handling. Replaces the schedule header with a reusable menu header component. Updates routing, navigation, and HTTP service to support the new importer. Adds a poster placeholder image and improves poster error handling. --- package-lock.json | 36 ++++++++-- package.json | 2 +- src/app/app-module.ts | 17 +++-- src/app/app-routing-module.ts | 2 + src/app/http.service.ts | 26 ++++++- .../menu-header.component.css} | 0 .../menu-header/menu-header.component.html | 14 ++++ src/app/menu-header/menu-header.component.ts | 15 ++++ ...ovie-import-no-search-result.component.css | 0 ...vie-import-no-search-result.component.html | 5 ++ ...movie-import-no-search-result.component.ts | 11 +++ .../movie-import-search-info.component.css | 4 ++ .../movie-import-search-info.component.html | 22 ++++++ .../movie-import-search-info.component.ts | 68 ++++++++++++++++++ .../movie-importer.component.css | 3 + .../movie-importer.component.html | 52 ++++++++++++++ .../movie-importer.component.ts | 58 +++++++++++++++ .../movie-poster/movie-poster.component.html | 7 +- .../movie-poster/movie-poster.component.ts | 9 +++ src/app/navbar/navbar.component.ts | 1 + .../schedule-header.component.html | 10 --- .../schedule-header.component.ts | 11 --- src/app/schedule/schedule.component.html | 2 +- src/app/schedule/schedule.component.ts | 5 +- src/assets/poster_placeholder.png | Bin 0 -> 7573 bytes 25 files changed, 338 insertions(+), 42 deletions(-) rename src/app/{schedule-header/schedule-header.component.css => menu-header/menu-header.component.css} (100%) create mode 100644 src/app/menu-header/menu-header.component.html create mode 100644 src/app/menu-header/menu-header.component.ts create mode 100644 src/app/movie-import-no-search-result/movie-import-no-search-result.component.css create mode 100644 src/app/movie-import-no-search-result/movie-import-no-search-result.component.html create mode 100644 src/app/movie-import-no-search-result/movie-import-no-search-result.component.ts create mode 100644 src/app/movie-import-search-info/movie-import-search-info.component.css create mode 100644 src/app/movie-import-search-info/movie-import-search-info.component.html create mode 100644 src/app/movie-import-search-info/movie-import-search-info.component.ts create mode 100644 src/app/movie-importer/movie-importer.component.css create mode 100644 src/app/movie-importer/movie-importer.component.html create mode 100644 src/app/movie-importer/movie-importer.component.ts delete mode 100644 src/app/schedule-header/schedule-header.component.html delete mode 100644 src/app/schedule-header/schedule-header.component.ts create mode 100644 src/assets/poster_placeholder.png diff --git a/package-lock.json b/package-lock.json index 6eb2e17..ea5c339 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@angular/material": "^20.2.9", "@angular/platform-browser": "^20.3.0", "@angular/router": "^20.3.0", - "@infinimotion/model-frontend": "^0.0.84", + "@infinimotion/model-frontend": "^0.0.85", "@tailwindcss/postcss": "^4.1.14", "postcss": "^8.5.6", "rxjs": "~7.8.0", @@ -400,6 +400,7 @@ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.9.tgz", "integrity": "sha512-rbY1AMz9389WJI29iAjWp4o0QKRQHCrQQUuP0ctNQzh1tgWpwiRLx8N4yabdVdsCA846vPsyKJtBlSNwKMsjJA==", "license": "MIT", + "peer": true, "dependencies": { "parse5": "^8.0.0", "tslib": "^2.3.0" @@ -446,6 +447,7 @@ "node_modules/@angular/common": { "version": "20.3.5", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -460,6 +462,7 @@ "node_modules/@angular/compiler": { "version": "20.3.5", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -471,6 +474,7 @@ "version": "20.3.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "7.28.3", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -501,6 +505,7 @@ "node_modules/@angular/core": { "version": "20.3.5", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -524,6 +529,7 @@ "node_modules/@angular/forms": { "version": "20.3.5", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -557,6 +563,7 @@ "node_modules/@angular/platform-browser": { "version": "20.3.5", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -615,6 +622,7 @@ "version": "7.28.3", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -1290,9 +1298,9 @@ } }, "node_modules/@infinimotion/model-frontend": { - "version": "0.0.84", - "resolved": "https://git.infinimotion.de/api/packages/infinimotion/npm/%40infinimotion%2Fmodel-frontend/-/0.0.84/model-frontend-0.0.84.tgz", - "integrity": "sha512-+SMFsobPpfh6H9cU54DfVl9sF9Mp1vj6HuB135Y+grWvk/nIN4wzzZLvYPIk3BDURTT1DHgg8O3m66FaBB22sQ==", + "version": "0.0.85", + "resolved": "https://git.infinimotion.de/api/packages/infinimotion/npm/%40infinimotion%2Fmodel-frontend/-/0.0.85/model-frontend-0.0.85.tgz", + "integrity": "sha512-QPiZNl//Y1JdxtXk+VScc67h1K664z68PUCXRff9fRf4IHlYXtqutc+ainK8vxOVSqqL6EEmDAtbLsRwrG6kRg==", "license": "ISC" }, "node_modules/@inquirer/ansi": { @@ -1507,6 +1515,7 @@ "version": "7.8.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@inquirer/checkbox": "^4.2.1", "@inquirer/confirm": "^5.1.14", @@ -3593,6 +3602,7 @@ "version": "24.8.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.14.0" } @@ -3878,6 +3888,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -4766,6 +4777,7 @@ "version": "5.1.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", @@ -5571,7 +5583,8 @@ "node_modules/jasmine-core": { "version": "5.9.0", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/jiti": { "version": "2.6.1", @@ -5645,6 +5658,7 @@ "version": "6.4.4", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@colors/colors": "1.5.0", "body-parser": "^1.19.0", @@ -6270,6 +6284,7 @@ "version": "9.0.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", @@ -7728,6 +7743,7 @@ "node_modules/rxjs": { "version": "7.8.2", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -7776,6 +7792,7 @@ "version": "1.90.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -8432,7 +8449,8 @@ }, "node_modules/tslib": { "version": "2.8.1", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tuf-js": { "version": "3.1.0", @@ -8464,6 +8482,7 @@ "version": "5.9.3", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8622,6 +8641,7 @@ "version": "7.1.5", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -8971,6 +8991,7 @@ "version": "3.25.76", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -8985,7 +9006,8 @@ }, "node_modules/zone.js": { "version": "0.15.1", - "license": "MIT" + "license": "MIT", + "peer": true } } } diff --git a/package.json b/package.json index e456362..5027aa3 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@angular/material": "^20.2.9", "@angular/platform-browser": "^20.3.0", "@angular/router": "^20.3.0", - "@infinimotion/model-frontend": "^0.0.84", + "@infinimotion/model-frontend": "^0.0.85", "@tailwindcss/postcss": "^4.1.14", "postcss": "^8.5.6", "rxjs": "~7.8.0", diff --git a/src/app/app-module.ts b/src/app/app-module.ts index 970fd36..751011c 100644 --- a/src/app/app-module.ts +++ b/src/app/app-module.ts @@ -16,7 +16,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatInputModule } from '@angular/material/input'; import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconButton } from '@angular/material/button'; +import { MatButtonModule, MatIconButton } from '@angular/material/button'; import { MatDividerModule } from '@angular/material/divider'; import { HeaderComponent } from './header/header.component'; @@ -41,8 +41,11 @@ import { SeatComponent } from './seat/seat.component'; import { SeatRowComponent } from './seat-row/seat-row.component'; import { TheaterLayoutComponent } from './theater-layout/theater-layout.component'; import { MovieSearchComponent } from './movie-search/movie-search.component'; -import { ScheduleHeaderComponent } from './schedule-header/schedule-header.component'; +import { MenuHeaderComponent } from './menu-header/menu-header.component'; import { MovieScheduleNoSearchResultComponent } from './movie-schedule-no-search-result/movie-schedule-no-search-result.component'; +import { MovieImporterComponent } from './movie-importer/movie-importer.component'; +import { MovieImportNoSearchResultComponent } from './movie-import-no-search-result/movie-import-no-search-result.component'; +import { MovieImportSearchInfoComponent } from './movie-import-search-info/movie-import-search-info.component'; @NgModule({ @@ -69,8 +72,11 @@ import { MovieScheduleNoSearchResultComponent } from './movie-schedule-no-search SeatRowComponent, TheaterLayoutComponent, MovieSearchComponent, - ScheduleHeaderComponent, - MovieScheduleNoSearchResultComponent + MenuHeaderComponent, + MovieScheduleNoSearchResultComponent, + MovieImporterComponent, + MovieImportNoSearchResultComponent, + MovieImportSearchInfoComponent, ], imports: [ AppRoutingModule, @@ -87,7 +93,8 @@ import { MovieScheduleNoSearchResultComponent } from './movie-schedule-no-search MatInputModule, MatFormFieldModule, MatIconButton, - MatDividerModule + MatDividerModule, + MatButtonModule ], providers: [ provideBrowserGlobalErrorListeners(), diff --git a/src/app/app-routing-module.ts b/src/app/app-routing-module.ts index aa4cc67..0478461 100644 --- a/src/app/app-routing-module.ts +++ b/src/app/app-routing-module.ts @@ -6,6 +6,7 @@ import { MainLayoutComponent } from './layouts/main-layout/main-layout.component import { MainComponent } from './main/main.component'; import { ScheduleComponent } from './schedule/schedule.component'; import { TheaterOverlayComponent} from './theater-overlay/theater-overlay.component'; +import { MovieImporterComponent } from './movie-importer/movie-importer.component'; const routes: Routes = [ // Seiten ohne Layout @@ -19,6 +20,7 @@ const routes: Routes = [ children: [ { path: '', component: MainComponent }, { path: 'schedule', component: ScheduleComponent }, + { path: 'admin/movie-importer', component: MovieImporterComponent }, { path: 'theater-overlay', component: TheaterOverlayComponent}, ], }, diff --git a/src/app/http.service.ts b/src/app/http.service.ts index 2429917..5e7b342 100644 --- a/src/app/http.service.ts +++ b/src/app/http.service.ts @@ -1,4 +1,4 @@ -import { Kinosaal, Sitzplatz, Vorstellung, Film } from '@infinimotion/model-frontend'; +import { Kinosaal, Sitzplatz, Vorstellung, Film, OmdbSearch } from '@infinimotion/model-frontend'; import { HttpClient } from "@angular/common/http"; import { inject, Injectable } from "@angular/core"; import { Observable } from "rxjs"; @@ -52,7 +52,7 @@ export class HttpService { } /* POST /api/vorstellung/filter */ - getPerformaceByFilter(filter: string[]): Observable { + getPerformacesByFilter(filter: string[]): Observable { return this.http.post(`${this.baseUrl}vorstellung/filter`, filter); } @@ -79,11 +79,31 @@ export class HttpService { return this.http.get(`${this.baseUrl}film`); } + /* POST /api/vorstellung/filter */ + getMoviesByFilter(filter: string[]): Observable { + return this.http.post(`${this.baseUrl}film/filter`, filter); + } - /* Show-Seats APIs*/ + + /* Show-Seats APIs */ /* GET /api/show-seats/{show} */ getSeatsByShowId(show: number): Observable<{seats:Sitzplatz[], reserved:Sitzplatz[], booked:Sitzplatz[]}> { return this.http.get<{seats:Sitzplatz[], reserved:Sitzplatz[], booked:Sitzplatz[]}>(`${this.baseUrl}show-seats/${show}`); } + + + /* Movie Importer APIs */ + + /* GET /api/importer/search */ + searchMovie(query: string): Observable { + return this.http.get(`${this.baseUrl}importer/search`, { + params: { title: query } + }); + } + + /* POST /api/importer/import */ + importMovie(imdbId: string): Observable { + return this.http.post(`${this.baseUrl}importer/import?id=${imdbId}`, {}) + } } diff --git a/src/app/schedule-header/schedule-header.component.css b/src/app/menu-header/menu-header.component.css similarity index 100% rename from src/app/schedule-header/schedule-header.component.css rename to src/app/menu-header/menu-header.component.css diff --git a/src/app/menu-header/menu-header.component.html b/src/app/menu-header/menu-header.component.html new file mode 100644 index 0000000..1941a29 --- /dev/null +++ b/src/app/menu-header/menu-header.component.html @@ -0,0 +1,14 @@ +
+
+ @if ( icon() ) { + {{ icon() }} + } +

+ {{ title() }} +

+
+ @if ( searchBar() ) { + + } +
+ diff --git a/src/app/menu-header/menu-header.component.ts b/src/app/menu-header/menu-header.component.ts new file mode 100644 index 0000000..18155ff --- /dev/null +++ b/src/app/menu-header/menu-header.component.ts @@ -0,0 +1,15 @@ +import { Component, input, output } from '@angular/core'; + +@Component({ + selector: 'app-menu-header', + standalone: false, + templateUrl: './menu-header.component.html', + styleUrl: './menu-header.component.css' +}) +export class MenuHeaderComponent { + title = input.required(); + icon = input(); + + searchBar = input(false); + movieSearchResult = output(); +} diff --git a/src/app/movie-import-no-search-result/movie-import-no-search-result.component.css b/src/app/movie-import-no-search-result/movie-import-no-search-result.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/movie-import-no-search-result/movie-import-no-search-result.component.html b/src/app/movie-import-no-search-result/movie-import-no-search-result.component.html new file mode 100644 index 0000000..89dc33a --- /dev/null +++ b/src/app/movie-import-no-search-result/movie-import-no-search-result.component.html @@ -0,0 +1,5 @@ +
+ search_off +

Kein Film gefunden

+

Für '{{ search() }}' konnten über IMDb keine Filme gefunden werden.

+
diff --git a/src/app/movie-import-no-search-result/movie-import-no-search-result.component.ts b/src/app/movie-import-no-search-result/movie-import-no-search-result.component.ts new file mode 100644 index 0000000..ec1ab4b --- /dev/null +++ b/src/app/movie-import-no-search-result/movie-import-no-search-result.component.ts @@ -0,0 +1,11 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'app-movie-import-no-search-result', + standalone: false, + templateUrl: './movie-import-no-search-result.component.html', + styleUrl: './movie-import-no-search-result.component.css' +}) +export class MovieImportNoSearchResultComponent { + search = input.required(); +} diff --git a/src/app/movie-import-search-info/movie-import-search-info.component.css b/src/app/movie-import-search-info/movie-import-search-info.component.css new file mode 100644 index 0000000..0426c40 --- /dev/null +++ b/src/app/movie-import-search-info/movie-import-search-info.component.css @@ -0,0 +1,4 @@ +:host { + display: block; + margin: 60px 0; +} diff --git a/src/app/movie-import-search-info/movie-import-search-info.component.html b/src/app/movie-import-search-info/movie-import-search-info.component.html new file mode 100644 index 0000000..fa3bab7 --- /dev/null +++ b/src/app/movie-import-search-info/movie-import-search-info.component.html @@ -0,0 +1,22 @@ +
+
+ Movie Poster +
+
+

{{ movie().title }}

+

+ Erscheinungsjahr: {{ movie().year }} +

+ + + +
+
diff --git a/src/app/movie-import-search-info/movie-import-search-info.component.ts b/src/app/movie-import-search-info/movie-import-search-info.component.ts new file mode 100644 index 0000000..8763783 --- /dev/null +++ b/src/app/movie-import-search-info/movie-import-search-info.component.ts @@ -0,0 +1,68 @@ +import { Component, inject, input } from '@angular/core'; +import { Film, OmdbMovie } from '@infinimotion/model-frontend'; +import { HttpService } from '../http.service'; +import { LoadingService } from '../loading.service'; +import { catchError, EMPTY, of, switchMap, tap } from 'rxjs'; + +@Component({ + selector: 'app-movie-import-search-info', + standalone: false, + templateUrl: './movie-import-search-info.component.html', + styleUrl: './movie-import-search-info.component.css' +}) +export class MovieImportSearchInfoComponent { + readonly movie = input.required(); + + importedMovie: Film | undefined; + buttonDisabled = false; + buttonText: string = "Film von IMDb importieren"; + buttonIcon: string = "cloud_download" + + private http = inject(HttpService) + private loading = inject(LoadingService) + + importMovie(imdbId: string, title: string) { + this.buttonDisabled = true; + this.loading.show(); + + const filter = this.generateTitleFilter(title); + + this.http.getMoviesByFilter(filter).pipe( + switchMap(movies => { + if (movies.length !== 0) { + this.loading.showError(`Dublette erkannt! Film '${title}' existiert bereits.`); + console.warn('Dublette erkannt. Import abgebrochen.'); + this.buttonText = 'Film existiert bereits' + this.buttonIcon = 'file_present'; + return EMPTY; + } + return this.http.importMovie(imdbId); + }), + tap(importedMovie => { + this.importedMovie = importedMovie; + this.buttonText = `Film erfolgreich importiert (Id: ${importedMovie.id})`; + this.buttonIcon = 'cloud_done'; + this.loading.hide(); + }), + catchError(err => { + this.buttonDisabled = false; + this.loading.showError(err); + console.error('Fehler beim Import oder bei der Dublettenprüfung', err); + return of([]); + }) + ).subscribe(); + } + + private generateTitleFilter(title: string): string[] { + return [`eq;title;string;${title}`] + } + + onPosterError(event: Event) { + const img = event.target as HTMLImageElement; + const placeholder = 'assets/poster_placeholder.png'; + + if (img.src !== window.location.origin + placeholder) { + img.src = placeholder; + } + } +} diff --git a/src/app/movie-importer/movie-importer.component.css b/src/app/movie-importer/movie-importer.component.css new file mode 100644 index 0000000..dd48b70 --- /dev/null +++ b/src/app/movie-importer/movie-importer.component.css @@ -0,0 +1,3 @@ +:host { + min-height: 100%; +} diff --git a/src/app/movie-importer/movie-importer.component.html b/src/app/movie-importer/movie-importer.component.html new file mode 100644 index 0000000..42b2ea2 --- /dev/null +++ b/src/app/movie-importer/movie-importer.component.html @@ -0,0 +1,52 @@ + + +
+
+
+ + Film online suchen + + @if (formControl.hasError('noResults')) { + Keine Suchergebnisse gefunden + } + + +
+
+ + @if (search_query.length > 0 && !isSearching) { +
+ + @if (movies.length > 0) { + + + + + @if (movies.length > 1 && !showAll) { +
+ {{ movies.length - 1 }} weitere Suchergebnisse anzeigen +
+ } + + + @if (showAll) { + @for (movie of movies.slice(1); track movie.imdbID) { + + } +
+ Weitere Suchergebnisse ausblenden +
+ } + } + + + @else { + + } + +
+ } +
+ diff --git a/src/app/movie-importer/movie-importer.component.ts b/src/app/movie-importer/movie-importer.component.ts new file mode 100644 index 0000000..c68289b --- /dev/null +++ b/src/app/movie-importer/movie-importer.component.ts @@ -0,0 +1,58 @@ +import { LoadingService } from './../loading.service'; +import { Component, inject } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { catchError, finalize, of, tap } from 'rxjs'; +import { HttpService } from '../http.service'; +import { OmdbMovie } from '@infinimotion/model-frontend'; + +@Component({ + selector: 'app-movie-importer', + standalone: false, + templateUrl: './movie-importer.component.html', + styleUrl: './movie-importer.component.css' +}) +export class MovieImporterComponent { + formControl = new FormControl('') + + movies: OmdbMovie[] = []; + search_query: string = ''; + showAll = false; + isSearching = false; + + private httpService = inject(HttpService) + public loadingService = inject(LoadingService) + + DoSubmit() { + this.showAll = false; + this.searchForMovies(); + } + + searchForMovies() { + this.search_query = this.formControl.value?.trim() || ''; + if (this.search_query?.length == 0) return; + + this.isSearching = true; + this.formControl.disable(); + this.loadingService.show(); + + this.httpService.searchMovie(this.search_query).pipe( + tap(movies => { + this.movies = movies.search || [] ; + this.loadingService.hide(); + }), + catchError(err => { + this.loadingService.showError(err); + console.error('Fehler bei der Suchen', err); + return of([]); + }), + finalize(() => { + this.isSearching = false; + this.formControl.enable(); + }) + ).subscribe(); + } + + toggleShowAll() { + this.showAll = !this.showAll; + } +} diff --git a/src/app/movie-poster/movie-poster.component.html b/src/app/movie-poster/movie-poster.component.html index f7e3ea2..1378eab 100644 --- a/src/app/movie-poster/movie-poster.component.html +++ b/src/app/movie-poster/movie-poster.component.html @@ -1,5 +1,10 @@
- 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 33b1ccf..b9900de 100644 --- a/src/app/movie-poster/movie-poster.component.ts +++ b/src/app/movie-poster/movie-poster.component.ts @@ -9,4 +9,13 @@ import { Film } from '@infinimotion/model-frontend'; }) export class MoviePosterComponent { movie = input.required(); + + onPosterError(event: Event) { + const img = event.target as HTMLImageElement; + const placeholder = 'assets/poster_placeholder.png'; + + if (img.src !== window.location.origin + placeholder) { + img.src = placeholder; + } + } } diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index b060804..0157272 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -13,6 +13,7 @@ export class NavbarComponent { navItems:{label:string, path:string}[] = [ {label: 'Programm', path: '/schedule'}, + {label: 'Film importieren', path: '/admin/movie-importer'}, {label: 'Kinosaal-test', path: '/theater-overlay'}, ] } diff --git a/src/app/schedule-header/schedule-header.component.html b/src/app/schedule-header/schedule-header.component.html deleted file mode 100644 index cfb017f..0000000 --- a/src/app/schedule-header/schedule-header.component.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
- event -

- Programmübersicht -

-
- -
- diff --git a/src/app/schedule-header/schedule-header.component.ts b/src/app/schedule-header/schedule-header.component.ts deleted file mode 100644 index 8ac6849..0000000 --- a/src/app/schedule-header/schedule-header.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component, output } from '@angular/core'; - -@Component({ - selector: 'app-schedule-header', - standalone: false, - templateUrl: './schedule-header.component.html', - styleUrl: './schedule-header.component.css' -}) -export class ScheduleHeaderComponent { - movieSearchResult = output(); -} diff --git a/src/app/schedule/schedule.component.html b/src/app/schedule/schedule.component.html index f5924df..902dab4 100644 --- a/src/app/schedule/schedule.component.html +++ b/src/app/schedule/schedule.component.html @@ -1,4 +1,4 @@ - + @for (dateInfo of dates; track dateInfo.date; let i = $index) { diff --git a/src/app/schedule/schedule.component.ts b/src/app/schedule/schedule.component.ts index aae539d..5d88ada 100644 --- a/src/app/schedule/schedule.component.ts +++ b/src/app/schedule/schedule.component.ts @@ -4,7 +4,7 @@ import { Vorstellung } from '@infinimotion/model-frontend'; import { Performance } from '../model/performance.model'; import { MovieGroup } from '../model/movie-group.model'; import { LoadingService } from '../loading.service'; -import { catchError, map, of, tap } from 'rxjs'; +import { catchError, of, tap } from 'rxjs'; @Component({ selector: 'app-schedule', @@ -61,8 +61,7 @@ export class ScheduleComponent implements OnInit { loadPerformances(bookableDays: number) { this.loading.show(); const filter = this.generateDateFilter(bookableDays); - this.http.getPerformaceByFilter(filter).pipe( - map(data => Array.isArray(data) ? data : [data]), + this.http.getPerformacesByFilter(filter).pipe( tap(performaces => { this.performaces = performaces; this.assignPerformancesToDates(); diff --git a/src/assets/poster_placeholder.png b/src/assets/poster_placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..2d94819597b4d869ff5e169ac0d50027555a304d GIT binary patch literal 7573 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 literal 0 HcmV?d00001