From e5fcdfe2125d345ab4cfb087a5d4c0dab3522478 Mon Sep 17 00:00:00 2001 From: Piet Ostendorp Date: Mon, 17 Nov 2025 22:42:17 +0100 Subject: [PATCH 1/3] Improve movie search & title fix --- src/app/app-module.ts | 4 ++ .../menu-header/menu-header.component.html | 4 +- src/app/menu-header/menu-header.component.ts | 2 +- .../movie-importer.component.html | 2 +- .../movie-search/movie-search.component.html | 34 +++++++----- src/app/schedule/schedule.component.css | 4 ++ src/app/schedule/schedule.component.html | 23 +++++--- src/app/schedule/schedule.component.ts | 55 +++++++++++-------- .../theater-overlay.component.html | 2 +- 9 files changed, 78 insertions(+), 52 deletions(-) diff --git a/src/app/app-module.ts b/src/app/app-module.ts index 2b87502..2172c39 100644 --- a/src/app/app-module.ts +++ b/src/app/app-module.ts @@ -23,6 +23,8 @@ import { MatDividerModule } from '@angular/material/divider'; import { MatDialogClose, MatDialogTitle, MatDialogContent, MatDialogActions } from "@angular/material/dialog"; import { MatStepperModule } from '@angular/material/stepper'; import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatBadgeModule } from '@angular/material/badge'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { HeaderComponent } from './header/header.component'; import { HomeComponent } from './home/home.component'; @@ -133,6 +135,8 @@ import { TicketListComponent } from './ticket-list/ticket-list.component'; NgxMaskDirective, NgxMaskPipe, QRCodeComponent, + MatBadgeModule, + MatTooltipModule, ], providers: [ provideBrowserGlobalErrorListeners(), diff --git a/src/app/menu-header/menu-header.component.html b/src/app/menu-header/menu-header.component.html index beb2792..a571725 100644 --- a/src/app/menu-header/menu-header.component.html +++ b/src/app/menu-header/menu-header.component.html @@ -3,8 +3,8 @@ @if ( icon() ) { {{ icon() }} } -

- {{ title() }} +

+ {{ label() }}

@if ( searchBar() ) { diff --git a/src/app/menu-header/menu-header.component.ts b/src/app/menu-header/menu-header.component.ts index 4dd4ec2..ea79579 100644 --- a/src/app/menu-header/menu-header.component.ts +++ b/src/app/menu-header/menu-header.component.ts @@ -7,7 +7,7 @@ import { Component, input, output } from '@angular/core'; styleUrl: './menu-header.component.css' }) export class MenuHeaderComponent { - title = input.required(); + label = input.required(); icon = input(); searchBar = input(false); diff --git a/src/app/movie-importer/movie-importer.component.html b/src/app/movie-importer/movie-importer.component.html index 42b2ea2..6f614b8 100644 --- a/src/app/movie-importer/movie-importer.component.html +++ b/src/app/movie-importer/movie-importer.component.html @@ -1,4 +1,4 @@ - +
diff --git a/src/app/movie-search/movie-search.component.html b/src/app/movie-search/movie-search.component.html index add7471..7b0f9b6 100644 --- a/src/app/movie-search/movie-search.component.html +++ b/src/app/movie-search/movie-search.component.html @@ -1,16 +1,22 @@ - - - Film suchen - +
- - - + @if (searchControl.value && searchControl.value.length > 0) { + + } - - @for (option of filteredOptions | async; track option) { - {{option}} - } - - - +
+ + Film suchen + + + + @for (option of filteredOptions | async; track option) { + {{option}} + } + + +
+ +
diff --git a/src/app/schedule/schedule.component.css b/src/app/schedule/schedule.component.css index e80c311..239f2e2 100644 --- a/src/app/schedule/schedule.component.css +++ b/src/app/schedule/schedule.component.css @@ -5,3 +5,7 @@ ::ng-deep .mat-mdc-tab .mdc-tab-indicator__content--underline { border-color: #6366f1 !important; /* indigo-500 */ } + +.mat-badge-content { + background: #dd2979; +} diff --git a/src/app/schedule/schedule.component.html b/src/app/schedule/schedule.component.html index 902dab4..84b6683 100644 --- a/src/app/schedule/schedule.component.html +++ b/src/app/schedule/schedule.component.html @@ -1,20 +1,25 @@ - + @for (dateInfo of dates; track dateInfo.date; let i = $index) { + + + {{ dateInfo.label }} + + @if (getMovieCount(i) > 0) { - @if (hasSearchResults(i)) { - @for (group of dateInfo.performances; track group.movie.id) { - @if (group.movie.title.toLowerCase().includes(movieSearchResult.toLowerCase())) { - - } + @for (group of dateInfo.performances; track group.movie.id) { + @if (group.movie.title.toLowerCase().includes(movieSearchResult.toLowerCase())) { + } - } @else { - } } @else { - + @if (isSearch()) { + + } @else { + + } } } diff --git a/src/app/schedule/schedule.component.ts b/src/app/schedule/schedule.component.ts index 5d88ada..cd58a67 100644 --- a/src/app/schedule/schedule.component.ts +++ b/src/app/schedule/schedule.component.ts @@ -31,15 +31,7 @@ export class ScheduleComponent implements OnInit { this.loadPerformances(this.bookableDays); } - hasSearchResults(dateIndex: number): boolean { - if (!this.movieSearchResult) return true; - - return this.dates[dateIndex].performances.some(group => - group.movie.title.toLowerCase().includes(this.movieSearchResult.toLowerCase()) - ); - } - - generateDates(bookableDays: number) { + private generateDates(bookableDays: number) { const today = new Date(); for (let i = 0; i < bookableDays; i++) { const date = new Date(today); @@ -58,7 +50,7 @@ export class ScheduleComponent implements OnInit { } } - loadPerformances(bookableDays: number) { + private loadPerformances(bookableDays: number) { this.loading.show(); const filter = this.generateDateFilter(bookableDays); this.http.getPerformacesByFilter(filter).pipe( @@ -75,22 +67,21 @@ export class ScheduleComponent implements OnInit { ).subscribe(); } -private generateDateFilter(bookableDays: number): string[] { - const startDate = new Date(); - const endDate = new Date(); - endDate.setDate(startDate.getDate() + bookableDays - 1); + private generateDateFilter(bookableDays: number): string[] { + const startDate = new Date(); + const endDate = new Date(); + endDate.setDate(startDate.getDate() + bookableDays - 1); - const startStr = startDate.toISOString().split('T')[0] + 'T00:00:00'; - const endStr = endDate.toISOString().split('T')[0] + 'T23:59:59'; + const startStr = startDate.toISOString().split('T')[0] + 'T00:00:00'; + const endStr = endDate.toISOString().split('T')[0] + 'T23:59:59'; - return [ - `ge;start;date;${startStr}`, - `le;start;date;${endStr}`, - ]; -} + return [ + `ge;start;date;${startStr}`, + `le;start;date;${endStr}`, + ]; + } - - assignPerformancesToDates() { + private assignPerformancesToDates() { // Gruppieren nach Datum const groupedByDate: { [key: string]: Vorstellung[] } = {}; @@ -133,6 +124,22 @@ private generateDateFilter(bookableDays: number): string[] { } getMovieCount(index: number): number { - return this.dates[index].performances.length; + if (!this.dates[index]?.performances) { + return 0; + } + + const performances = this.dates[index].performances; + + if (!this.isSearch()) { + return performances.length; + } + + return performances.filter(group => + group.movie.title.toLowerCase().includes(this.movieSearchResult.toLowerCase()) + ).length; + } + + isSearch(): boolean { + return !!this.movieSearchResult && this.movieSearchResult.trim() !== ''; } } diff --git a/src/app/theater-overlay/theater-overlay.component.html b/src/app/theater-overlay/theater-overlay.component.html index f2d7a36..7a88cd2 100644 --- a/src/app/theater-overlay/theater-overlay.component.html +++ b/src/app/theater-overlay/theater-overlay.component.html @@ -1,4 +1,4 @@ - +
From fba895a3bbb085e75c57a4994174592224e797c0 Mon Sep 17 00:00:00 2001 From: Piet Ostendorp Date: Mon, 17 Nov 2025 23:06:42 +0100 Subject: [PATCH 2/3] Add transactional order creation API integration Introduces saveAddOrder method in HttpService to POST orders and tickets transactionally. Refactors OrderComponent to use the new API for submitting orders and tickets in a single transaction, improving data consistency. --- src/app/http.service.ts | 6 ++++ src/app/order/order.component.ts | 50 ++++++++++++++------------------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/app/http.service.ts b/src/app/http.service.ts index d189716..ede6c37 100644 --- a/src/app/http.service.ts +++ b/src/app/http.service.ts @@ -44,6 +44,12 @@ export class HttpService { } + /* POST /api/order-transaction/create */ + saveAddOrder(req: {order:Bestellung, tickets:Eintrittskarte[]}): Observable<{order:Bestellung, tickets:Eintrittskarte[]}> { + return this.http.post<{order: Bestellung, tickets: Eintrittskarte[]}>(`${this.baseUrl}order-transaction/create`, req); + } + + /* Eintrittskarte APIs */ /* GET /api/eintrittskarte/{id} */ diff --git a/src/app/order/order.component.ts b/src/app/order/order.component.ts index 8b13379..84aa2d0 100644 --- a/src/app/order/order.component.ts +++ b/src/app/order/order.component.ts @@ -161,37 +161,31 @@ export class OrderComponent { submitOrder(order: Bestellung, seats: Sitzplatz[], performance: Vorstellung, mode: SubmissionMode) { - this.httpService.addOrder(order).pipe( - // Order erstellen - switchMap(createdOrder => { - // Tickets parallel erstellen - const ticketObservables = seats.map(seat => { - const ticket = this.generateNewTicketObject(performance, seat, createdOrder); - return this.httpService.addTicket(ticket); - }); + // Tickets anlegen + const tickets = seats.map(seat => { + return this.generateNewTicketObject(performance, seat, order);; + }); - // Warten bis alles fertig sind - return forkJoin(ticketObservables).pipe( - tap(createdTickets => { - // Success Handling - if (mode === 'reservation') { - this.orderState.set({ - status: 'reservation-success', - order: createdOrder - }); - } else { - this.orderState.set({ - status: 'purchase-success', - tickets: createdTickets - }); - } + // Transaktionssicher Sitzplatzbuchung + this.httpService.saveAddOrder({order, tickets}).pipe( + tap(createdOrderAndTickets => { + // Success Handling + if (mode === 'reservation') { + this.orderState.set({ + status: 'reservation-success', + order: createdOrderAndTickets.order + }); + } else { + this.orderState.set({ + status: 'purchase-success', + tickets: createdOrderAndTickets.tickets + }); + } - this.selectedSeatsService.commit(); - this.loadingService.hide(); - this.showConfetti(); - }) - ); + this.selectedSeatsService.commit(); + this.loadingService.hide(); + this.showConfetti(); }), catchError(err => { // Error handling From 4408bc9b8466b8e50200399061cb64f1183fd6e0 Mon Sep 17 00:00:00 2001 From: Piet Ostendorp Date: Mon, 17 Nov 2025 23:37:34 +0100 Subject: [PATCH 3/3] Next Sprint. Juhuuu! --- src/app/main/main.component.html | 100 +++++++++++++++---------------- src/app/main/main.component.ts | 2 +- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/app/main/main.component.html b/src/app/main/main.component.html index 9979e51..20d8c07 100644 --- a/src/app/main/main.component.html +++ b/src/app/main/main.component.html @@ -1,11 +1,11 @@ -
-
+
+

Willkommen bei

-
+
 InfiniMotion

! 🎬

@@ -41,7 +41,7 @@ Wir haben uns bei Gestaltung und Stil bewusst an bestehenden Kinowebsites orientiert. Dabei handelt es sich um eine rein stilistische Anlehnung; diese Seite verfolgt keinerlei kommerzielle Zwecke und dient ausschließlich universitären Zwecken. Marken, Designs oder Funktionalitäten, die bekannten Anbietern ähneln, sind nicht als Kopie zum Wettbewerb gedacht, sondern als pragmatische Inspirationsquelle im Rahmen der Praxisarbeit. - + https://infinimotion.de wird zum Projektende offline genommen. @@ -52,57 +52,57 @@
-
- +
+ - - - Sprint #0:    Planung, Installation und Vorbereitung - - + + + Sprint #0:    Planung, Installation und Vorbereitung + + - - - Sprint #1:    Programmübersicht - - + + + Sprint #1:    Programmübersicht + + - - - Sprint #2:    Kinosäle anzeigen - - + + + Sprint #2:    Kinosäle anzeigen + + - - - Sprint #3:    Vorstellungstickets reservieren und buchen - - + + + Sprint #3:    Vorstellungstickets reservieren und buchen + + - - - Sprint #4:    Statistiken auswerten und anzeigen - - + + + Sprint #4:    Statistiken auswerten und anzeigen + + - - - Sprint #5:    Aufbereitung und Optimierung - - - - + + + Sprint #5:    Aufbereitung und Optimierung + + + +
diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 706cf1a..8bfe389 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -7,7 +7,7 @@ import { Component } from '@angular/core'; styleUrl: './main.component.css' }) export class MainComponent { - currentSprint = 3; + currentSprint = 4; isCompleted(index: number): boolean { return index <= this.currentSprint;