Backend changes: - Add tinypdf-plus dependency for TTF/OTF font support - Create PDF service with text, certificate, and label generation - Add PDF API endpoints (/api/pdf/*) - Add branding service for custom fonts and styling - Add branding API endpoints (/api/branding/*) - Add font registration endpoint for custom fonts Frontend changes: - Add PDF library with download utilities - Add PDFDownloadButton component for reusable PDF downloads - Add branding API client for managing branding configs - Update ReportsPage with PDF generation tab Co-Authored-By: Claude <noreply@anthropic.com>
145 lines
3.4 KiB
TypeScript
145 lines
3.4 KiB
TypeScript
/**
|
|
* PDF Generation API
|
|
* Handles PDF generation using tinypdf-plus backend
|
|
*/
|
|
|
|
import { Capacitor } from '@capacitor/core';
|
|
|
|
const API_BASE_URL = Capacitor.isNativePlatform()
|
|
? 'https://veridian.runfoo.run'
|
|
: (import.meta.env.VITE_API_URL || '');
|
|
|
|
export interface TextPDFOptions {
|
|
content: string;
|
|
options?: {
|
|
width?: number;
|
|
height?: number;
|
|
margin?: number;
|
|
};
|
|
}
|
|
|
|
export interface CertificatePDFData {
|
|
title: string;
|
|
recipientName: string;
|
|
description: string;
|
|
date: string;
|
|
certificateNumber: string;
|
|
authorizedBy?: string;
|
|
}
|
|
|
|
export interface LabelPDFData {
|
|
title: string;
|
|
subtitle?: string;
|
|
qrCode?: string;
|
|
details: Array<{
|
|
label: string;
|
|
value: string;
|
|
}>;
|
|
}
|
|
|
|
/**
|
|
* Helper function to download PDF blob
|
|
*/
|
|
function downloadPDF(blob: Blob, filename: string) {
|
|
const url = window.URL.createObjectURL(blob);
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = filename;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
window.URL.revokeObjectURL(url);
|
|
}
|
|
|
|
/**
|
|
* Generate a simple text PDF
|
|
*/
|
|
export async function generateTextPDF(data: TextPDFOptions, filename = 'document.pdf'): Promise<void> {
|
|
const token = localStorage.getItem('token');
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
};
|
|
if (token) {
|
|
headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
|
|
const response = await fetch(`${API_BASE_URL}/api/pdf/text`, {
|
|
method: 'POST',
|
|
headers,
|
|
body: JSON.stringify(data),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to generate PDF: ${response.statusText}`);
|
|
}
|
|
|
|
const blob = await response.blob();
|
|
downloadPDF(blob, filename);
|
|
}
|
|
|
|
/**
|
|
* Generate a certificate PDF
|
|
*/
|
|
export async function generateCertificatePDF(
|
|
data: CertificatePDFData,
|
|
filename?: string
|
|
): Promise<void> {
|
|
const token = localStorage.getItem('token');
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
};
|
|
if (token) {
|
|
headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
|
|
const response = await fetch(`${API_BASE_URL}/api/pdf/certificate`, {
|
|
method: 'POST',
|
|
headers,
|
|
body: JSON.stringify(data),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to generate certificate: ${response.statusText}`);
|
|
}
|
|
|
|
const blob = await response.blob();
|
|
const defaultFilename = `certificate-${data.certificateNumber}.pdf`;
|
|
downloadPDF(blob, filename || defaultFilename);
|
|
}
|
|
|
|
/**
|
|
* Generate a label PDF (for plants, batches, etc.)
|
|
*/
|
|
export async function generateLabelPDF(data: LabelPDFData, filename = 'label.pdf'): Promise<void> {
|
|
const token = localStorage.getItem('token');
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
};
|
|
if (token) {
|
|
headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
|
|
const response = await fetch(`${API_BASE_URL}/api/pdf/label`, {
|
|
method: 'POST',
|
|
headers,
|
|
body: JSON.stringify(data),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to generate label: ${response.statusText}`);
|
|
}
|
|
|
|
const blob = await response.blob();
|
|
downloadPDF(blob, filename);
|
|
}
|
|
|
|
/**
|
|
* Check PDF service health
|
|
*/
|
|
export async function checkPDFHealth(): Promise<{ status: string; service: string; timestamp: string }> {
|
|
const response = await fetch(`${API_BASE_URL}/api/pdf/health`);
|
|
if (!response.ok) {
|
|
throw new Error(`PDF health check failed: ${response.statusText}`);
|
|
}
|
|
return response.json();
|
|
}
|