From 15a74bb56ec8d11acb5daa875cdd3f200c7de18b Mon Sep 17 00:00:00 2001 From: ncoronel Date: Tue, 28 Jul 2026 16:26:45 -0300 Subject: [PATCH] feat: enhance purchase status page with ticket handling and WhatsApp integration --- src/app/core/services/checkout.service.ts | 2 + .../purchase-status-page.component.html | 90 +++++++----- .../purchase-status-page.component.scss | 2 +- .../purchase-status-page.component.spec.ts | 130 ++++++++++++++++++ .../purchase-status-page.component.ts | 34 ++++- 5 files changed, 216 insertions(+), 42 deletions(-) create mode 100644 src/app/features/store/pages/purchase-status-page/purchase-status-page.component.spec.ts diff --git a/src/app/core/services/checkout.service.ts b/src/app/core/services/checkout.service.ts index ab22b4e..1b1accd 100644 --- a/src/app/core/services/checkout.service.ts +++ b/src/app/core/services/checkout.service.ts @@ -68,6 +68,8 @@ export interface PurchaseDetailResponse extends PurchaseStatusResponse { email: string | null; items_source: 'purchase'; items: PurchaseDetailItemResponse[]; + tickets_count?: number; + has_generated_tickets?: boolean; subtotal: string; total: string; } diff --git a/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.html b/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.html index 7953c02..9018c1f 100644 --- a/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.html +++ b/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.html @@ -16,18 +16,34 @@
-

Comunicate con nosotros para coordinar el envío.

