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:
165
admin-compliance/lib/sdk/tom-generator/export/docx-helpers.ts
Normal file
165
admin-compliance/lib/sdk/tom-generator/export/docx-helpers.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
// =============================================================================
|
||||
// TOM Generator DOCX Export - Helper Functions
|
||||
// Private helpers extracted from docx.ts to stay under 500 LOC hard cap
|
||||
// =============================================================================
|
||||
|
||||
import { DerivedTOM, ControlCategory } from '../types'
|
||||
import { getControlById } from '../controls/loader'
|
||||
import type { DOCXExportOptions, DocxTableRow } from './docx'
|
||||
|
||||
// =============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// =============================================================================
|
||||
|
||||
export 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
|
||||
}
|
||||
|
||||
export 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
|
||||
}
|
||||
|
||||
export 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
|
||||
}
|
||||
|
||||
export 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
|
||||
}
|
||||
|
||||
export 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
|
||||
}
|
||||
|
||||
export 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
|
||||
}
|
||||
|
||||
export function generateHTMLFromContent(
|
||||
content: Array<{ type: string; content?: string; headers?: string[]; rows?: DocxTableRow[] }>,
|
||||
options: Partial<DOCXExportOptions>
|
||||
): string {
|
||||
const DEFAULT_COLOR = '#1a56db'
|
||||
const primaryColor = options.primaryColor || DEFAULT_COLOR
|
||||
|
||||
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: ${primaryColor}; border-bottom: 2px solid ${primaryColor}; }
|
||||
h2 { font-size: 18pt; color: ${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: ${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' && element.headers && element.rows) {
|
||||
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
|
||||
}
|
||||
|
||||
export 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'
|
||||
}
|
||||
}
|
||||
|
||||
export function escapeHtml(text: string): string {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
export function processContent(content: string): string {
|
||||
// Convert markdown-style bold to HTML
|
||||
return escapeHtml(content).replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
||||
}
|
||||
Reference in New Issue
Block a user