// Version Comparison Component class VersionComparison { constructor(options = {}) { this.container = null; this.versions = new Map(); this.selectedVersions = new Set(); this.maxSelections = options.maxSelections || 3; this.onCompare = options.onCompare || (() => {}); this.onRollback = options.onRollback || (() => {}); this.timeline = null; } initialize(containerId) { this.container = document.getElementById(containerId); if (!this.container) { throw new Error(`Container element with id "${containerId}" not found`); } this._initializeTimeline(); } setVersions(versions) { this.versions = new Map(versions.map(v => [v.name, v])); this._updateTimeline(); this.render(); } _initializeTimeline() { this.timeline = document.createElement('div'); this.timeline.className = 'version-timeline'; this.container.appendChild(this.timeline); } _updateTimeline() { const sortedVersions = Array.from(this.versions.values()) .sort((a, b) => new Date(a.created_at) - new Date(b.created_at)); this.timeline.innerHTML = `
${this._renderTimelineDots(sortedVersions)}
${this._renderTimelineLabels(sortedVersions)}
`; } _renderTimelineDots(versions) { return versions.map(version => `
${this._renderImpactIndicator(version)}
`).join(''); } _renderImpactIndicator(version) { const impact = version.comparison_metadata.impact_score || 0; const size = Math.max(8, Math.min(24, impact * 24)); // 8-24px based on impact return `
`; } _renderTimelineLabels(versions) { return versions.map(version => `
${version.name}
${new Date(version.created_at).toLocaleDateString()}
`).join(''); } render() { if (!this.container) return; const selectedVersionsArray = Array.from(this.selectedVersions); this.container.innerHTML = `

Version Comparison

${this._renderSelectedVersions(selectedVersionsArray)}
${this._renderActionButtons(selectedVersionsArray)}
${this.timeline.outerHTML}
${this._renderComparisonContent(selectedVersionsArray)}
`; this.attachEventListeners(); } _renderSelectedVersions(selectedVersions) { return selectedVersions.map((version, index) => `
Version ${index + 1}: ${version}
`).join(''); } _renderActionButtons(selectedVersions) { const canCompare = selectedVersions.length >= 2; const canRollback = selectedVersions.length === 1; return ` ${canCompare ? ` ` : ''} ${canRollback ? ` ` : ''} `; } _renderComparisonContent(selectedVersions) { if (selectedVersions.length < 2) { return `
Select at least two versions to compare
`; } return `
Computing differences...
`; } _toggleVersionSelection(versionName) { if (this.selectedVersions.has(versionName)) { this.selectedVersions.delete(versionName); } else if (this.selectedVersions.size < this.maxSelections) { this.selectedVersions.add(versionName); } else { // Show max selections warning this._showWarning(`Maximum ${this.maxSelections} versions can be compared`); return; } this.render(); } _removeVersion(versionName) { this.selectedVersions.delete(versionName); this.render(); } async _handleCompare() { const selectedVersions = Array.from(this.selectedVersions); if (selectedVersions.length < 2) return; try { const results = await this.onCompare(selectedVersions); this._renderComparisonResults(results); } catch (error) { console.error('Comparison failed:', error); this._showError('Failed to compare versions'); } } async _handleRollback() { const selectedVersion = Array.from(this.selectedVersions)[0]; if (!selectedVersion) return; try { await this.onRollback(selectedVersion); // Handle successful rollback } catch (error) { console.error('Rollback failed:', error); this._showError('Failed to rollback version'); } } _renderComparisonResults(results) { const resultsContainer = this.container.querySelector('.comparison-results'); if (!resultsContainer) return; resultsContainer.innerHTML = `
${results.map(diff => this._renderDiffSection(diff)).join('')}
`; } _renderDiffSection(diff) { return `

Changes: ${diff.version1} → ${diff.version2}

Computed in ${diff.computation_time.toFixed(2)}s Impact Score: ${Math.round(diff.impact_score * 100)}%
${this._renderChanges(diff.changes)}
`; } _renderChanges(changes) { return changes.map(change => `
${change.type} ${change.file}
Previous
${this._escapeHtml(change.old_value)}
New
${this._escapeHtml(change.new_value)}
`).join(''); } _showWarning(message) { const warning = document.createElement('div'); warning.className = 'comparison-warning'; warning.textContent = message; this.container.appendChild(warning); setTimeout(() => warning.remove(), 3000); } _showError(message) { const error = document.createElement('div'); error.className = 'comparison-error'; error.textContent = message; this.container.appendChild(error); setTimeout(() => error.remove(), 3000); } _escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } attachEventListeners() { // Timeline scroll handling const timeline = this.container.querySelector('.version-timeline'); if (timeline) { let isDown = false; let startX; let scrollLeft; timeline.addEventListener('mousedown', (e) => { isDown = true; timeline.classList.add('active'); startX = e.pageX - timeline.offsetLeft; scrollLeft = timeline.scrollLeft; }); timeline.addEventListener('mouseleave', () => { isDown = false; timeline.classList.remove('active'); }); timeline.addEventListener('mouseup', () => { isDown = false; timeline.classList.remove('active'); }); timeline.addEventListener('mousemove', (e) => { if (!isDown) return; e.preventDefault(); const x = e.pageX - timeline.offsetLeft; const walk = (x - startX) * 2; timeline.scrollLeft = scrollLeft - walk; }); } } } export default VersionComparison;