feat: add download and share functionality for selected tickets
This commit is contained in:
@@ -30,4 +30,14 @@ export class TicketService {
|
|||||||
this.http.get<{ data: TicketResponse[] }>(`${this.tenantService.getTenantApiUrl()}/tickets`),
|
this.http.get<{ data: TicketResponse[] }>(`${this.tenantService.getTenantApiUrl()}/tickets`),
|
||||||
).then((response) => response.data ?? []);
|
).then((response) => response.data ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadPdf(ticketIds: number[]): Promise<Blob> {
|
||||||
|
return firstValueFrom(
|
||||||
|
this.http.post(
|
||||||
|
`${this.tenantService.getTenantApiUrl()}/tickets/pdf`,
|
||||||
|
{ ticket_ids: ticketIds },
|
||||||
|
{ responseType: 'blob' },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,19 @@
|
|||||||
|
|
||||||
<div class="tickets-list__bulk-actions">
|
<div class="tickets-list__bulk-actions">
|
||||||
<span class="tickets-list__bulk-action">
|
<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>
|
<small>Descargar</small>
|
||||||
</span>
|
</span>
|
||||||
<span class="tickets-list__bulk-action">
|
<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>
|
<small>Compartir</small>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,6 +70,8 @@
|
|||||||
[selected]="isSelected(ticket.id)"
|
[selected]="isSelected(ticket.id)"
|
||||||
(selectedChange)="toggleTicket(ticket.id, $event)"
|
(selectedChange)="toggleTicket(ticket.id, $event)"
|
||||||
(viewQr)="viewQr(ticket)"
|
(viewQr)="viewQr(ticket)"
|
||||||
|
(download)="downloadTicket(ticket)"
|
||||||
|
(share)="shareTicket(ticket)"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,4 +86,77 @@ export class TicketsPage implements OnInit {
|
|||||||
ticket: ticket.ticket,
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user