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') {

Stiamo completando la tua conferma...

Un attimo ancora, stiamo verificando il link ricevuto via email.

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

I tuoi posti sono confermati

Perfetto: la prenotazione e' andata a buon fine. Tieni questo QR code a portata di mano e mostralo all'ingresso quando arrivi.

qr_code_2 QR pronto da mostrare theater_comedy Ti aspettiamo in sala
@if (confirmation()!.qr_code_image) {

Il tuo QR code di ingresso

QR code della prenotazione
} @if (confirmation()!.qr_code_url) {
link

Link di accesso: {{ confirmation()!.qr_code_url }}

}

Portalo con te

Conserva il QR code sul telefono oppure stampalo. All'ingresso bastera' mostrarlo allo staff.

Tieni l'email a portata di mano

Se ne avrai bisogno, potrai riaprire questa pagina in qualsiasi momento dal messaggio di conferma.

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

Link di conferma non valido

Questo link non risulta valido. Ti consigliamo di usare l'ultimo messaggio ricevuto via email.

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

Link di conferma scaduto

Il link che hai aperto non e' piu' attivo. Ti chiediamo di creare una nuova prenotazione.

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

Non siamo riusciti a completare la conferma

Riprova tra qualche istante: il tuo link potrebbe avere bisogno di un nuovo tentativo.

}
Torna agli spettacoli
`, styles: [` .confirmation-shell { width: min(100%, 700px); margin: 0 auto; } .page-header { margin-bottom: 28px; text-align: center; } h1 { margin: 0; font-size: 3rem; } .supporting { max-width: 40ch; margin: 16px auto 0; } .status-card { border-radius: var(--azionelab-radius-lg); border: 1px solid var(--azionelab-border); background: var(--azionelab-surface-strong); box-shadow: var(--azionelab-shadow); overflow: hidden; } mat-card-content { padding: 28px !important; } mat-card-actions { padding: 0 28px 28px !important; justify-content: center; } .status-panel { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 18px; padding: 24px; border-radius: 18px; border: 1px solid transparent; } .status-panel h2 { margin: 0 0 6px; } .status-panel p { margin: 0; color: var(--azionelab-muted); line-height: 1.5; } .status-panel.loading { background: rgba(159, 47, 40, 0.04); border-color: rgba(159, 47, 40, 0.1); } .status-panel.success { background: var(--azionelab-success-bg); border-color: var(--azionelab-success-border); } .status-panel.warning { background: #fff7ea; border-color: rgba(181, 126, 0, 0.15); } .status-panel.error { background: var(--azionelab-error-bg); border-color: var(--azionelab-error-border); } .status-icon { display: grid; place-items: center; width: 52px; height: 52px; border-radius: 16px; flex: 0 0 auto; background: rgba(30, 27, 24, 0.06); } .status-panel.success .status-icon { background: rgba(46, 125, 50, 0.12); } .status-panel.warning .status-icon { background: rgba(181, 126, 0, 0.14); } .status-panel.error .status-icon { background: rgba(179, 38, 30, 0.12); } .status-panel.success .status-icon mat-icon { color: var(--azionelab-success-ink); } .status-panel.warning .status-icon mat-icon { color: #9b6c00; } .status-panel.error .status-icon mat-icon { color: var(--azionelab-error-ink); } .success-points { display: flex; justify-content: center; flex-wrap: wrap; gap: 10px; margin-top: 14px; } .success-points span { display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px; border-radius: 999px; background: rgba(255, 255, 255, 0.72); color: var(--azionelab-success-ink); font-size: 0.92rem; } .success-points mat-icon { font-size: 18px; width: 18px; height: 18px; } .qr-panel { display: grid; justify-items: center; margin: 22px auto 0; padding: 22px; border-radius: var(--azionelab-radius-md); border: 1px solid var(--azionelab-border); background: #ffffff; width: min(100%, 360px); } .panel-label { margin: 0 0 12px; font-size: 0.88rem; font-weight: 700; color: var(--azionelab-muted); text-transform: uppercase; letter-spacing: 0.08em; } .qr-panel img { width: min(280px, 100%); height: auto; display: block; } .meta-card { display: flex; align-items: flex-start; gap: 10px; margin-top: 18px; padding: 14px 16px; border-radius: var(--azionelab-radius-md); background: var(--azionelab-bg-strong); color: var(--azionelab-muted); } .meta-card p { margin: 0; word-break: break-word; } .meta-card a { color: var(--azionelab-accent-strong); } .next-steps { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-top: 22px; } .next-steps > div { padding: 16px; border-radius: var(--azionelab-radius-md); background: rgba(34, 28, 24, 0.035); border: 1px solid var(--azionelab-border); } .step-label { margin: 0 0 6px !important; font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--azionelab-accent); } .next-steps p { margin: 0; line-height: 1.55; color: var(--azionelab-muted); } @media (max-width: 640px) { h1 { font-size: 2.3rem; } mat-card-content { padding: 22px !important; } mat-card-actions { padding: 0 22px 20px !important; } .status-panel { padding: 18px; border-radius: 16px; } .qr-panel { width: 100%; } .qr-panel img { width: min(100%, 280px); margin: 0 auto; } .next-steps { grid-template-columns: 1fr; } } `], 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'); }, }); } }