106 lines
3.3 KiB
TypeScript
106 lines
3.3 KiB
TypeScript
/**
|
|
* Document Generation API
|
|
* Services for PDF, Excel, Word, and label generation.
|
|
* Phase 9 — runtime deps (@react-pdf/renderer, exceljs, docx) added when installed.
|
|
* This file provides the API surface; implementations use dynamic imports.
|
|
*/
|
|
|
|
export function createDocumentGeneratorApi() {
|
|
return {
|
|
/**
|
|
* Generate a PDF document (member card, invoice, certificate, etc.)
|
|
* Uses @react-pdf/renderer or jspdf at runtime.
|
|
*/
|
|
async generatePdf(params: {
|
|
title: string;
|
|
content: Record<string, unknown>;
|
|
format?: 'A4' | 'A5' | 'letter';
|
|
orientation?: 'portrait' | 'landscape';
|
|
}): Promise<Uint8Array> {
|
|
// Dynamic import to avoid bundle bloat in SSR
|
|
// Actual implementation will use @react-pdf/renderer
|
|
throw new Error(
|
|
'PDF generation requires @react-pdf/renderer. Install it and implement the renderer in pdf-generator.service.ts',
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Generate an Excel workbook (reports, data exports)
|
|
* Uses exceljs at runtime.
|
|
*/
|
|
async generateExcel(params: {
|
|
title: string;
|
|
sheets: Array<{
|
|
name: string;
|
|
columns: Array<{ header: string; key: string; width?: number }>;
|
|
rows: Array<Record<string, unknown>>;
|
|
}>;
|
|
}): Promise<Uint8Array> {
|
|
throw new Error(
|
|
'Excel generation requires exceljs. Install it and implement in excel-generator.service.ts',
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Generate a Word document (mail merge, letters)
|
|
* Uses docx at runtime.
|
|
*/
|
|
async generateWord(params: {
|
|
title: string;
|
|
templateContent: string;
|
|
mergeFields: Record<string, string>;
|
|
}): Promise<Uint8Array> {
|
|
throw new Error(
|
|
'Word generation requires docx. Install it and implement in word-generator.service.ts',
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Generate address labels in Avery format
|
|
* Pure implementation — no external deps needed.
|
|
*/
|
|
generateLabelsHtml(params: {
|
|
labelFormat: string;
|
|
records: Array<{
|
|
line1: string;
|
|
line2?: string;
|
|
line3?: string;
|
|
line4?: string;
|
|
}>;
|
|
}): string {
|
|
const { records } = params;
|
|
|
|
// Avery L7163 = 14 labels per page, 2 columns x 7 rows
|
|
const labelsPerPage = 14;
|
|
const pages: string[] = [];
|
|
|
|
for (let i = 0; i < records.length; i += labelsPerPage) {
|
|
const pageRecords = records.slice(i, i + labelsPerPage);
|
|
const labels = pageRecords
|
|
.map(
|
|
(r) => `
|
|
<div style="width:99.1mm;height:38.1mm;padding:4mm;box-sizing:border-box;overflow:hidden;font-size:10pt;font-family:Arial,sans-serif;">
|
|
<div>${r.line1}</div>
|
|
${r.line2 ? `<div>${r.line2}</div>` : ''}
|
|
${r.line3 ? `<div>${r.line3}</div>` : ''}
|
|
${r.line4 ? `<div>${r.line4}</div>` : ''}
|
|
</div>
|
|
`,
|
|
)
|
|
.join('');
|
|
|
|
pages.push(`
|
|
<div style="width:210mm;display:grid;grid-template-columns:1fr 1fr;gap:0;page-break-after:always;">
|
|
${labels}
|
|
</div>
|
|
`);
|
|
}
|
|
|
|
return `<!DOCTYPE html><html><head><meta charset="utf-8"><style>
|
|
@page { size: A4; margin: 5mm 5mm; }
|
|
body { margin: 0; }
|
|
</style></head><body>${pages.join('')}</body></html>`;
|
|
},
|
|
};
|
|
}
|