// src/js/auditTrailRenderer.js import { auditService } from '../../src/utils/auditService.js'; export class AuditTrailRenderer { constructor(containerId, options = {}) { this.containerId = containerId; this.options = { title: options.title || 'KI-Entscheidungspfad', collapsible: options.collapsible !== false, defaultExpanded: options.defaultExpanded || false, ...options }; this.componentId = `audit-trail-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`; } /** * Render audit trail from raw audit data * FIXED: Proper Promise handling */ render(rawAuditTrail) { const container = document.getElementById(this.containerId); if (!container) { console.error(`[AUDIT RENDERER] Container ${this.containerId} not found`); return; } if (!rawAuditTrail || !Array.isArray(rawAuditTrail) || rawAuditTrail.length === 0) { this.renderEmpty(); return; } try { console.log('[AUDIT RENDERER] Processing audit trail...', rawAuditTrail.length, 'entries'); // Process audit trail using the centralized service (synchronous) const processedAudit = auditService.processAuditTrail(rawAuditTrail); console.log('[AUDIT RENDERER] Processed audit:', processedAudit); if (processedAudit && processedAudit.phases && processedAudit.phases.length > 0) { this.renderProcessed(processedAudit); // Attach event handlers after DOM is updated setTimeout(() => this.attachEventHandlers(), 0); } else { console.warn('[AUDIT RENDERER] No processed audit data'); this.renderEmpty(); } } catch (error) { console.error('[AUDIT RENDERER] Failed to render audit trail:', error); this.renderError(error); } } /** * Render processed audit trail */ renderProcessed(processedAudit) { const container = document.getElementById(this.containerId); if (!container) return; const detailsId = `${this.componentId}-details`; console.log('[AUDIT RENDERER] Rendering processed audit with', processedAudit.phases.length, 'phases'); container.innerHTML = `

${this.options.title}

${auditService.formatDuration(processedAudit.totalTime)}
${processedAudit.avgConfidence}% Vertrauen
${processedAudit.stepCount} Schritte
${this.options.collapsible ? `
` : ''}
${this.renderSummary(processedAudit)} ${this.renderProcessFlow(processedAudit)} ${this.renderTechnicalDetails(processedAudit)}
`; console.log('[AUDIT RENDERER] HTML rendered successfully'); } /** * Render audit summary section */ renderSummary(audit) { return `
📊 Analyse-Qualität
${audit.highConfidenceSteps}
Hohe Sicherheit
${audit.lowConfidenceSteps}
Unsichere Schritte
${auditService.formatDuration(audit.totalTime)}
Verarbeitungszeit
${audit.summary.keyInsights && audit.summary.keyInsights.length > 0 ? `
✓ Erkenntnisse:
` : ''} ${audit.summary.potentialIssues && audit.summary.potentialIssues.length > 0 ? `
⚠ Hinweise:
` : ''}
`; } /** * Render process flow section */ renderProcessFlow(audit) { if (!audit.phases || audit.phases.length === 0) { return '

Keine Phasen verfügbar

'; } return `
${audit.phases.map((phase, index) => `
${phase.icon || '📋'} ${phase.displayName || phase.name}
${phase.avgConfidence || 0}%
${(phase.entries || []).map(entry => `
${auditService.getActionDisplayName(entry.action)}
${(entry.inputSummary && entry.inputSummary !== 'null') || (entry.outputSummary && entry.outputSummary !== 'null') ? `
${entry.inputSummary && entry.inputSummary !== 'null' ? `
Input: ${this.escapeHtml(entry.inputSummary)}
` : ''} ${entry.outputSummary && entry.outputSummary !== 'null' ? `
Output: ${this.escapeHtml(entry.outputSummary)}
` : ''}
` : ''}
`).join('')}
`).join('')}
`; } /** * Render technical details section */ renderTechnicalDetails(audit) { const technicalId = `${this.componentId}-technical`; return `
`; } /** * Attach event handlers for interactions */ attachEventHandlers() { console.log('[AUDIT RENDERER] Attaching event handlers...'); // Handle collapsible header if (this.options.collapsible) { const header = document.querySelector(`[data-target="${this.componentId}-details"]`); const details = document.getElementById(`${this.componentId}-details`); const toggleIcon = header?.querySelector('.toggle-icon svg'); if (header && details && toggleIcon) { // Remove existing listeners header.replaceWith(header.cloneNode(true)); const newHeader = document.querySelector(`[data-target="${this.componentId}-details"]`); const newToggleIcon = newHeader?.querySelector('.toggle-icon svg'); if (newHeader && newToggleIcon) { newHeader.addEventListener('click', () => { const isCollapsed = details.classList.contains('collapsed'); if (isCollapsed) { details.classList.remove('collapsed'); newToggleIcon.style.transform = 'rotate(180deg)'; } else { details.classList.add('collapsed'); newToggleIcon.style.transform = 'rotate(0deg)'; } }); console.log('[AUDIT RENDERER] Collapsible header handler attached'); } } } // Handle technical details toggle const technicalBtn = document.querySelector(`[data-target="${this.componentId}-technical"]`); const technicalDetails = document.getElementById(`${this.componentId}-technical`); if (technicalBtn && technicalDetails) { // Remove existing listener technicalBtn.replaceWith(technicalBtn.cloneNode(true)); const newTechnicalBtn = document.querySelector(`[data-target="${this.componentId}-technical"]`); if (newTechnicalBtn) { newTechnicalBtn.addEventListener('click', () => { const isCollapsed = technicalDetails.classList.contains('collapsed'); if (isCollapsed) { technicalDetails.classList.remove('collapsed'); newTechnicalBtn.textContent = '🔧 Technische Details ausblenden'; } else { technicalDetails.classList.add('collapsed'); newTechnicalBtn.textContent = '🔧 Technische Details anzeigen'; } }); console.log('[AUDIT RENDERER] Technical details handler attached'); } } } /** * Render empty state */ renderEmpty() { const container = document.getElementById(this.containerId); if (container) { container.innerHTML = `

Kein Audit-Trail verfügbar

`; } } /** * Render error state */ renderError(error) { const container = document.getElementById(this.containerId); if (container) { container.innerHTML = `

Audit-Trail Fehler

Fehler beim Laden der Audit-Informationen: ${this.escapeHtml(error.message)}

`; } } /** * Utility method to escape HTML */ escapeHtml(text) { if (typeof text !== 'string') return String(text); const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * Clear the audit trail display */ clear() { const container = document.getElementById(this.containerId); if (container) { container.innerHTML = ''; } } /** * Get container element */ getContainer() { return document.getElementById(this.containerId); } }