import { ChangeDetectionStrategy, Component, DestroyRef, inject, signal } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { HttpErrorResponse } from '@angular/common/http'; import { ActivatedRoute, RouterLink } from '@angular/router'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { ReservationConfirmResponse, ShowsApiService } from '../services/shows-api.service'; type ConfirmationState = 'loading' | 'success' | 'invalid' | 'expired' | 'error'; @Component({ standalone: true, imports: [ RouterLink, MatButtonModule, MatCardModule, MatIconModule, MatProgressSpinnerModule, ], template: `
@if (state() === 'loading') {

Confirming reservation...

Please wait while we validate your link.

} @if (state() === 'success' && confirmation()) {
check_circle

Reservation confirmed

Your seats are confirmed. Present this QR code at check-in.

@if (confirmation()!.qr_code_image) {
Reservation QR code
} @if (confirmation()!.qr_code_url) {

Check-in URL: {{ confirmation()!.qr_code_url }}

} } @if (state() === 'invalid') {
error

Invalid confirmation link

This token is not valid. Please use the latest email confirmation link.

} @if (state() === 'expired') {
schedule

Confirmation link expired

This link has expired. Please create a new reservation.

} @if (state() === 'error') {
warning

Could not confirm reservation

Please try again in a moment.

}
Home Shows
`, styles: [` .page { max-width: 760px; margin: 0 auto; } .page-header { margin-bottom: 24px; } .eyebrow { margin: 0 0 10px; color: var(--azionelab-accent); text-transform: uppercase; font-size: 0.78rem; font-weight: 700; } h1 { margin: 0; font-size: clamp(2rem, 4vw, 3rem); } .status-card { border-radius: 8px; border: 1px solid var(--azionelab-border); background: var(--azionelab-surface); box-shadow: var(--azionelab-shadow); } .status-copy { display: flex; align-items: flex-start; gap: 14px; } .status-copy h2 { margin: 0 0 6px; font-size: 1.2rem; } .status-copy p { margin: 0; color: var(--azionelab-muted); line-height: 1.5; } .status-copy.success mat-icon { color: #2e7d32; } .qr-panel { margin-top: 18px; padding: 14px; border-radius: 8px; border: 1px solid var(--azionelab-border); display: inline-block; background: white; } .qr-panel img { width: min(280px, 100%); height: auto; display: block; } .meta { margin: 14px 0 0; word-break: break-word; color: var(--azionelab-muted); } `], changeDetection: ChangeDetectionStrategy.OnPush, }) export class ReservationConfirmPageComponent { private readonly destroyRef = inject(DestroyRef); private readonly route = inject(ActivatedRoute); private readonly showsApi = inject(ShowsApiService); protected readonly state = signal('loading'); protected readonly confirmation = signal(null); constructor() { const token = this.route.snapshot.queryParamMap.get('token')?.trim() ?? ''; if (!token) { this.state.set('invalid'); return; } this.showsApi.confirmReservation(token) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: (response) => { this.confirmation.set(response); this.state.set('success'); }, error: (error: HttpErrorResponse) => { if (error.status === 404 || error.status === 400) { this.state.set('invalid'); return; } if (error.status === 410) { this.state.set('expired'); return; } this.state.set('error'); }, }); } }