+ @if (ticketsRoute()) { +

A continuación, vas a poder ver los tickets que debés presentar en el evento.

- - - WhatsApp - + + Mis tickets + + } @else if (whatsappUrl()) { +

Comunicate con nosotros para coordinar el envío.

+ + + + WhatsApp + + } @else { +

Tu compra quedó registrada correctamente.

+ }
} @else if (status() === 'pending') {
@@ -71,18 +87,20 @@

Si ya pagaste, escribinos para que podamos revisarlo.

-
- - - WhatsApp - -
+ @if (whatsappUrl()) { +
+ + + WhatsApp + +
+ }
} @else {
@@ -98,18 +116,20 @@

Volvé a ingresar más tarde. Si el problema sigue, comunicate con nosotros.

-
- - - WhatsApp - -
+ @if (whatsappUrl()) { +
+ + + WhatsApp + +
+ }
}
diff --git a/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.scss b/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.scss index b77c4c2..ebf9bed 100644 --- a/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.scss +++ b/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.scss @@ -98,7 +98,7 @@ font-size: 13px; font-weight: 325; line-height: 1.45; - color: var(--color-text); + color: #666666; } &__hint { diff --git a/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.spec.ts b/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.spec.ts new file mode 100644 index 0000000..420a0e4 --- /dev/null +++ b/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.spec.ts @@ -0,0 +1,130 @@ +import { TestBed } from '@angular/core/testing'; +import { ActivatedRoute, convertToParamMap, Router } from '@angular/router'; + +import { CheckoutService, PurchaseDetailResponse } from '../../../../core/services/checkout.service'; +import { Tenant } from '../../../../core/services/tenant.interface'; +import { TenantService } from '../../../../core/services/tenant.service'; +import { PurchaseStatusPageComponent } from './purchase-status-page.component'; + +const tenant: Tenant = { + id: 1, + codigo: 'tickets-tenant', + nombre: 'Tickets tenant', + dominio: 'tickets.local', + primary_color: '#000000', + secondary_color: '#000000', + danger_color: '#000000', + success_color: '#000000', + header_bg_color: '#000000', + footer_bg_color: '#000000', + header_logo: '', + footer_logo: '', + categories: [], + social_media: [ + { + code: 'whatsapp', + icon: 'whatsapp', + name: 'WhatsApp', + url: 'https://wa.me/543411234567', + }, + ], + menues: [ + { + id: 1, + code: 'account', + label: 'Mi cuenta', + parent_menu_code: null, + content_type: 'dynamic', + route: '/mi-cuenta', + submenues: [ + { + id: 2, + code: 'account.tickets', + label: 'Mis tickets', + parent_menu_code: 'account', + content_type: 'dynamic', + route: '/mi-cuenta/tickets', + submenues: [], + }, + ], + }, + ], +}; + +const purchase = (hasGeneratedTickets: boolean): PurchaseDetailResponse => + ({ + status: 'paid', + has_generated_tickets: hasGeneratedTickets, + tickets_count: hasGeneratedTickets ? 1 : 0, + }) as PurchaseDetailResponse; + +describe('PurchaseStatusPageComponent', () => { + beforeEach(() => { + TestBed.resetTestingModule(); + }); + + async function render(hasGeneratedTickets: boolean) { + const checkoutService = { + getPurchase: vi.fn().mockResolvedValue(purchase(hasGeneratedTickets)), + }; + const router = { + navigate: vi.fn().mockResolvedValue(true), + navigateByUrl: vi.fn().mockResolvedValue(true), + }; + + await TestBed.configureTestingModule({ + imports: [PurchaseStatusPageComponent], + providers: [ + { provide: CheckoutService, useValue: checkoutService }, + { provide: TenantService, useValue: { tenant: () => tenant } }, + { + provide: ActivatedRoute, + useValue: { snapshot: { paramMap: convertToParamMap({ id: '42' }) } }, + }, + { provide: Router, useValue: router }, + ], + }).compileComponents(); + + const fixture = TestBed.createComponent(PurchaseStatusPageComponent); + fixture.detectChanges(); + await fixture.whenStable(); + fixture.detectChanges(); + + return { + fixture, + element: fixture.nativeElement as HTMLElement, + checkoutService, + router, + }; + } + + it('shows the tickets action when this purchase generated tickets', async () => { + const { element, checkoutService, router } = await render(true); + + expect(checkoutService.getPurchase).toHaveBeenCalledOnce(); + expect(element.textContent).toContain('Ver mis tickets'); + expect(element.textContent).not.toContain('WhatsApp'); + + element.querySelector('app-button button')?.click(); + + expect(router.navigateByUrl).toHaveBeenCalledWith('/mi-cuenta/tickets'); + }); + + it('shows the tenant WhatsApp action when this purchase did not generate tickets', async () => { + const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null); + const { element } = await render(false); + + expect(element.textContent).toContain('WhatsApp'); + expect(element.textContent).not.toContain('Ver mis tickets'); + + element.querySelector('app-button button')?.click(); + + expect(openSpy).toHaveBeenCalledWith( + 'https://wa.me/543411234567', + '_blank', + 'noopener,noreferrer', + ); + + openSpy.mockRestore(); + }); +}); diff --git a/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.ts b/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.ts index a36db90..f3ad371 100644 --- a/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.ts +++ b/src/app/features/store/pages/purchase-status-page/purchase-status-page.component.ts @@ -1,7 +1,8 @@ -import { ChangeDetectionStrategy, Component, OnInit, inject, signal } from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, computed, inject, signal } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { CheckoutService, PurchaseStatusResponse } from '../../../../core/services/checkout.service'; +import { findMenu } from '../../../../core/services/menu.utils'; import { TenantService } from '../../../../core/services/tenant.service'; import { ButtonComponent } from '../../../../shared/components/button/button.component'; @@ -26,6 +27,20 @@ export class PurchaseStatusPageComponent implements OnInit { protected readonly isLoading = signal(true); protected readonly status = signal('pending'); + protected readonly hasGeneratedTickets = signal(false); + protected readonly ticketsRoute = computed(() => { + if (!this.hasGeneratedTickets()) { + return null; + } + + return findMenu(this.tenantService.tenant()?.menues ?? [], 'account.tickets')?.route ?? null; + }); + protected readonly whatsappUrl = computed( + () => + this.tenantService + .tenant() + ?.social_media?.find((socialMedia) => socialMedia.code === 'whatsapp')?.url ?? null, + ); ngOnInit(): void { const purchaseId = this.route.snapshot.paramMap.get('id'); @@ -50,6 +65,7 @@ export class PurchaseStatusPageComponent implements OnInit { try { const purchase = await this.checkoutService.getPurchase(this.tenantCode, this.purchaseId); this.status.set(this.resolveStatus(purchase)); + this.hasGeneratedTickets.set(purchase.has_generated_tickets === true); } catch (error) { console.error('Failed to fetch purchase status:', error); this.status.set('error'); @@ -74,13 +90,19 @@ export class PurchaseStatusPageComponent implements OnInit { return 'pending'; } - protected getWhatsAppLink(): string { - const phone = '5493416658247'; - const message = encodeURIComponent('Hola! Mi compra fue realizada con \u00E9xito.'); - return `https://wa.me/${phone}?text=${message}`; + protected goToTickets(): void { + const route = this.ticketsRoute(); + + if (route) { + void this.router.navigateByUrl(route); + } } protected openWhatsApp(): void { - window.open(this.getWhatsAppLink(), '_blank', 'noopener,noreferrer'); + const url = this.whatsappUrl(); + + if (url) { + window.open(url, '_blank', 'noopener,noreferrer'); + } } }