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:
Sharang Parnerkar
2026-04-18 00:43:41 +02:00
parent feedeb052f
commit 7d8e5667c9
14 changed files with 1460 additions and 1312 deletions

View File

@@ -10,6 +10,15 @@ import {
CONTROL_CATEGORIES,
} from '../types'
import { getControlById, getCategoryMetadata } from '../controls/loader'
import {
groupTOMsByCategory,
formatRole,
formatProtectionLevel,
formatType,
formatImplementationStatus,
formatApplicability,
generateHTMLFromContent,
} from './docx-helpers'
// =============================================================================
// TYPES
@@ -320,78 +329,6 @@ export function generateDOCXContent(
return elements
}
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
function groupTOMsByCategory(
toms: DerivedTOM[],
includeNotApplicable: boolean
): Map<ControlCategory, DerivedTOM[]> {
const grouped = new Map<ControlCategory, DerivedTOM[]>()
for (const tom of toms) {
if (!includeNotApplicable && tom.applicability === 'NOT_APPLICABLE') {
continue
}
const control = getControlById(tom.controlId)
if (!control) continue
const category = control.category
const existing = grouped.get(category) || []
existing.push(tom)
grouped.set(category, existing)
}
return grouped
}
function formatRole(role: string, language: 'de' | 'en'): string {
const roles: Record<string, Record<'de' | 'en', string>> = {
CONTROLLER: { de: 'Verantwortlicher', en: 'Controller' },
PROCESSOR: { de: 'Auftragsverarbeiter', en: 'Processor' },
JOINT_CONTROLLER: { de: 'Gemeinsam Verantwortlicher', en: 'Joint Controller' },
}
return roles[role]?.[language] || role
}
function formatProtectionLevel(level: string, language: 'de' | 'en'): string {
const levels: Record<string, Record<'de' | 'en', string>> = {
NORMAL: { de: 'Normal', en: 'Normal' },
HIGH: { de: 'Hoch', en: 'High' },
VERY_HIGH: { de: 'Sehr hoch', en: 'Very High' },
}
return levels[level]?.[language] || level
}
function formatType(type: string, language: 'de' | 'en'): string {
const types: Record<string, Record<'de' | 'en', string>> = {
TECHNICAL: { de: 'Technisch', en: 'Technical' },
ORGANIZATIONAL: { de: 'Organisatorisch', en: 'Organizational' },
}
return types[type]?.[language] || type
}
function formatImplementationStatus(status: string, language: 'de' | 'en'): string {
const statuses: Record<string, Record<'de' | 'en', string>> = {
NOT_IMPLEMENTED: { de: 'Nicht umgesetzt', en: 'Not Implemented' },
PARTIAL: { de: 'Teilweise umgesetzt', en: 'Partially Implemented' },
IMPLEMENTED: { de: 'Umgesetzt', en: 'Implemented' },
}
return statuses[status]?.[language] || status
}
function formatApplicability(applicability: string, language: 'de' | 'en'): string {
const apps: Record<string, Record<'de' | 'en', string>> = {
REQUIRED: { de: 'Erforderlich', en: 'Required' },
RECOMMENDED: { de: 'Empfohlen', en: 'Recommended' },
OPTIONAL: { de: 'Optional', en: 'Optional' },
NOT_APPLICABLE: { de: 'Nicht anwendbar', en: 'Not Applicable' },
}
return apps[applicability]?.[language] || applicability
}
// =============================================================================
// DOCX BLOB GENERATION
// Uses simple XML structure compatible with docx libraries
@@ -421,90 +358,6 @@ export async function generateDOCXBlob(
return blob
}
function generateHTMLFromContent(
content: DocxElement[],
options: Partial<DOCXExportOptions>
): string {
const opts = { ...DEFAULT_OPTIONS, ...options }
let html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Calibri, Arial, sans-serif; font-size: 11pt; line-height: 1.5; }
h1 { font-size: 24pt; color: ${opts.primaryColor}; border-bottom: 2px solid ${opts.primaryColor}; }
h2 { font-size: 18pt; color: ${opts.primaryColor}; margin-top: 24pt; }
h3 { font-size: 14pt; color: #333; margin-top: 18pt; }
table { border-collapse: collapse; width: 100%; margin: 12pt 0; }
th, td { border: 1px solid #ddd; padding: 8pt; text-align: left; }
th { background-color: ${opts.primaryColor}; color: white; }
tr:nth-child(even) { background-color: #f9f9f9; }
ul { margin: 6pt 0; }
li { margin: 3pt 0; }
.warning { color: #dc2626; font-weight: bold; }
</style>
</head>
<body>
`
for (const element of content) {
if (element.type === 'table') {
html += '<table>'
html += '<tr>'
for (const header of element.headers) {
html += `<th>${escapeHtml(header)}</th>`
}
html += '</tr>'
for (const row of element.rows) {
html += '<tr>'
for (const cell of row.cells) {
html += `<td>${escapeHtml(cell)}</td>`
}
html += '</tr>'
}
html += '</table>'
} else {
const tag = getHtmlTag(element.type)
const processedContent = processContent(element.content)
html += `<${tag}>${processedContent}</${tag}>\n`
}
}
html += '</body></html>'
return html
}
function getHtmlTag(type: string): string {
switch (type) {
case 'heading1':
return 'h1'
case 'heading2':
return 'h2'
case 'heading3':
return 'h3'
case 'bullet':
return 'li'
default:
return 'p'
}
}
function escapeHtml(text: string): string {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;')
}
function processContent(content: string): string {
// Convert markdown-style bold to HTML
return escapeHtml(content).replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
}
// =============================================================================
// FILENAME GENERATION
// =============================================================================