feat: add download and share functionality for selected tickets

This commit is contained in:
2026-07-28 09:01:39 -03:00
parent 163444c91f
commit c41c60f349
3 changed files with 95 additions and 2 deletions

View File

@@ -30,4 +30,14 @@ export class TicketService {
this.http.get<{ data: TicketResponse[] }>(`${this.tenantService.getTenantApiUrl()}/tickets`),
).then((response) => response.data ?? []);
}
downloadPdf(ticketIds: number[]): Promise<Blob> {
return firstValueFrom(
this.http.post(
`${this.tenantService.getTenantApiUrl()}/tickets/pdf`,
{ ticket_ids: ticketIds },
{ responseType: 'blob' },
),
);
}
}

View File

@@ -15,11 +15,19 @@
<div class="tickets-list__bulk-actions">
<span class="tickets-list__bulk-action">
<app-icon-button variant="download" ariaLabel="Descargar tickets seleccionados" />
<app-icon-button
variant="download"
ariaLabel="Descargar tickets seleccionados"
(clicked)="downloadSelected()"
/>
<small>Descargar</small>
</span>
<span class="tickets-list__bulk-action">
<app-icon-button variant="share" ariaLabel="Compartir tickets seleccionados" />
<app-icon-button
variant="share"
ariaLabel="Compartir tickets seleccionados"
(clicked)="shareSelected()"
/>
<small>Compartir</small>
</span>
</div>
@@ -62,6 +70,8 @@
[selected]="isSelected(ticket.id)"
(selectedChange)="toggleTicket(ticket.id, $event)"
(viewQr)="viewQr(ticket)"
(download)="downloadTicket(ticket)"
(share)="shareTicket(ticket)"
/>
}

View File

@@ -86,4 +86,77 @@ export class TicketsPage implements OnInit {
ticket: ticket.ticket,
});
}
protected async downloadTicket(ticket: TicketResponse): Promise<void> {
await this.downloadTickets([ticket]);
}
protected async downloadSelected(): Promise<void> {
const tickets = this.activeTickets().filter((ticket) => this.selectedIds().has(ticket.id));
if (!tickets.length) {
this.toastService.danger('Seleccioná al menos un ticket para descargar.');
return;
}
await this.downloadTickets(tickets);
}
protected async shareTicket(ticket: TicketResponse): Promise<void> {
await this.shareTickets([ticket]);
}
protected async shareSelected(): Promise<void> {
const tickets = this.activeTickets().filter((ticket) => this.selectedIds().has(ticket.id));
if (!tickets.length) {
this.toastService.danger('Seleccioná al menos un ticket para compartir.');
return;
}
await this.shareTickets(tickets);
}
private async downloadTickets(tickets: TicketResponse[]): Promise<void> {
try {
const pdf = await this.ticketService.downloadPdf(tickets.map((ticket) => ticket.id));
this.downloadPdf(pdf);
} catch {
this.toastService.danger('No se pudo generar el PDF de los tickets.');
}
}
private async shareTickets(tickets: TicketResponse[]): Promise<void> {
try {
const pdf = await this.ticketService.downloadPdf(tickets.map((ticket) => ticket.id));
const file = new File([pdf], 'mis-tickets.pdf', { type: 'application/pdf' });
if (navigator.canShare?.({ files: [file] })) {
await navigator.share({
files: [file],
title: 'Mis tickets',
text: 'Te comparto mis tickets digitales.',
});
return;
}
this.downloadPdf(pdf);
this.toastService.success('Tu navegador no permite compartir archivos; descargamos el PDF.');
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') return;
this.toastService.danger('No se pudo generar el PDF de los tickets.');
}
}
private downloadPdf(pdf: Blob): void {
const url = URL.createObjectURL(pdf);
const link = document.createElement('a');
link.href = url;
link.download = 'mis-tickets.pdf';
document.body.appendChild(link);
link.click();
link.remove();
window.setTimeout(() => URL.revokeObjectURL(url), 0);
}
}