refactor(admin-compliance): split 7 oversized files under 500 LOC hard cap (batch 3)
- tom-generator/export/zip.ts: extract private helpers to zip-helpers.ts (544→342 LOC) - tom-generator/export/docx.ts: extract private helpers to docx-helpers.ts (525→378 LOC) - tom-generator/export/pdf.ts: extract private helpers to pdf-helpers.ts (517→446 LOC) - tom-generator/demo-data/index.ts: extract DEMO_RISK_PROFILES + DEMO_EVIDENCE_DOCUMENTS to demo-data-part2.ts (518→360 LOC) - einwilligungen/generator/privacy-policy-sections.ts: extract sections 5-7 to part2 (559→313 LOC) - einwilligungen/export/pdf.ts: extract HTML/CSS helpers to pdf-helpers.ts (505→296 LOC) - vendor-compliance/context.tsx: extract API action hooks to context-actions.tsx (509→286 LOC) All originals re-export from sibling files — zero consumer import changes needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
CATEGORY_METADATA,
|
||||
RETENTION_PERIOD_INFO,
|
||||
} from '../types'
|
||||
import { generateHTMLFromContent } from './pdf-helpers'
|
||||
|
||||
// =============================================================================
|
||||
// TYPES
|
||||
@@ -277,216 +278,6 @@ export async function generatePDFBlob(
|
||||
return new Blob([html], { type: 'text/html' })
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate printable HTML from PDF content
|
||||
*/
|
||||
function generateHTMLFromContent(
|
||||
content: PDFSection[],
|
||||
options: PDFExportOptions
|
||||
): string {
|
||||
const pageWidth = options.pageSize === 'A4' ? '210mm' : '8.5in'
|
||||
const pageHeight = options.pageSize === 'A4' ? '297mm' : '11in'
|
||||
|
||||
let html = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="${options.language}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>${options.language === 'de' ? 'Datenschutzerklaerung' : 'Privacy Policy'}</title>
|
||||
<style>
|
||||
@page {
|
||||
size: ${pageWidth} ${pageHeight};
|
||||
margin: 20mm;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Calibri, Arial, sans-serif;
|
||||
font-size: ${options.fontSize}pt;
|
||||
line-height: 1.6;
|
||||
color: #1e293b;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24pt;
|
||||
color: ${options.primaryColor};
|
||||
border-bottom: 2px solid ${options.primaryColor};
|
||||
padding-bottom: 10px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16pt;
|
||||
color: ${options.primaryColor};
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 13pt;
|
||||
color: #334155;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 12px 0;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28pt;
|
||||
text-align: center;
|
||||
color: ${options.primaryColor};
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 16px 0;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid #e2e8f0;
|
||||
padding: 8px 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: ${options.primaryColor};
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 12px 0;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
.pagebreak {
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 40px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
font-size: 9pt;
|
||||
color: #94a3b8;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
padding: 0;
|
||||
}
|
||||
.pagebreak {
|
||||
page-break-after: always;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`
|
||||
|
||||
for (const section of content) {
|
||||
switch (section.type) {
|
||||
case 'title':
|
||||
html += `<div class="title" style="${getStyleString(section.style)}">${escapeHtml(section.content || '')}</div>\n`
|
||||
break
|
||||
|
||||
case 'heading':
|
||||
html += `<h1 style="${getStyleString(section.style)}">${escapeHtml(section.content || '')}</h1>\n`
|
||||
break
|
||||
|
||||
case 'subheading':
|
||||
html += `<h3 style="${getStyleString(section.style)}">${escapeHtml(section.content || '')}</h3>\n`
|
||||
break
|
||||
|
||||
case 'paragraph':
|
||||
const alignClass = section.style?.align === 'center' ? ' class="center"' : ''
|
||||
html += `<p${alignClass} style="${getStyleString(section.style)}">${escapeHtml(section.content || '')}</p>\n`
|
||||
break
|
||||
|
||||
case 'list':
|
||||
html += '<ul>\n'
|
||||
for (const item of section.items || []) {
|
||||
html += ` <li>${escapeHtml(item)}</li>\n`
|
||||
}
|
||||
html += '</ul>\n'
|
||||
break
|
||||
|
||||
case 'table':
|
||||
if (section.table) {
|
||||
html += '<table>\n<thead><tr>\n'
|
||||
for (const header of section.table.headers) {
|
||||
html += ` <th>${escapeHtml(header)}</th>\n`
|
||||
}
|
||||
html += '</tr></thead>\n<tbody>\n'
|
||||
for (const row of section.table.rows) {
|
||||
html += '<tr>\n'
|
||||
for (const cell of row) {
|
||||
html += ` <td>${escapeHtml(cell)}</td>\n`
|
||||
}
|
||||
html += '</tr>\n'
|
||||
}
|
||||
html += '</tbody></table>\n'
|
||||
}
|
||||
break
|
||||
|
||||
case 'pagebreak':
|
||||
html += '<div class="pagebreak"></div>\n'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
html += '</body></html>'
|
||||
return html
|
||||
}
|
||||
|
||||
function getStyleString(style?: PDFSection['style']): string {
|
||||
if (!style) return ''
|
||||
|
||||
const parts: string[] = []
|
||||
if (style.color) parts.push(`color: ${style.color}`)
|
||||
if (style.fontSize) parts.push(`font-size: ${style.fontSize}pt`)
|
||||
if (style.bold) parts.push('font-weight: bold')
|
||||
if (style.italic) parts.push('font-style: italic')
|
||||
if (style.align) parts.push(`text-align: ${style.align}`)
|
||||
|
||||
return parts.join('; ')
|
||||
}
|
||||
|
||||
function escapeHtml(text: string): string {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// FILENAME GENERATION
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user