Add PDF ticket generation and download feature
Introduces PDF ticket generation using html2canvas and jsPDF, including a new PdfTicketComponent for ticket rendering and a PdfService for PDF creation. Updates purchase success flow to allow users to download tickets as PDFs, adds progress feedback, and includes a test route and component for development. Also refactors order form with a fake fill helper and improves UI details.
This commit is contained in:
111
src/app/pdf.service.ts
Normal file
111
src/app/pdf.service.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Injectable, ComponentRef, ViewContainerRef, ApplicationRef, createComponent, EnvironmentInjector, inject, signal } from '@angular/core';
|
||||
import html2canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
import { Eintrittskarte } from '@infinimotion/model-frontend';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PdfService {
|
||||
|
||||
private ticketsGreatedSignal = signal(0);
|
||||
private totalTicketsSignal = signal(0);
|
||||
|
||||
readonly ticketsGreated = this.ticketsGreatedSignal.asReadonly();
|
||||
readonly totalTickets = this.totalTicketsSignal.asReadonly();
|
||||
|
||||
appRef = inject(ApplicationRef);
|
||||
injector = inject(EnvironmentInjector);
|
||||
|
||||
async genTicket(tickets: Eintrittskarte[], ticketComponent: any): Promise<void> {
|
||||
if (tickets.length === 0) {
|
||||
throw new Error('Keine Tickets zum Generieren vorhanden');
|
||||
}
|
||||
|
||||
this.ticketsGreatedSignal.set(0)
|
||||
this.totalTicketsSignal.set(tickets.length)
|
||||
|
||||
// Container für temporäres Rendering erstellen
|
||||
const container = document.createElement('div');
|
||||
container.style.position = 'fixed';
|
||||
container.style.left = '-9999px';
|
||||
container.style.top = '0';
|
||||
document.body.appendChild(container);
|
||||
|
||||
const componentRefs: ComponentRef<any>[] = [];
|
||||
|
||||
try {
|
||||
// Ticket-Format: 210mm x 99mm
|
||||
const ticketWidthMM = 210;
|
||||
const ticketHeightMM = 99;
|
||||
|
||||
const pdf = new jsPDF({
|
||||
orientation: 'landscape',
|
||||
unit: 'mm',
|
||||
format: [ticketWidthMM, ticketHeightMM]
|
||||
});
|
||||
|
||||
for (let i = 0; i < tickets.length; i++) {
|
||||
const ticket = tickets[i];
|
||||
|
||||
// Komponente dynamisch erstellen
|
||||
const componentRef = createComponent(ticketComponent, {
|
||||
environmentInjector: this.injector
|
||||
});
|
||||
|
||||
// Ticket-Daten an die Komponente übergeben
|
||||
(componentRef.instance as any).ticket = ticket;
|
||||
|
||||
// Komponente ins DOM einfügen
|
||||
this.appRef.attachView(componentRef.hostView);
|
||||
container.appendChild(componentRef.location.nativeElement);
|
||||
componentRefs.push(componentRef);
|
||||
|
||||
// Change Detection triggern
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
|
||||
// Kurz warten, damit alles gerendert ist
|
||||
await new Promise(requestAnimationFrame);
|
||||
|
||||
// HTML zu Canvas konvertieren
|
||||
const canvas = await html2canvas(componentRef.location.nativeElement, {
|
||||
scale: 2,
|
||||
backgroundColor: '#ffffff',
|
||||
logging: false,
|
||||
useCORS: true
|
||||
});
|
||||
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
|
||||
if (i > 0) {
|
||||
pdf.addPage([ticketWidthMM, ticketHeightMM], 'landscape');
|
||||
}
|
||||
|
||||
// Bild ins PDF einfügen
|
||||
pdf.addImage(imgData, 'PNG', 0, 0, ticketWidthMM, ticketHeightMM);
|
||||
|
||||
this.ticketsGreatedSignal.set(this.ticketsGreatedSignal() + 1)
|
||||
}
|
||||
|
||||
const fileName = this.generateFileName(tickets);
|
||||
pdf.save(fileName);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Generieren des PDFs:', error);
|
||||
throw new Error('Das PDF konnte nicht erstellt werden. Bitte versuche es erneut.');
|
||||
} finally {
|
||||
componentRefs.forEach(ref => {
|
||||
this.appRef.detachView(ref.hostView);
|
||||
ref.destroy();
|
||||
});
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
}
|
||||
|
||||
private generateFileName(tickets: Eintrittskarte[]): string {
|
||||
const orderCode = tickets[0].order.code;
|
||||
const timestamp = new Date().getTime();
|
||||
|
||||
return `Ticket_${orderCode}_${timestamp}.pdf`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user