diff --git a/src/app/features/store/pages/account-page/pages/tickets-page/components/ticket-event-context/ticket-event-context.component.html b/src/app/features/store/pages/account-page/pages/tickets-page/components/ticket-event-context/ticket-event-context.component.html new file mode 100644 index 0000000..c290a5d --- /dev/null +++ b/src/app/features/store/pages/account-page/pages/tickets-page/components/ticket-event-context/ticket-event-context.component.html @@ -0,0 +1,9 @@ +
+ @if (event().image; as image) { + + } + +

+ {{ event().title }} +

+
diff --git a/src/app/features/store/pages/account-page/pages/tickets-page/components/ticket-event-context/ticket-event-context.component.scss b/src/app/features/store/pages/account-page/pages/tickets-page/components/ticket-event-context/ticket-event-context.component.scss new file mode 100644 index 0000000..8be7147 --- /dev/null +++ b/src/app/features/store/pages/account-page/pages/tickets-page/components/ticket-event-context/ticket-event-context.component.scss @@ -0,0 +1,24 @@ +:host { + display: block; +} + +.ticket-event-context { + padding-top: 1.5rem; + border-top: 1px solid var(--border-color); +} + +.ticket-event-context__image { + display: block; + width: 100%; + height: auto; + border-radius: 4px; +} + +.ticket-event-context__title { + margin: 1rem 0 0; + color: #666666; + font-size: 13px; + font-weight: 700; + line-height: 1.4; + text-transform: uppercase; +} diff --git a/src/app/features/store/pages/account-page/pages/tickets-page/components/ticket-event-context/ticket-event-context.component.ts b/src/app/features/store/pages/account-page/pages/tickets-page/components/ticket-event-context/ticket-event-context.component.ts new file mode 100644 index 0000000..d4df605 --- /dev/null +++ b/src/app/features/store/pages/account-page/pages/tickets-page/components/ticket-event-context/ticket-event-context.component.ts @@ -0,0 +1,15 @@ +import { Component, input } from '@angular/core'; + +export interface TicketEventContext { + title: string; + image: string | null; +} + +@Component({ + selector: 'app-ticket-event-context', + templateUrl: './ticket-event-context.component.html', + styleUrl: './ticket-event-context.component.scss', +}) +export class TicketEventContextComponent { + readonly event = input.required(); +} diff --git a/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.html b/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.html index 55f990b..953287e 100644 --- a/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.html +++ b/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.html @@ -109,3 +109,9 @@ } + +@if (eventId !== undefined) { + + + +} diff --git a/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.spec.ts b/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.spec.ts index 63223c7..c73cbd1 100644 --- a/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.spec.ts +++ b/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.spec.ts @@ -1,18 +1,22 @@ import { signal } from '@angular/core'; import { By } from '@angular/platform-browser'; import { TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { TenantService } from '../../../../../../core/services/tenant.service'; import { ToastService } from '../../../../../../core/services/toast.service'; import { ModalService } from '../../../../../../core/services/modal.service'; +import { MenuPageSidebarContentService } from '../../../../components/menu-page-layout/menu-page-sidebar-content.service'; import { TicketComponent } from './components/ticket/ticket.component'; import { TicketResponse, TicketService } from './ticket.service'; import { TicketsPage } from './tickets-page'; function withCustomLoading(service: T): T & { withCustomLoading: () => T } { - return Object.assign(service, { - withCustomLoading: () => service, + const enhancedService = Object.assign({ getTicketEvents: () => Promise.resolve([]) }, service); + + return Object.assign(enhancedService, { + withCustomLoading: () => enhancedService, }); } @@ -300,4 +304,69 @@ describe('TicketsPage', () => { ticket: 'ticket-1', }); }); + + it('declares contextual test data when tickets belong to an event', async () => { + const getTickets = vi.fn().mockResolvedValue([]); + const getTicketEvents = vi.fn().mockResolvedValue([ + { + id: 42, + title: 'XX Campeonato Mundial de Pelota Vasca', + image: 'https://example.com/pelota-vasca.jpg', + }, + ]); + + await TestBed.configureTestingModule({ + imports: [TicketsPage], + providers: [ + MenuPageSidebarContentService, + { provide: ToastService, useValue: { danger: vi.fn() } }, + { + provide: ActivatedRoute, + useValue: { snapshot: { paramMap: { get: () => '42' } } }, + }, + ], + }) + .overrideComponent(TicketsPage, { + set: { + providers: [ + { + provide: TicketService, + useValue: withCustomLoading({ getTickets, getTicketEvents }), + }, + ], + }, + }) + .compileComponents(); + + const fixture = TestBed.createComponent(TicketsPage); + fixture.detectChanges(); + await fixture.whenStable(); + await vi.waitFor(() => expect(getTicketEvents).toHaveBeenCalledOnce()); + await vi.waitFor(() => + expect( + ( + fixture.componentInstance as unknown as { eventContext: () => { image: string | null } } + ).eventContext().image, + ).toBe('https://example.com/pelota-vasca.jpg'), + ); + + const sidebarContent = TestBed.inject(MenuPageSidebarContentService); + const sidebarView = sidebarContent.template()?.createEmbeddedView({}); + sidebarView?.detectChanges(); + + const container = document.createElement('div'); + sidebarView?.rootNodes.forEach((node) => container.appendChild(node)); + + expect(getTickets).toHaveBeenCalledWith(42); + expect(container.querySelector('.ticket-event-context')?.textContent).toContain( + 'XX Campeonato Mundial de Pelota Vasca', + ); + expect(container.querySelector('.ticket-event-context__image')?.src).toBe( + 'https://example.com/pelota-vasca.jpg', + ); + expect(container.querySelector('.ticket-event-context__details')).toBeNull(); + expect(container.querySelector('.ticket-event-context__recommendation')).toBeNull(); + + sidebarView?.destroy(); + }); }); diff --git a/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.ts b/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.ts index 246156a..92ccff8 100644 --- a/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.ts +++ b/src/app/features/store/pages/account-page/pages/tickets-page/tickets-page.ts @@ -5,12 +5,23 @@ import { ModalService } from '../../../../../../core/services/modal.service'; import { ToastService } from '../../../../../../core/services/toast.service'; import { IconButtonComponent } from '../../../../../../shared/components/icon-button/icon-button.component'; import { MenuContentSectionComponent } from '../../../../components/menu-content-section/menu-content-section.component'; +import { MenuPageSidebarContentDirective } from '../../../../components/menu-page-layout/menu-page-sidebar-content.directive'; +import { + TicketEventContext, + TicketEventContextComponent, +} from './components/ticket-event-context/ticket-event-context.component'; import { TicketComponent } from './components/ticket/ticket.component'; import { TicketResponse, TicketService } from './ticket.service'; @Component({ selector: 'app-tickets-page', - imports: [IconButtonComponent, TicketComponent, MenuContentSectionComponent], + imports: [ + IconButtonComponent, + TicketComponent, + TicketEventContextComponent, + MenuContentSectionComponent, + MenuPageSidebarContentDirective, + ], providers: [TicketService], templateUrl: './tickets-page.html', styleUrl: './tickets-page.scss', @@ -21,6 +32,11 @@ export class TicketsPage implements OnInit { private readonly modalService = inject(ModalService); private readonly route = inject(ActivatedRoute, { optional: true }); + protected readonly eventId = this.eventIdFromRoute(); + protected readonly eventContext = signal({ + title: 'XX Campeonato Mundial de Pelota Vasca', + image: null, + }); protected readonly tickets = signal([]); protected readonly activeTickets = computed(() => this.tickets().filter((ticket) => !this.isInactive(ticket)), @@ -38,9 +54,25 @@ export class TicketsPage implements OnInit { async ngOnInit(): Promise { try { - this.tickets.set( - await this.ticketService.withCustomLoading().getTickets(this.eventIdFromRoute()), - ); + const service = this.ticketService.withCustomLoading(); + this.tickets.set(await service.getTickets(this.eventId)); + + if (this.eventId !== undefined) { + void service + .getTicketEvents() + .then((events) => { + const event = events.find((candidate) => candidate.id === this.eventId); + + if (event) { + this.eventContext.update((context) => ({ + ...context, + title: event.title, + image: event.image, + })); + } + }) + .catch(() => undefined); + } } catch { this.toastService.danger('Hubo un error al cargar los tickets'); } finally {