ca-grow-ops-manager/frontend/src/lib/pdf.ts
fullsizemalt 4bdbfc82ca Add tinypdf-plus integration with PDF generation and branding
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>
2026-01-08 12:07:35 -08:00

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();
}