// ============================================================================= // 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 { const grouped = new Map() 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> = { 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> = { 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> = { 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> = { 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> = { 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 ): string { const DEFAULT_COLOR = '#1a56db' const primaryColor = options.primaryColor || DEFAULT_COLOR let html = ` ` for (const element of content) { if (element.type === 'table' && element.headers && element.rows) { html += '' html += '' for (const header of element.headers) { html += `` } html += '' for (const row of element.rows) { html += '' for (const cell of row.cells) { html += `` } html += '' } html += '
${escapeHtml(header)}
${escapeHtml(cell)}
' } else { const tag = getHtmlTag(element.type) const processedContent = processContent(element.content || '') html += `<${tag}>${processedContent}\n` } } 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, ''') } export function processContent(content: string): string { // Convert markdown-style bold to HTML return escapeHtml(content).replace(/\*\*(.*?)\*\*/g, '$1') }