From bd56f3242e65821d2b0a781a1213c87d713fa470 Mon Sep 17 00:00:00 2001 From: Piet Ostendorp Date: Sat, 15 Nov 2025 16:34:20 +0100 Subject: [PATCH 01/15] Handle empty seats and rows in theater layout Updated seat and row data structures to allow null values for seats and states, enabling the display of empty seat spaces and rows. Adjusted rendering logic and styles to visually represent gaps in seating, and enhanced the converter to fill missing seats and rows with placeholders. --- src/app/seat-row/seat-row.component.css | 7 ++- src/app/seat-row/seat-row.component.html | 7 ++- src/app/seat-row/seat-row.component.ts | 2 +- .../theater-layout.component.ts | 2 +- .../theater-overlay.component.ts | 56 ++++++++++++++++--- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/app/seat-row/seat-row.component.css b/src/app/seat-row/seat-row.component.css index 8b13789..4cf8c8e 100644 --- a/src/app/seat-row/seat-row.component.css +++ b/src/app/seat-row/seat-row.component.css @@ -1 +1,6 @@ - +.empty-seat-space { + width: 30px; + /* Keine Ahnung, wo die zusätzlichen 6.5px herkommen müssen. Wir sonst dünner angezeigt */ + height: 36.5px; + /* height: 30px; */ +} diff --git a/src/app/seat-row/seat-row.component.html b/src/app/seat-row/seat-row.component.html index 869b536..5ef657e 100644 --- a/src/app/seat-row/seat-row.component.html +++ b/src/app/seat-row/seat-row.component.html @@ -1,3 +1,8 @@ @for (entry of rowSeatList(); track $index) { - + + @if (entry.seat != null && entry.state != null) { + + } @else { +
+ } } diff --git a/src/app/seat-row/seat-row.component.ts b/src/app/seat-row/seat-row.component.ts index ec4c001..6afab92 100644 --- a/src/app/seat-row/seat-row.component.ts +++ b/src/app/seat-row/seat-row.component.ts @@ -9,5 +9,5 @@ import {TheaterSeatState} from '../model/theater-seat-state.model'; styleUrl: './seat-row.component.css' }) export class SeatRowComponent { - rowSeatList = input.required<{ seat: Sitzplatz, state: TheaterSeatState }[]>(); + rowSeatList = input.required<{ seat: Sitzplatz | null, state: TheaterSeatState | null }[]>(); } diff --git a/src/app/theater-layout/theater-layout.component.ts b/src/app/theater-layout/theater-layout.component.ts index d05fbce..1478459 100644 --- a/src/app/theater-layout/theater-layout.component.ts +++ b/src/app/theater-layout/theater-layout.component.ts @@ -10,7 +10,7 @@ import {TheaterSeatState} from '../model/theater-seat-state.model'; styleUrl: './theater-layout.component.css' }) export class TheaterLayoutComponent { - seatsPerRow = input.required<{ seat: Sitzplatz, state: TheaterSeatState }[][]>(); + seatsPerRow = input.required<{ seat: Sitzplatz | null, state: TheaterSeatState | null }[][]>(); protected selectedSeatsService = inject(SelectedSeatsService); } diff --git a/src/app/theater-overlay/theater-overlay.component.ts b/src/app/theater-overlay/theater-overlay.component.ts index 5a3e55c..68621eb 100644 --- a/src/app/theater-overlay/theater-overlay.component.ts +++ b/src/app/theater-overlay/theater-overlay.component.ts @@ -25,7 +25,7 @@ export class TheaterOverlayComponent implements OnInit, OnDestroy { readonly loading = inject(LoadingService); showId!: number; - seatsPerRow = signal<{ seat: Sitzplatz, state: TheaterSeatState }[][]>([]); + seatsPerRow = signal<{ seat: Sitzplatz | null, state: TheaterSeatState | null }[][]>([]); performance: Vorstellung | undefined; seatCategories: Sitzkategorie[] = []; @@ -90,12 +90,13 @@ export class TheaterOverlayComponent implements OnInit, OnDestroy { } converter(resp: { seats: Sitzplatz[], reserved: Sitzplatz[], booked: Sitzplatz[] }): { - seat: Sitzplatz, - state: TheaterSeatState + seat: Sitzplatz | null, + state: TheaterSeatState | null }[][] { - let rows: { seat: Sitzplatz, state: TheaterSeatState }[][] = []; + let rows: { seat: Sitzplatz | null, state: TheaterSeatState | null }[][] = []; const categoryMap = new Map(); + // Sitzplätze sammeln resp.seats.forEach(seat => { if (!rows[seat.row.position]) { rows[seat.row.position] = []; @@ -114,9 +115,50 @@ export class TheaterOverlayComponent implements OnInit, OnDestroy { this.seatCategories = Array.from(categoryMap.values()).sort((a, b) => a.id - b.id); - rows = rows.filter(row => row.length > 0).sort((a, b) => a[0].seat.row.position - b[0].seat.row.position); - rows.forEach(row => row.sort((a, b) => a.seat.position - b.seat.position)); - return rows; + rows = rows.filter(row => row && row.length > 0).sort((a, b) => a[0].seat!.row.position - b[0].seat!.row.position); + + if (rows.length === 0) { + return []; + } + + // Leere Plätze auffüllen + const filledSeats: { seat: Sitzplatz | null, state: TheaterSeatState | null }[][] = []; + + rows.forEach(row => { + row.sort((a, b) => a.seat!.position - b.seat!.position) + + const minPos = row[0].seat!.position; + const maxPos = row[row.length - 1].seat!.position; + const filledRow: { seat: Sitzplatz | null, state: TheaterSeatState | null }[] = []; + + for (let pos = minPos; pos <= maxPos; pos++) { + const existingSeat = row.find(s => s.seat!.position === pos); + if (existingSeat) { + filledRow.push(existingSeat); + } else { + filledRow.push({ seat: null, state: null }); + } + } + + filledSeats.push(filledRow); + }); + + // Leere Reihen auffüllen + const minRowPos = rows[0][0].seat!.row.position; + const maxRowPos = rows[rows.length - 1][0].seat!.row.position; + const filledRows: { seat: Sitzplatz | null, state: TheaterSeatState | null }[][] = []; + + let processedIndex = 0; + for (let rowPos = minRowPos; rowPos <= maxRowPos; rowPos++) { + if (processedIndex < filledSeats.length && filledSeats[processedIndex][0].seat!.row.position === rowPos) { + filledRows.push(filledSeats[processedIndex]); + processedIndex++; + } else { + filledRows.push([{ seat: null, state: null }]); + } + } + + return filledRows; } refreshSeats(): void { From e5fcdfe2125d345ab4cfb087a5d4c0dab3522478 Mon Sep 17 00:00:00 2001 From: Piet Ostendorp Date: Mon, 17 Nov 2025 22:42:17 +0100 Subject: [PATCH 02/15] 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 03/15] 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 04/15] 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; From bc274c0e0b6c040e6afeca46b14458728b8dac83 Mon Sep 17 00:00:00 2001 From: Marcel-Anker Date: Tue, 18 Nov 2025 14:01:00 +0100 Subject: [PATCH 05/15] providing statistic data in statistic component --- package-lock.json | 284 +++++++++---------- package.json | 2 +- src/app/app-module.ts | 2 + src/app/app-routing-module.ts | 7 + src/app/http.service.ts | 31 +- src/app/navbar/navbar.component.ts | 1 + src/app/statistics/statistics.component.css | 0 src/app/statistics/statistics.component.html | 6 + src/app/statistics/statistics.component.ts | 38 +++ 9 files changed, 212 insertions(+), 159 deletions(-) create mode 100644 src/app/statistics/statistics.component.css create mode 100644 src/app/statistics/statistics.component.html create mode 100644 src/app/statistics/statistics.component.ts diff --git a/package-lock.json b/package-lock.json index d26398e..1d2ab91 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.102", + "@infinimotion/model-frontend": "^0.0.110", "@tailwindcss/postcss": "^4.1.14", "angularx-qrcode": "^20.0.0", "canvas-confetti": "^1.9.4", @@ -277,13 +277,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.2003.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.9.tgz", - "integrity": "sha512-p0GO2H8hiZjRHI9sm4tXTF3OpWaEnkqvB0GBGJfGp8RvpPfDA2t3j2NAUNtd75H+B0xdfyWLmNq9YJGpy6gznA==", + "version": "0.2003.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.10.tgz", + "integrity": "sha512-2SWetxJzS8gRX6OKQstkWx37VRvZVgcEBDLsDSaeTjpnwh81A+niZQjAVRdwL0NEt1Wixk/RxfeUuCmdyyHvhQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.9", + "@angular-devkit/core": "20.3.10", "rxjs": "7.8.2" }, "engines": { @@ -293,9 +293,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "20.3.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.9.tgz", - "integrity": "sha512-bXsAGIUb4p60x548YmvnMvjwd3FwWz6re1uTM7dV0XH8nQn3XMhOQ3Q3sAckzJHxkDuaRhB3K/a4kupoOmVfTQ==", + "version": "20.3.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.10.tgz", + "integrity": "sha512-COOT2eVebDwHhwENk12VR6m0wjL8D7p0dncEHF15zaBt1IXEnVhGESjSrs5klnPnt5T55qCBKyCTaeK7i/cS8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -321,13 +321,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "20.3.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.9.tgz", - "integrity": "sha512-oaIjAKPmHMZBTC0met5M7dbXBeZnCNwmHacT/kBHNVBAz/NI95fuAfb2P0Jxt7gWdQXejDSxWp0tL+sZIyO0xw==", + "version": "20.3.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.10.tgz", + "integrity": "sha512-2N2WF9lj+kr3uCG4+vFadYCL5hAT4dxMgzwScSdOqSd0O+GZD0CzKbDzlfvWIWC/ZealC5Sh4dFEQaRfmy72xA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.9", + "@angular-devkit/core": "20.3.10", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "8.2.0", @@ -340,14 +340,14 @@ } }, "node_modules/@angular/build": { - "version": "20.3.9", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.9.tgz", - "integrity": "sha512-Ulimvg6twPSCraaZECEmENfKBlD4M1yqeHlg6dCzFNM4xcwaGUnuG6O3cIQD59DaEvaG73ceM2y8ftYdxAwFow==", + "version": "20.3.10", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.10.tgz", + "integrity": "sha512-nQrj1nMNZygYDilThc7hPrD6/NIWF/BOSgMfE4VkXQp8d0QronP3HFJ/h77MeoughMRFRhix0pqQSlXJQ2SGTQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2003.9", + "@angular-devkit/architect": "0.2003.10", "@babel/core": "7.28.3", "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", @@ -389,7 +389,7 @@ "@angular/platform-browser": "^20.0.0", "@angular/platform-server": "^20.0.0", "@angular/service-worker": "^20.0.0", - "@angular/ssr": "^20.3.9", + "@angular/ssr": "^20.3.10", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^20.0.0", @@ -439,11 +439,10 @@ } }, "node_modules/@angular/cdk": { - "version": "20.2.12", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.12.tgz", - "integrity": "sha512-hz8GtiMy3N9/e8407ZfrByHD5GEC4SkWtxyUknWuTM9P88AOie0jDZ6CfQg9gQ0OJX+6BAbJV3RpYZA1uzNUqA==", + "version": "20.2.13", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.13.tgz", + "integrity": "sha512-h1jTkCmJ/rEQQMkxgKFMCBOrMfjZEnppgdekNmSTerwdVp4vdosTDTzFH/kwiOGFeRClffmvqQ2XLG8mQOKOtA==", "license": "MIT", - "peer": true, "dependencies": { "parse5": "^8.0.0", "tslib": "^2.3.0" @@ -455,19 +454,19 @@ } }, "node_modules/@angular/cli": { - "version": "20.3.9", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.9.tgz", - "integrity": "sha512-4eKpRDg96B20yrKJqjA24zgxYy1RiRd70FvF/KG1hqSowsWwtzydtEJ3VM6iFWS9t1D8truuVpKjMEnn1Y274A==", + "version": "20.3.10", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.10.tgz", + "integrity": "sha512-CQzXScurBXSuMMn0jf6UYDItdggaM3bHYERKL4cUG1z5JqSozVFin1+TB1EjWYkddwdgC10R5xQurdMb+ahRNw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.2003.9", - "@angular-devkit/core": "20.3.9", - "@angular-devkit/schematics": "20.3.9", + "@angular-devkit/architect": "0.2003.10", + "@angular-devkit/core": "20.3.10", + "@angular-devkit/schematics": "20.3.10", "@inquirer/prompts": "7.8.2", "@listr2/prompt-adapter-inquirer": "3.0.1", "@modelcontextprotocol/sdk": "1.17.3", - "@schematics/angular": "20.3.9", + "@schematics/angular": "20.3.10", "@yarnpkg/lockfile": "1.1.0", "algoliasearch": "5.35.0", "ini": "5.0.0", @@ -490,11 +489,10 @@ } }, "node_modules/@angular/common": { - "version": "20.3.10", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.10.tgz", - "integrity": "sha512-12fEzvKbEqjqy1fSk9DMYlJz6dF1MJVXuC5BB+oWWJpd+2lfh4xJ62pkvvLGAICI89hfM5n9Cy5kWnXwnqPZsA==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.12.tgz", + "integrity": "sha512-rFcDfe67ffrb435C6t2lc27WGbizeOcgce30tUhH0iezwEvU+kHHWezXXX6Ylx3TFgqGkhcxL0fliuFYrpM1Vw==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -502,16 +500,15 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "20.3.10", + "@angular/core": "20.3.12", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "20.3.10", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.10.tgz", - "integrity": "sha512-cW939Lr8GZjPSYfbQKIDNrUaHWmn2M+zBbERThfq5skLuY+xM60bJFv4NqBekfX6YqKLCY62ilUZlnImYIXaqA==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.12.tgz", + "integrity": "sha512-bGESKz97nWiEQ/sydTq/Lzv3zlLvDb8t0msLG5Xti7Ch1EdLddXS8d2D/zFsjiGbAUKVsT6RgPCLHYoi4ocbhA==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -520,12 +517,11 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "20.3.10", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.3.10.tgz", - "integrity": "sha512-9BemvpFxA26yIVdu8ROffadMkEdlk/AQQ2Jb486w7RPkrvUQ0pbEJukhv9aryJvhbMopT66S5H/j4ipOUMzmzQ==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.3.12.tgz", + "integrity": "sha512-3SJkexqsydYjIs0iLiJr5AdwkvumpzvjJM6s76iaxXHkRll5k/vM0wqkXLlSIwieBrecO9D4J73lDLWDevXl5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "7.28.3", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -544,7 +540,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "20.3.10", + "@angular/compiler": "20.3.12", "typescript": ">=5.8 <6.0" }, "peerDependenciesMeta": { @@ -554,11 +550,10 @@ } }, "node_modules/@angular/core": { - "version": "20.3.10", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.10.tgz", - "integrity": "sha512-g99Qe+NOVo72OLxowVF9NjCckswWYHmvO7MgeiZTDJbTjF9tXH96dMx7AWq76/GUinV10sNzDysVW16NoAbCRQ==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.12.tgz", + "integrity": "sha512-K7vibMr55a7+EsuDhkg4Pk+ELuMm12olllwqL/CiQUcHXZ9Zgc4KYGTUuxWB69qJCG90gdSZS7tm5Dx0wDcyjg==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -566,7 +561,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "20.3.10", + "@angular/compiler": "20.3.12", "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0" }, @@ -580,11 +575,10 @@ } }, "node_modules/@angular/forms": { - "version": "20.3.10", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.10.tgz", - "integrity": "sha512-9yWr51EUauTEINB745AaHwZNTHLpXIm4uxuykxzOg+g2QskEgVfH26uS8G2ogdNuwYpB8wnsXWr34qhM3qgOWw==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.12.tgz", + "integrity": "sha512-O0Jy8ScaN3qVipDfR4s0SIxGrz/+MbCdmR05ZYVWf1W5P3dvETKt9WNjX9fYYV47GdgSveyFjuCR2NvWlv94zA==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -592,22 +586,22 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "20.3.10", - "@angular/core": "20.3.10", - "@angular/platform-browser": "20.3.10", + "@angular/common": "20.3.12", + "@angular/core": "20.3.12", + "@angular/platform-browser": "20.3.12", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/material": { - "version": "20.2.12", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-20.2.12.tgz", - "integrity": "sha512-DVenIZmV87qhDBlI2Xv3Z+b+IFI1s4wcZsFrzDi1FBMxKLsltJwMHf4SAmuqY0Mm/2Vw7HEZlfE130TuqjG8Ig==", + "version": "20.2.13", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-20.2.13.tgz", + "integrity": "sha512-9pjp2mULOxojYzOO7qdqt/gSVLrpYBwsIM3K0fxp+mNEcJgNjIxvmRKx46LY9+v0yrPY9puoQvP/T2C+o1+xsw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/cdk": "20.2.12", + "@angular/cdk": "20.2.13", "@angular/common": "^20.0.0 || ^21.0.0", "@angular/core": "^20.0.0 || ^21.0.0", "@angular/forms": "^20.0.0 || ^21.0.0", @@ -616,11 +610,10 @@ } }, "node_modules/@angular/platform-browser": { - "version": "20.3.10", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.10.tgz", - "integrity": "sha512-UV8CGoB5P3FmJciI3/I/n3L7C3NVgGh7bIlZ1BaB/qJDtv0Wq0rRAGwmT/Z3gwmrRtfHZWme7/CeQ2CYJmMyUQ==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.12.tgz", + "integrity": "sha512-14KQsXZyaQhbRwFz1W58CtbXQc9L+mfuHBgwQjQo99422Yk0ye5WVMb6DHH7dH671qFVqL0XL7zdOPBebaAnJQ==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -628,9 +621,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/animations": "20.3.10", - "@angular/common": "20.3.10", - "@angular/core": "20.3.10" + "@angular/animations": "20.3.12", + "@angular/common": "20.3.12", + "@angular/core": "20.3.12" }, "peerDependenciesMeta": { "@angular/animations": { @@ -639,9 +632,9 @@ } }, "node_modules/@angular/router": { - "version": "20.3.10", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.10.tgz", - "integrity": "sha512-Z03cfH1jgQ7XMDJj4R8qAGqivcvhdG3wYBwaiN1K1ODBgPhbFKNeD4stKqYp7xBNtswmM2O2jMxrL/Djwju4Gg==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.12.tgz", + "integrity": "sha512-hUipb9JI/Euy3bdlhzkcWlw3cTyssPTVTDwSvyGxWO4i+UKATQYmxh8EDOrDYzFp6Aexiy0Hff/H8umdsn6ZdA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -650,9 +643,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "20.3.10", - "@angular/core": "20.3.10", - "@angular/platform-browser": "20.3.10", + "@angular/common": "20.3.12", + "@angular/core": "20.3.12", + "@angular/platform-browser": "20.3.12", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -687,7 +680,6 @@ "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -1403,9 +1395,9 @@ } }, "node_modules/@infinimotion/model-frontend": { - "version": "0.0.102", - "resolved": "https://git.infinimotion.de/api/packages/infinimotion/npm/%40infinimotion%2Fmodel-frontend/-/0.0.102/model-frontend-0.0.102.tgz", - "integrity": "sha512-NJV9bSBubdOZ1GBIe9To3o/hh6AZscJcTyaZY2nGmMxH+GhtvO1AHmjhrQeRjwAKFiwZMEwEg4ktFiOAp3MTMQ==", + "version": "0.0.110", + "resolved": "https://git.infinimotion.de/api/packages/infinimotion/npm/%40infinimotion%2Fmodel-frontend/-/0.0.110/model-frontend-0.0.110.tgz", + "integrity": "sha512-MzHFDV8g2nOQ3p9UysB9HfoFzmycXIIaccN2do3gqIM9lkwsiN/htoGHr8fza9uv+OACifrDt4xp73nSy2Xf8w==", "license": "ISC" }, "node_modules/@inquirer/ansi": { @@ -1419,14 +1411,14 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.1.tgz", - "integrity": "sha512-rOcLotrptYIy59SGQhKlU0xBg1vvcVl2FdPIEclUvKHh0wo12OfGkId/01PIMJ/V+EimJ77t085YabgnQHBa5A==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", "dev": true, "license": "MIT", "dependencies": { "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.1", + "@inquirer/core": "^10.3.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" @@ -1466,9 +1458,9 @@ } }, "node_modules/@inquirer/core": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.1.tgz", - "integrity": "sha512-hzGKIkfomGFPgxKmnKEKeA+uCYBqC+TKtRx5LgyHRCrF6S2MliwRIjp3sUaWwVzMp7ZXVs8elB0Tfe682Rpg4w==", + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", "dev": true, "license": "MIT", "dependencies": { @@ -1476,7 +1468,7 @@ "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", - "mute-stream": "^3.0.0", + "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" @@ -1494,13 +1486,13 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.22", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.22.tgz", - "integrity": "sha512-8yYZ9TCbBKoBkzHtVNMF6PV1RJEUvMlhvmS3GxH4UvXMEHlS45jFyqFy0DU+K42jBs5slOaA78xGqqqWAx3u6A==", + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.1", + "@inquirer/core": "^10.3.2", "@inquirer/external-editor": "^1.0.3", "@inquirer/type": "^3.0.10" }, @@ -1517,13 +1509,13 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.22", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.22.tgz", - "integrity": "sha512-9XOjCjvioLjwlq4S4yXzhvBmAXj5tG+jvva0uqedEsQ9VD8kZ+YT7ap23i0bIXOtow+di4+u3i6u26nDqEfY4Q==", + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.1", + "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" }, @@ -1572,13 +1564,13 @@ } }, "node_modules/@inquirer/input": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.0.tgz", - "integrity": "sha512-h4fgse5zeGsBSW3cRQqu9a99OXRdRsNCvHoBqVmz40cjYjYFzcfwD0KA96BHIPlT7rZw0IpiefQIqXrjbzjS4Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.1", + "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" }, "engines": { @@ -1594,13 +1586,13 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.22", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.22.tgz", - "integrity": "sha512-oAdMJXz++fX58HsIEYmvuf5EdE8CfBHHXjoi9cTcQzgFoHGZE+8+Y3P38MlaRMeBvAVnkWtAxMUF6urL2zYsbg==", + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.1", + "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" }, "engines": { @@ -1616,14 +1608,14 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.22", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.22.tgz", - "integrity": "sha512-CbdqK1ioIr0Y3akx03k/+Twf+KSlHjn05hBL+rmubMll7PsDTGH0R4vfFkr+XrkB0FOHrjIwVP9crt49dgt+1g==", + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", "dev": true, "license": "MIT", "dependencies": { "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.1", + "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" }, "engines": { @@ -1644,7 +1636,6 @@ "integrity": "sha512-nqhDw2ZcAUrKNPwhjinJny903bRhI0rQhiDz1LksjeRxqa36i3l75+4iXbOy0rlDpLJGxqtgoPavQjmmyS5UJw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/checkbox": "^4.2.1", "@inquirer/confirm": "^5.1.14", @@ -1670,13 +1661,13 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.10.tgz", - "integrity": "sha512-Du4uidsgTMkoH5izgpfyauTL/ItVHOLsVdcY+wGeoGaG56BV+/JfmyoQGniyhegrDzXpfn3D+LFHaxMDRygcAw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.1", + "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" }, @@ -1693,13 +1684,13 @@ } }, "node_modules/@inquirer/search": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.1.tgz", - "integrity": "sha512-cKiuUvETublmTmaOneEermfG2tI9ABpb7fW/LqzZAnSv4ZaJnbEis05lOkiBuYX5hNdnX0Q9ryOQyrNidb55WA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.1", + "@inquirer/core": "^10.3.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" @@ -1717,14 +1708,14 @@ } }, "node_modules/@inquirer/select": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.1.tgz", - "integrity": "sha512-E9hbLU4XsNe2SAOSsFrtYtYQDVi1mfbqJrPDvXKnGlnRiApBdWMJz7r3J2Ff38AqULkPUD3XjQMD4492TymD7Q==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", "dev": true, "license": "MIT", "dependencies": { "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.1", + "@inquirer/core": "^10.3.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" @@ -3429,14 +3420,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "20.3.9", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.9.tgz", - "integrity": "sha512-XkgTwGhhrx+MVi2+TFO32d6Es5Uezzx7Y7B/e2ulDlj08bizxQj+9wkeLt5+bR8JWODHpEntZn/Xd5WvXnODGA==", + "version": "20.3.10", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.10.tgz", + "integrity": "sha512-F9ntS2CElpoWlENf4b03nwdTcN9Ri0Nb4SAE/pfRw3In09h2UHxYyf1ex9jqQt70xltDg4wvyuc3mMs+JlSx9A==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.9", - "@angular-devkit/schematics": "20.3.9", + "@angular-devkit/core": "20.3.10", + "@angular-devkit/schematics": "20.3.10", "jsonc-parser": "3.3.1" }, "engines": { @@ -3865,9 +3856,9 @@ "license": "MIT" }, "node_modules/@types/jasmine": { - "version": "5.1.12", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.12.tgz", - "integrity": "sha512-1BzPxNsFDLDfj9InVR3IeY0ZVf4o9XV+4mDqoCfyPkbsA7dYyKAPAb2co6wLFlHcvxPlt1wShm7zQdV7uTfLGA==", + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.13.tgz", + "integrity": "sha512-MYCcDkruFc92LeYZux5BC0dmqo2jk+M5UIZ4/oFnAPCXN9mCcQhLyj7F3/Za7rocVyt5YRr1MmqJqFlvQ9LVcg==", "dev": true, "license": "MIT" }, @@ -3877,7 +3868,6 @@ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -4097,9 +4087,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.8.26", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.26.tgz", - "integrity": "sha512-73lC1ugzwoaWCLJ1LvOgrR5xsMLTqSKIEoMHVtL9E/HNk0PXtTM76ZIm84856/SF7Nv8mPZxKoBsgpm0tR1u1Q==", + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.29.tgz", + "integrity": "sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4224,7 +4214,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -4412,9 +4401,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001754", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", - "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==", + "version": "1.0.30001755", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001755.tgz", + "integrity": "sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==", "dev": true, "funding": [ { @@ -4992,9 +4981,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.250", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.250.tgz", - "integrity": "sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==", + "version": "1.5.254", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.254.tgz", + "integrity": "sha512-DcUsWpVhv9svsKRxnSCZ86SjD+sp32SGidNB37KpqXJncp1mfUgKbHvBomE89WJDbfVKw1mdv5+ikrvd43r+Bg==", "dev": true, "license": "ISC" }, @@ -5353,7 +5342,6 @@ "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", @@ -6317,8 +6305,7 @@ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.9.0.tgz", "integrity": "sha512-OMUvF1iI6+gSRYOhMrH4QYothVLN9C3EJ6wm4g7zLJlnaTl8zbaPOr0bTw70l7QxkoM7sVFOWo83u9B2Fe2Zng==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jiti": { "version": "2.6.1", @@ -6412,7 +6399,6 @@ "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "1.5.0", "body-parser": "^1.19.0", @@ -7129,7 +7115,6 @@ "integrity": "sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", @@ -7743,13 +7728,13 @@ } }, "node_modules/mute-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", - "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", "dev": true, "license": "ISC", "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/nanoid": { @@ -8991,7 +8976,6 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -9048,7 +9032,6 @@ "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -9903,8 +9886,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/tuf-js": { "version": "3.1.0", @@ -9942,7 +9924,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10129,7 +10110,6 @@ "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -10532,7 +10512,6 @@ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -10551,8 +10530,7 @@ "version": "0.15.1", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz", "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==", - "license": "MIT", - "peer": true + "license": "MIT" } } } diff --git a/package.json b/package.json index 955f4ce..47a9dd6 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.102", + "@infinimotion/model-frontend": "^0.0.110", "@tailwindcss/postcss": "^4.1.14", "angularx-qrcode": "^20.0.0", "canvas-confetti": "^1.9.4", diff --git a/src/app/app-module.ts b/src/app/app-module.ts index 2b87502..58a44ac 100644 --- a/src/app/app-module.ts +++ b/src/app/app-module.ts @@ -62,6 +62,7 @@ import { PurchaseSuccessComponent } from './purchase-success/purchase-success.co import { PurchaseFailedComponent } from './purchase-failed/purchase-failed.component'; import { TicketSmallComponent } from './ticket-small/ticket-small.component'; import { TicketListComponent } from './ticket-list/ticket-list.component'; +import { StatisticsComponent } from './statistics/statistics.component'; @NgModule({ @@ -105,6 +106,7 @@ import { TicketListComponent } from './ticket-list/ticket-list.component'; PurchaseFailedComponent, TicketSmallComponent, TicketListComponent, + StatisticsComponent, ], imports: [ AppRoutingModule, diff --git a/src/app/app-routing-module.ts b/src/app/app-routing-module.ts index d22e9df..178b014 100644 --- a/src/app/app-routing-module.ts +++ b/src/app/app-routing-module.ts @@ -8,6 +8,7 @@ import { ScheduleComponent } from './schedule/schedule.component'; import { TheaterOverlayComponent} from './theater-overlay/theater-overlay.component'; import { MovieImporterComponent } from './movie-importer/movie-importer.component'; import { AuthGuard } from './auth.guard'; +import {StatisticsComponent} from './statistics/statistics.component'; const routes: Routes = [ // Seiten ohne Layout @@ -28,6 +29,12 @@ const routes: Routes = [ data: { roles: ['admin'] }, // Array von erlaubten Rollen. Derzeit gäbe es 'admin' und 'employee' }, { path: 'selection/performance/:id', component: TheaterOverlayComponent}, + { + path: 'admin/statistics', + component: StatisticsComponent, + canActivate: [AuthGuard], + data: { roles: ['admin'] }, // Array von erlaubten Rollen. Derzeit gäbe es 'admin' und 'employee' + }, ], }, diff --git a/src/app/http.service.ts b/src/app/http.service.ts index d189716..5aefa63 100644 --- a/src/app/http.service.ts +++ b/src/app/http.service.ts @@ -1,4 +1,13 @@ -import { Kinosaal, Sitzplatz, Vorstellung, Film, OmdbSearch, Bestellung, Eintrittskarte } from '@infinimotion/model-frontend'; +import { + Kinosaal, + Sitzplatz, + Vorstellung, + Film, + OmdbSearch, + Bestellung, + Eintrittskarte, + StatisticsReduced, StatisticsFilm +} from '@infinimotion/model-frontend'; import { HttpClient } from "@angular/common/http"; import { inject, Injectable } from "@angular/core"; import { Observable } from "rxjs"; @@ -154,8 +163,12 @@ export class HttpService { /* 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}`); + 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}`); } @@ -164,12 +177,20 @@ export class HttpService { /* GET /api/importer/search */ searchMovie(query: string): Observable { return this.http.get(`${this.baseUrl}importer/search`, { - params: { title: query } + params: {title: query} }); } /* POST /api/importer/import */ importMovie(imdbId: string): Observable { - return this.http.post(`${this.baseUrl}importer/import?id=${imdbId}`, {}) + return this.http.post(`${this.baseUrl}importer/import?id=${imdbId}`, {}) + } + + + /* Statistics APIs */ + + /* GET /api/statistics/list */ + getStatisticsList(): Observable { + return this.http.get(`${this.baseUrl}statistics/list`) } } diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index 5f2de5f..8633d9c 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -11,6 +11,7 @@ export class NavbarComponent { navItems: { label:string, path:string }[] = [ {label: 'Programm', path: '/schedule'}, {label: 'Film importieren', path: '/admin/movie-importer'}, + {label: 'Statistiken', path: '/admin/statistics'}, ] private auth = inject(AuthService) diff --git a/src/app/statistics/statistics.component.css b/src/app/statistics/statistics.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/statistics/statistics.component.html b/src/app/statistics/statistics.component.html new file mode 100644 index 0000000..4dd5f10 --- /dev/null +++ b/src/app/statistics/statistics.component.html @@ -0,0 +1,6 @@ + diff --git a/src/app/statistics/statistics.component.ts b/src/app/statistics/statistics.component.ts new file mode 100644 index 0000000..fa82c7f --- /dev/null +++ b/src/app/statistics/statistics.component.ts @@ -0,0 +1,38 @@ +import {Component, inject} from '@angular/core'; +import {HttpService} from '../http.service'; +import { + Eintrittskarte, + Film, + StatisticsFilm, + StatisticsFilmReduced, + StatisticsReduced, StatisticsVorstellungReduced, + Vorstellung +} from '@infinimotion/model-frontend'; + +@Component({ + selector: 'app-statistics', + standalone: false, + templateUrl: './statistics.component.html', + styleUrl: './statistics.component.css', +}) +export class StatisticsComponent { + private http = inject(HttpService); + private movies: StatisticsFilmReduced[] = [] + private shows: StatisticsVorstellungReduced[] = [] + + ngOnInit(): void { + this.loadData(); + } + + loadData() { + this.http.getStatisticsList().subscribe({ + next: (response) => { + this.movies = response.movies; + this.shows = response.shows; + console.log(this.movies) + console.log(this.shows) + }, + error: (err) => console.error('Fehler beim Laden der Statistiken', err), + }); + } +} From 78144d74475d69ddd22731098908f0be844b7fb6 Mon Sep 17 00:00:00 2001 From: Piet Ostendorp Date: Tue, 18 Nov 2025 21:00:29 +0100 Subject: [PATCH 06/15] Update routing and UI for performance checkout Changed route paths and parameters for performance checkout to use '/performance/:performanceId/checkout' instead of '/selection/performance/:id'. Updated related components to use new route and param names. Improved UI spacing and styling in several components for better layout and consistency. --- src/app/app-routing-module.ts | 2 +- .../movie-performance/movie-performance.component.html | 2 +- src/app/movie-performance/movie-performance.component.ts | 2 +- src/app/performance-info/performance-info.component.html | 2 +- src/app/purchase-failed/purchase-failed.component.html | 2 +- src/app/purchase-success/purchase-success.component.html | 6 +++--- .../reservation-failed/reservation-failed.component.html | 2 +- .../reservation-success.component.html | 8 +++----- src/app/theater-layout/theater-layout.component.html | 6 +++--- src/app/theater-overlay/theater-overlay.component.html | 6 +++--- src/app/theater-overlay/theater-overlay.component.ts | 4 +++- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/app/app-routing-module.ts b/src/app/app-routing-module.ts index d22e9df..6f48c5e 100644 --- a/src/app/app-routing-module.ts +++ b/src/app/app-routing-module.ts @@ -27,7 +27,7 @@ const routes: Routes = [ canActivate: [AuthGuard], data: { roles: ['admin'] }, // Array von erlaubten Rollen. Derzeit gäbe es 'admin' und 'employee' }, - { path: 'selection/performance/:id', component: TheaterOverlayComponent}, + { path: 'performance/:performanceId/checkout', component: TheaterOverlayComponent}, ], }, diff --git a/src/app/movie-performance/movie-performance.component.html b/src/app/movie-performance/movie-performance.component.html index f1811e4..1584f95 100644 --- a/src/app/movie-performance/movie-performance.component.html +++ b/src/app/movie-performance/movie-performance.component.html @@ -1,6 +1,6 @@ -
+

{{ hall() }}

diff --git a/src/app/movie-performance/movie-performance.component.ts b/src/app/movie-performance/movie-performance.component.ts index b6925d0..6f98ff9 100644 --- a/src/app/movie-performance/movie-performance.component.ts +++ b/src/app/movie-performance/movie-performance.component.ts @@ -15,7 +15,7 @@ export class MoviePerformanceComponent implements OnInit { route: string = ''; ngOnInit() { - this.route = `../selection/performance/${this.id()}`; + this.route = `../performance/${this.id()}/checkout`; } startTime = computed(() => diff --git a/src/app/performance-info/performance-info.component.html b/src/app/performance-info/performance-info.component.html index 786c373..ee30310 100644 --- a/src/app/performance-info/performance-info.component.html +++ b/src/app/performance-info/performance-info.component.html @@ -13,7 +13,7 @@

{{ getStartTimeString() }} • {{ performance().hall.name }}

{{ movie().title }}

- +
diff --git a/src/app/purchase-failed/purchase-failed.component.html b/src/app/purchase-failed/purchase-failed.component.html index 6cc5888..d841ce0 100644 --- a/src/app/purchase-failed/purchase-failed.component.html +++ b/src/app/purchase-failed/purchase-failed.component.html @@ -6,6 +6,6 @@

Leider konnten Ihre Sitzplätze nicht gekauft werden. Dies kann passieren, wenn andere Nutzer zeitgleich versucht haben, dieselben Sitzplätze zu kaufen.

- +
diff --git a/src/app/purchase-success/purchase-success.component.html b/src/app/purchase-success/purchase-success.component.html index 4d40726..c439043 100644 --- a/src/app/purchase-success/purchase-success.component.html +++ b/src/app/purchase-success/purchase-success.component.html @@ -2,10 +2,10 @@

Vielen Dank für Ihren Einkauf!

Ihre Sitzplätze wurden erfolgreich gebucht.

- + - - + +
diff --git a/src/app/reservation-failed/reservation-failed.component.html b/src/app/reservation-failed/reservation-failed.component.html index a8913c0..abd1532 100644 --- a/src/app/reservation-failed/reservation-failed.component.html +++ b/src/app/reservation-failed/reservation-failed.component.html @@ -6,6 +6,6 @@

Leider konnten Ihre Sitzplätze nicht reserviert werden. Dies kann passieren, wenn andere Nutzer gleichzeitig versucht haben, dieselben Sitzplätze zu reservieren.

- +
diff --git a/src/app/reservation-success/reservation-success.component.html b/src/app/reservation-success/reservation-success.component.html index 0a463a0..8d28e59 100644 --- a/src/app/reservation-success/reservation-success.component.html +++ b/src/app/reservation-success/reservation-success.component.html @@ -1,17 +1,15 @@

Reservierung erfolgreich!

-

Ihre Sitzplätze wurden erfolgreich reserviert. Bitte nennen sie den folgenden Code an der Kasse, um Ihre Reservierung in eine Buchung umzuwandeln.

{{ order().code }}
- - -
+ + +
Reservierung stornieren
- diff --git a/src/app/theater-layout/theater-layout.component.html b/src/app/theater-layout/theater-layout.component.html index 5637093..0563b21 100644 --- a/src/app/theater-layout/theater-layout.component.html +++ b/src/app/theater-layout/theater-layout.component.html @@ -3,12 +3,12 @@ Leinwand

-
+
@for (row of seatsPerRow(); track $index) {
-
+
@if ($index % 4 === 0) { speaker @@ -25,7 +25,7 @@ -
+
@if ($index % 4 === 0) { speaker diff --git a/src/app/theater-overlay/theater-overlay.component.html b/src/app/theater-overlay/theater-overlay.component.html index 7a88cd2..78bd5b8 100644 --- a/src/app/theater-overlay/theater-overlay.component.html +++ b/src/app/theater-overlay/theater-overlay.component.html @@ -1,8 +1,8 @@ -
+
-
+
@if (!performance && (loading.loading$ | async)){
@@ -18,6 +18,6 @@
- +
diff --git a/src/app/theater-overlay/theater-overlay.component.ts b/src/app/theater-overlay/theater-overlay.component.ts index 68621eb..f588044 100644 --- a/src/app/theater-overlay/theater-overlay.component.ts +++ b/src/app/theater-overlay/theater-overlay.component.ts @@ -25,6 +25,7 @@ export class TheaterOverlayComponent implements OnInit, OnDestroy { readonly loading = inject(LoadingService); showId!: number; + orderId?: string; seatsPerRow = signal<{ seat: Sitzplatz | null, state: TheaterSeatState | null }[][]>([]); performance: Vorstellung | undefined; seatCategories: Sitzkategorie[] = []; @@ -33,7 +34,8 @@ export class TheaterOverlayComponent implements OnInit, OnDestroy { private isInitialLoad = signal(true); ngOnInit() { - this.showId = Number(this.route.snapshot.paramMap.get('id')!); + this.showId = Number(this.route.snapshot.paramMap.get('performanceId')!); + this.orderId = this.route.snapshot.queryParams['paramName']; this.selectedSeatService.clearSelection(); this.selectedSeatService.setSeatSelectable(true); From f4eb700ab49148ca050fbbc823c43890a39bd316 Mon Sep 17 00:00:00 2001 From: Piet Ostendorp Date: Tue, 18 Nov 2025 23:00:18 +0100 Subject: [PATCH 07/15] Add zoom and device detection with warning component Introduces DeviceDetectionService and ZoomDetectionService to detect mobile devices and browser zoom level. Adds ZoomWarningComponent to display warnings for unsupported mobile devices and non-optimal browser zoom, and integrates it into the app layout. Updates routing to allow mobile access for the 'poc-model' route. --- src/app/app-module.ts | 2 + src/app/app-routing-module.ts | 2 +- src/app/app.html | 1 + src/app/device-detection.service.ts | 29 +++++ src/app/zoom-detection.service.ts | 33 ++++++ .../zoom-warning/zoom-warning.component.css | 14 +++ .../zoom-warning/zoom-warning.component.html | 81 ++++++++++++++ .../zoom-warning/zoom-warning.component.ts | 100 ++++++++++++++++++ 8 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 src/app/device-detection.service.ts create mode 100644 src/app/zoom-detection.service.ts create mode 100644 src/app/zoom-warning/zoom-warning.component.css create mode 100644 src/app/zoom-warning/zoom-warning.component.html create mode 100644 src/app/zoom-warning/zoom-warning.component.ts diff --git a/src/app/app-module.ts b/src/app/app-module.ts index 2172c39..2794819 100644 --- a/src/app/app-module.ts +++ b/src/app/app-module.ts @@ -64,6 +64,7 @@ import { PurchaseSuccessComponent } from './purchase-success/purchase-success.co import { PurchaseFailedComponent } from './purchase-failed/purchase-failed.component'; import { TicketSmallComponent } from './ticket-small/ticket-small.component'; import { TicketListComponent } from './ticket-list/ticket-list.component'; +import { ZoomWarningComponent } from './zoom-warning/zoom-warning.component'; @NgModule({ @@ -107,6 +108,7 @@ import { TicketListComponent } from './ticket-list/ticket-list.component'; PurchaseFailedComponent, TicketSmallComponent, TicketListComponent, + ZoomWarningComponent, ], imports: [ AppRoutingModule, diff --git a/src/app/app-routing-module.ts b/src/app/app-routing-module.ts index 6f48c5e..e5692a4 100644 --- a/src/app/app-routing-module.ts +++ b/src/app/app-routing-module.ts @@ -12,7 +12,7 @@ import { AuthGuard } from './auth.guard'; const routes: Routes = [ // Seiten ohne Layout { path: 'landing', component: HomeComponent }, - { path: 'poc-model', component: PocModelComponent }, + { path: 'poc-model', component: PocModelComponent, data: { allowMobile: true } }, // Seiten mit MainLayout { diff --git a/src/app/app.html b/src/app/app.html index 67e7bd4..bc230ce 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -1 +1,2 @@ + diff --git a/src/app/device-detection.service.ts b/src/app/device-detection.service.ts new file mode 100644 index 0000000..c4035ea --- /dev/null +++ b/src/app/device-detection.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class DeviceDetectionService { + private _isMobile: boolean; + + constructor() { + this._isMobile = this.checkIfMobile(); + } + + isMobile(): boolean { + return this._isMobile; + } + + private checkIfMobile(): boolean { + const userAgent = navigator.userAgent.toLowerCase(); + const isMobileUA = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent); + const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0; + const isSmallScreen = window.innerWidth < 768; + + return isMobileUA || (isTouchDevice && isSmallScreen); + } + + recheckDevice(): void { + this._isMobile = this.checkIfMobile(); + } +} diff --git a/src/app/zoom-detection.service.ts b/src/app/zoom-detection.service.ts new file mode 100644 index 0000000..d9e3311 --- /dev/null +++ b/src/app/zoom-detection.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, fromEvent } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class ZoomDetectionService { + private zoomLevel$ = new BehaviorSubject(this.getZoomLevel()); + + constructor() { + // Zoom-Änderungen überwachen + fromEvent(window, 'resize') + .pipe(debounceTime(200)) + .subscribe(() => { + this.zoomLevel$.next(this.getZoomLevel()); + }); + } + + getZoomLevel(): number { + const devicePixelRatio = window.devicePixelRatio || 1; + return devicePixelRatio; + } + + getZoomLevel$() { + return this.zoomLevel$.asObservable(); + } + + isZoomOutOfRange(minZoom: number = 0.95, maxZoom: number = 1.05): boolean { + const currentZoom = this.getZoomLevel(); + return currentZoom < minZoom || currentZoom > maxZoom; + } +} diff --git a/src/app/zoom-warning/zoom-warning.component.css b/src/app/zoom-warning/zoom-warning.component.css new file mode 100644 index 0000000..21fcb9c --- /dev/null +++ b/src/app/zoom-warning/zoom-warning.component.css @@ -0,0 +1,14 @@ +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(20px) scale(0.9); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.zoom-info-box { + animation: slideIn 0.3s ease-out; +} diff --git a/src/app/zoom-warning/zoom-warning.component.html b/src/app/zoom-warning/zoom-warning.component.html new file mode 100644 index 0000000..ea79125 --- /dev/null +++ b/src/app/zoom-warning/zoom-warning.component.html @@ -0,0 +1,81 @@ +@if (isOutOfRange && !isDismissed && !isMobile) { +
+ + +
+ + screenshot_monitor + + + warning + +
+ +

Browser-Zoom nicht optimal!

+

Ihr Browser-Zoom ist auf {{ currentZoomPercentage }}% eingestellt.
Für die beste Darstellung empfehlen wir 100%.

+ +
+
+

Browser-Zoom zurücksetzen:

+
+

+ Strg + / + + + + 0 + (Windows, Linux / Mac) +

+
+
+ +
+

Windows-Skalierung prüfen:

+
    +
  1. Öffnen Sie die Windows-Einstellungen
  2. +
  3. Navigieren Sie zu SystemBildschirm
  4. +
  5. Setzen Sie unter "Skalierung" den Wert auf 100%
  6. +
+
+
+
+} + +@if (showMobileWarning) { +
+
+ +

InfiniMotion

+
+
+
+
+
+ + screenshot_monitor + + + warning + +
+ +

Nur am PC verfügbar

+ +

+ Diese Anwendung ist für die Nutzung am Desktop-PC optimiert und kann auf mobilen Geräten nicht verwendet werden. +

+ +
+

Deine derzeitige URL:

+

+ {{ currentUrl }} +

+
+
+
+} diff --git a/src/app/zoom-warning/zoom-warning.component.ts b/src/app/zoom-warning/zoom-warning.component.ts new file mode 100644 index 0000000..8802167 --- /dev/null +++ b/src/app/zoom-warning/zoom-warning.component.ts @@ -0,0 +1,100 @@ +import { DeviceDetectionService } from './../device-detection.service'; +import { Component, HostListener, inject, OnDestroy, OnInit } from '@angular/core'; +import { filter, Subject, takeUntil } from 'rxjs'; +import { ZoomDetectionService } from '../zoom-detection.service'; +import { NavigationEnd, Router } from '@angular/router'; + +@Component({ + selector: 'app-zoom-warning', + standalone: false, + templateUrl: './zoom-warning.component.html', + styleUrl: './zoom-warning.component.css', +}) +export class ZoomWarningComponent implements OnInit, OnDestroy { + currentZoomPercentage = 100; + isOutOfRange = false; + isDismissed = false; + isMobile = false; + showMobileWarning = false; + currentUrl = ''; + + private destroy$ = new Subject(); + private currentZoomLevel = 1; + private lastZoomLevel = 0; + + zoomDetectionService = inject(ZoomDetectionService); + deviceDetectionService = inject(DeviceDetectionService) + + constructor(private router: Router) { + this.isMobile = this.deviceDetectionService.isMobile(); + this.currentUrl = window.location.href; + this.checkIfShouldShowMobileWarning(); + } + + ngOnInit() { + this.router.events + .pipe( + filter(event => event instanceof NavigationEnd), + takeUntil(this.destroy$) + ) + .subscribe(() => { + this.checkIfShouldShowMobileWarning(); + }); + + if (!this.isMobile) { + this.updateZoomInfo(); + + this.zoomDetectionService.getZoomLevel$() + .pipe(takeUntil(this.destroy$)) + .subscribe((newZoomLevel) => { + if (Math.abs(newZoomLevel - this.lastZoomLevel) > 0.01) { + this.lastZoomLevel = newZoomLevel; + this.isDismissed = false; + this.updateZoomInfo(); + } + }); + } + } + + private checkIfShouldShowMobileWarning() { + if (!this.isMobile) { + this.showMobileWarning = false; + return; + } + + const currentRoute = this.router.routerState.root; + const allowMobile = this.getRouteData(currentRoute, 'allowMobile'); + + this.showMobileWarning = !allowMobile; + } + + private getRouteData(route: any, key: string): any { + while (route) { + if (route.snapshot?.data?.[key] !== undefined) { + return route.snapshot.data[key]; + } + route = route.firstChild; + } + return false; + } + + private updateZoomInfo() { + this.currentZoomLevel = this.zoomDetectionService.getZoomLevel(); + this.currentZoomPercentage = Math.round(this.currentZoomLevel * 100); + this.isOutOfRange = this.zoomDetectionService.isZoomOutOfRange(); + } + + getCompensationTransform(): string { + const scale = 1 / this.currentZoomLevel; + return `scale(${scale})`; + } + + dismissWarning() { + this.isDismissed = true; + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } +} From 26f2dd21652b2f02b9cdd7b8d2496f2b7c0980fd Mon Sep 17 00:00:00 2001 From: Marcel-Anker Date: Wed, 19 Nov 2025 11:03:29 +0100 Subject: [PATCH 08/15] removed redundant ; --- src/app/order/order.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/order/order.component.ts b/src/app/order/order.component.ts index 84aa2d0..868029e 100644 --- a/src/app/order/order.component.ts +++ b/src/app/order/order.component.ts @@ -164,7 +164,7 @@ export class OrderComponent { // Tickets anlegen const tickets = seats.map(seat => { - return this.generateNewTicketObject(performance, seat, order);; + return this.generateNewTicketObject(performance, seat, order); }); // Transaktionssicher Sitzplatzbuchung From 172b0af9abd7257f1fde85966e10cca9109f9840 Mon Sep 17 00:00:00 2001 From: Marcel-Anker Date: Wed, 19 Nov 2025 11:04:20 +0100 Subject: [PATCH 09/15] add api methods for getting statistics data for movies and shows --- src/app/http.service.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/app/http.service.ts b/src/app/http.service.ts index 3a563ec..57c3684 100644 --- a/src/app/http.service.ts +++ b/src/app/http.service.ts @@ -6,7 +6,7 @@ import { OmdbSearch, Bestellung, Eintrittskarte, - StatisticsReduced, StatisticsFilm + StatisticsFilm, StatisticsVorstellung } from '@infinimotion/model-frontend'; import { HttpClient } from "@angular/common/http"; import { inject, Injectable } from "@angular/core"; @@ -195,8 +195,13 @@ export class HttpService { /* Statistics APIs */ - /* GET /api/statistics/list */ - getStatisticsList(): Observable { - return this.http.get(`${this.baseUrl}statistics/list`) + /* GET /api/statistics/movies */ + getMovieStatistics(): Observable { + return this.http.get(`${this.baseUrl}statistics/movies`) + } + + /* GET /api/statistics/shows */ + getShowStatistics(): Observable { + return this.http.get(`${this.baseUrl}statistics/shows`) } } From 0aca7ace80ca041789f0bda0832da4fa76b5494c Mon Sep 17 00:00:00 2001 From: Marcel-Anker Date: Wed, 19 Nov 2025 11:04:31 +0100 Subject: [PATCH 10/15] npm install --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1d2ab91..7e96782 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.110", + "@infinimotion/model-frontend": "^0.0.116", "@tailwindcss/postcss": "^4.1.14", "angularx-qrcode": "^20.0.0", "canvas-confetti": "^1.9.4", @@ -1395,9 +1395,9 @@ } }, "node_modules/@infinimotion/model-frontend": { - "version": "0.0.110", - "resolved": "https://git.infinimotion.de/api/packages/infinimotion/npm/%40infinimotion%2Fmodel-frontend/-/0.0.110/model-frontend-0.0.110.tgz", - "integrity": "sha512-MzHFDV8g2nOQ3p9UysB9HfoFzmycXIIaccN2do3gqIM9lkwsiN/htoGHr8fza9uv+OACifrDt4xp73nSy2Xf8w==", + "version": "0.0.116", + "resolved": "https://git.infinimotion.de/api/packages/infinimotion/npm/%40infinimotion%2Fmodel-frontend/-/0.0.116/model-frontend-0.0.116.tgz", + "integrity": "sha512-kGnZW1klIHzdL/44fOEUrDTVSkQSxErgHqwuSb6eQDLUW7Q9ZDL389LFR2haJCpqcYWIjsG2HjpvkJwrm2ctpA==", "license": "ISC" }, "node_modules/@inquirer/ansi": { diff --git a/package.json b/package.json index 47a9dd6..594376e 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.110", + "@infinimotion/model-frontend": "^0.0.116", "@tailwindcss/postcss": "^4.1.14", "angularx-qrcode": "^20.0.0", "canvas-confetti": "^1.9.4", From 8ca531779566868240a850cd6e68403bdf86edfc Mon Sep 17 00:00:00 2001 From: Marcel-Anker Date: Wed, 19 Nov 2025 11:07:47 +0100 Subject: [PATCH 11/15] loading data from api asynchronously and dynamically setting page number. loading onInit --- src/app/statistics/statistics.component.ts | 49 ++++++++++++++-------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/app/statistics/statistics.component.ts b/src/app/statistics/statistics.component.ts index fa82c7f..2c7b086 100644 --- a/src/app/statistics/statistics.component.ts +++ b/src/app/statistics/statistics.component.ts @@ -1,13 +1,11 @@ import {Component, inject} from '@angular/core'; import {HttpService} from '../http.service'; import { - Eintrittskarte, - Film, StatisticsFilm, - StatisticsFilmReduced, - StatisticsReduced, StatisticsVorstellungReduced, - Vorstellung + StatisticsVorstellung, } from '@infinimotion/model-frontend'; +import {LoadingService} from '../loading.service'; +import {firstValueFrom, forkJoin} from 'rxjs'; @Component({ selector: 'app-statistics', @@ -17,22 +15,37 @@ import { }) export class StatisticsComponent { private http = inject(HttpService); - private movies: StatisticsFilmReduced[] = [] - private shows: StatisticsVorstellungReduced[] = [] + protected movies: StatisticsFilm[] = []; + protected shows: StatisticsVorstellung[] = []; + protected moviesDisplayedColumns: string[] = ['id', 'title', 'earnings', 'tickets']; + protected showsDisplayedColumns: string[] = ['id', 'hall', 'movie_title', 'date', 'earnings', 'tickets']; + protected movieResultsLength: number = 0; + protected showsResultLength: number = 0; + + private loading = inject(LoadingService); ngOnInit(): void { - this.loadData(); + this.loading.show() + this.loadData().then(); } - loadData() { - this.http.getStatisticsList().subscribe({ - next: (response) => { - this.movies = response.movies; - this.shows = response.shows; - console.log(this.movies) - console.log(this.shows) - }, - error: (err) => console.error('Fehler beim Laden der Statistiken', err), - }); + async loadData() { + let movieRequest = this.http.getMovieStatistics(); + let showRequest = this.http.getShowStatistics(); + let movieResponse = await firstValueFrom(movieRequest); + let showResponse = await firstValueFrom(showRequest); + this.movies = movieResponse + this.shows = showResponse + if (this.movies.length / 30 < 1) { + this.movieResultsLength = 1; + } else { + this.movieResultsLength = Math.ceil(this.movies.length / 30); + } + if (this.shows.length / 30 < 1) { + this.showsResultLength = 1; + } else { + this.showsResultLength = Math.ceil(this.shows.length / 30); + } + this.loading.hide(); } } From ecb31432e11a38368607b15038b9f373bba01c2f Mon Sep 17 00:00:00 2001 From: Marcel-Anker Date: Wed, 19 Nov 2025 11:08:04 +0100 Subject: [PATCH 12/15] add helperfunction for date formating --- src/app/statistics/statistics.component.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/statistics/statistics.component.ts b/src/app/statistics/statistics.component.ts index 2c7b086..acc4b38 100644 --- a/src/app/statistics/statistics.component.ts +++ b/src/app/statistics/statistics.component.ts @@ -48,4 +48,9 @@ export class StatisticsComponent { } this.loading.hide(); } + + formatDate(date: Date) { + return new Date(date).toLocaleString("de"); + } + } From 3587af0e70dadf7bc39eca3738c78acacdd619e5 Mon Sep 17 00:00:00 2001 From: Marcel-Anker Date: Wed, 19 Nov 2025 11:08:34 +0100 Subject: [PATCH 13/15] add two tables for statistics for movies and shows --- src/app/app-module.ts | 5 ++ src/app/statistics/statistics.component.html | 91 ++++++++++++++++++-- 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/src/app/app-module.ts b/src/app/app-module.ts index a2cab07..c0e7b03 100644 --- a/src/app/app-module.ts +++ b/src/app/app-module.ts @@ -25,6 +25,8 @@ 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 { MatPaginatorModule } from '@angular/material/paginator'; +import { MatTableModule } from '@angular/material/table'; import { HeaderComponent } from './header/header.component'; import { HomeComponent } from './home/home.component'; @@ -67,6 +69,7 @@ import { TicketListComponent } from './ticket-list/ticket-list.component'; import { StatisticsComponent } from './statistics/statistics.component'; + @NgModule({ declarations: [ App, @@ -139,6 +142,8 @@ import { StatisticsComponent } from './statistics/statistics.component'; QRCodeComponent, MatBadgeModule, MatTooltipModule, + MatPaginatorModule, + MatTableModule, ], providers: [ provideBrowserGlobalErrorListeners(), diff --git a/src/app/statistics/statistics.component.html b/src/app/statistics/statistics.component.html index 4dd5f10..4ab7190 100644 --- a/src/app/statistics/statistics.component.html +++ b/src/app/statistics/statistics.component.html @@ -1,6 +1,85 @@ - + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ID{{row.movieId}}Titel{{row.movieTitle}} + Umsatz + {{(row.earnings/100).toFixed(2)}} € + Gebuchte Tickets + {{row.tickets}}
+
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID{{row.showId}}Kinosaal{{row.showHallName}}Film Name{{row.movieTitle}}Datum{{formatDate(row.showStart)}} + Umsatz + {{(row.earnings/100).toFixed(2)}} € + Gebuchte Tickets + {{row.tickets}}
+
+ + + From cdf45876be120db373b21526b1530ca679208abe Mon Sep 17 00:00:00 2001 From: Marcel-Anker Date: Wed, 19 Nov 2025 11:26:01 +0100 Subject: [PATCH 14/15] =?UTF-8?q?merge=20upsi=20fix=20=F0=9F=A6=8F?= =?UTF-8?q?=F0=9F=8D=86=F0=9F=A4=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app-module.ts | 4 ++++ src/app/app-routing-module.ts | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/app/app-module.ts b/src/app/app-module.ts index 286e276..2164bb3 100644 --- a/src/app/app-module.ts +++ b/src/app/app-module.ts @@ -66,6 +66,8 @@ import { PurchaseSuccessComponent } from './purchase-success/purchase-success.co import { PurchaseFailedComponent } from './purchase-failed/purchase-failed.component'; import { TicketSmallComponent } from './ticket-small/ticket-small.component'; import { TicketListComponent } from './ticket-list/ticket-list.component'; +import { StatisticsComponent } from './statistics/statistics.component'; +import { ZoomWarningComponent } from './zoom-warning/zoom-warning.component'; @NgModule({ @@ -109,6 +111,8 @@ import { TicketListComponent } from './ticket-list/ticket-list.component'; PurchaseFailedComponent, TicketSmallComponent, TicketListComponent, + StatisticsComponent, + ZoomWarningComponent, ], imports: [ AppRoutingModule, diff --git a/src/app/app-routing-module.ts b/src/app/app-routing-module.ts index 2c6fe8a..83cb4e7 100644 --- a/src/app/app-routing-module.ts +++ b/src/app/app-routing-module.ts @@ -28,7 +28,13 @@ const routes: Routes = [ canActivate: [AuthGuard], data: { roles: ['admin'] }, // Array von erlaubten Rollen. Derzeit gäbe es 'admin' und 'employee' }, - { path: 'selection/performance/:id', component: TheaterOverlayComponent}, + { path: 'performance/:performanceId/checkout', component: TheaterOverlayComponent}, + { + path: 'admin/statistics', + component: StatisticsComponent, + canActivate: [AuthGuard], + data: { roles: ['admin'] }, // Array von erlaubten Rollen. Derzeit gäbe es 'admin' und 'employee' + }, ], }, From 1ef223c19928f832b0792677e5c184a123428a1b Mon Sep 17 00:00:00 2001 From: Lennart Heinrich Date: Wed, 19 Nov 2025 11:33:59 +0100 Subject: [PATCH 15/15] increase angular size budget --- angular.json | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/angular.json b/angular.json index 735b501..66beaba 100644 --- a/angular.json +++ b/angular.json @@ -31,9 +31,7 @@ "builder": "@angular/build:application", "options": { "browser": "src/main.ts", - "polyfills": [ - "zone.js" - ], + "polyfills": ["zone.js"], "tsConfig": "tsconfig.app.json", "inlineStyleLanguage": "css", "assets": [ @@ -47,23 +45,20 @@ "output": "assets" } ], - "styles": [ - "src/custom-theme.scss", - "src/styles.css" - ] + "styles": ["src/custom-theme.scss", "src/styles.css"] }, "configurations": { "production": { "budgets": [ { "type": "initial", - "maximumWarning": "500kB", - "maximumError": "1MB" + "maximumWarning": "5MB", + "maximumError": "10MB" }, { "type": "anyComponentStyle", - "maximumWarning": "4kB", - "maximumError": "8kB" + "maximumWarning": "100kB", + "maximumError": "500kB" } ], "outputHashing": "all" @@ -94,10 +89,7 @@ "test": { "builder": "@angular/build:karma", "options": { - "polyfills": [ - "zone.js", - "zone.js/testing" - ], + "polyfills": ["zone.js", "zone.js/testing"], "tsConfig": "tsconfig.spec.json", "inlineStyleLanguage": "css", "assets": [ @@ -106,9 +98,7 @@ "input": "public" } ], - "styles": [ - "src/styles.css" - ] + "styles": ["src/styles.css"] } } }