From 1252f4f7c685fa34bf29375894aad1b42953dfde Mon Sep 17 00:00:00 2001 From: catlog22 Date: Wed, 26 Nov 2025 16:26:16 +0800 Subject: [PATCH] feat: Enhance progress tracking and UI updates for dimension loading --- .claude/templates/review-cycle-dashboard.html | 199 ++++++++++++------ 1 file changed, 138 insertions(+), 61 deletions(-) diff --git a/.claude/templates/review-cycle-dashboard.html b/.claude/templates/review-cycle-dashboard.html index 4ea9ac5d..880e84a3 100644 --- a/.claude/templates/review-cycle-dashboard.html +++ b/.claude/templates/review-cycle-dashboard.html @@ -1953,20 +1953,101 @@ if (!response.ok) throw new Error('Progress file not found'); const progress = await response.json(); - updateProgressUI(progress); - // If complete, stop polling and load final results + // ✨ NEW: Load available dimensions incrementally and get actual count + const loadedCount = await loadAvailableDimensions(); + + // ✨ NEW: Update progress UI with actual file count + updateProgressUI(progress, loadedCount); + + // If complete, stop polling if (progress.phase === 'complete') { stopPolling(); - await loadFinalResults(); } } catch (error) { console.error('Error loading progress:', error); } } + // ✨ NEW: Load all available dimension files (even if review not complete) + async function loadAvailableDimensions() { + try { + // Load review state to get expected dimensions + const stateResponse = await fetch('./review-state.json'); + if (!stateResponse.ok) return; + + reviewState = await stateResponse.json(); + document.getElementById('sessionId').textContent = reviewState.session_id; + + // Try to load all expected dimensions + const allDimensions = reviewState.metadata?.dimensions || + Object.keys(dimensionConfig); + + const dimensionPromises = allDimensions.map(dim => + fetch(`./dimensions/${dim}.json`) + .then(r => { + if (r.ok) return r.json(); + return null; // Dimension not ready yet + }) + .catch(err => { + // Silently skip dimensions that don't exist yet + return null; + }) + ); + + const dimensions = await Promise.all(dimensionPromises); + + // Aggregate findings from completed dimensions + allFindings = []; + const loadedDimensions = []; + const severityCounts = { critical: 0, high: 0, medium: 0, low: 0 }; + + dimensions.forEach(dim => { + if (dim) { + // Handle both array and object formats + const dimData = Array.isArray(dim) ? dim[0] : dim; + + if (dimData && dimData.findings) { + loadedDimensions.push(dimData.dimension); + + // Aggregate findings + allFindings.push(...dimData.findings.map(f => ({ + ...f, + dimension: dimData.dimension + }))); + + // Aggregate severity counts + if (dimData.summary) { + severityCounts.critical += dimData.summary.critical || 0; + severityCounts.high += dimData.summary.high || 0; + severityCounts.medium += dimData.summary.medium || 0; + severityCounts.low += dimData.summary.low || 0; + } + } + } + }); + + // Update severity counts with real data + if (loadedDimensions.length > 0) { + updateSeverityCounts(severityCounts); + updateDimensionSummary(); + renderFindings(); + } + + // ✨ NEW: Return loaded count for progress calculation + return { + loaded: loadedDimensions.length, + total: allDimensions.length, + findings: allFindings.length + }; + } catch (error) { + console.error('Error loading available dimensions:', error); + return { loaded: 0, total: 7, findings: 0 }; + } + } + // Update progress UI - function updateProgressUI(progress) { + function updateProgressUI(progress, loadedCount) { document.getElementById('reviewId').textContent = progress.review_id || 'N/A'; document.getElementById('lastUpdate').textContent = new Date(progress.last_update).toLocaleString(); @@ -1977,67 +2058,49 @@ let percentComplete = 0; let progressText = ''; - if (progress.progress.parallel_review) { - percentComplete = progress.progress.parallel_review.percent_complete; - progressText = `Parallel Review: ${progress.progress.parallel_review.completed}/${progress.progress.parallel_review.total_dimensions} dimensions`; - } + // ✨ NEW: Calculate progress based on actual loaded dimension files + if (loadedCount && loadedCount.total > 0) { + percentComplete = Math.round((loadedCount.loaded / loadedCount.total) * 100); - if (progress.progress.deep_dive) { - percentComplete = progress.progress.deep_dive.percent_complete; - progressText = `Deep-Dive: ${progress.progress.deep_dive.analyzed}/${progress.progress.deep_dive.total_findings} findings`; - } + if (progress.phase === 'parallel' || progress.phase === 'aggregate') { + progressText = `Parallel Review: ${loadedCount.loaded}/${loadedCount.total} dimensions completed`; + if (loadedCount.findings > 0) { + progressText += ` • ${loadedCount.findings} finding${loadedCount.findings !== 1 ? 's' : ''} discovered`; + } + } else if (progress.phase === 'iterate' && progress.progress.deep_dive) { + percentComplete = progress.progress.deep_dive.percent_complete; + progressText = `Deep-Dive: ${progress.progress.deep_dive.analyzed}/${progress.progress.deep_dive.total_findings} findings analyzed`; + } else if (progress.phase === 'complete') { + percentComplete = 100; + progressText = `Review Complete • ${loadedCount.findings} total finding${loadedCount.findings !== 1 ? 's' : ''}`; + } + } else { + // Fallback to original logic if loadedCount not available + if (progress.progress.parallel_review) { + percentComplete = progress.progress.parallel_review.percent_complete; + progressText = `Parallel Review: ${progress.progress.parallel_review.completed}/${progress.progress.parallel_review.total_dimensions} dimensions`; + } - if (progress.phase === 'complete') { - percentComplete = 100; - progressText = 'Review Complete'; + if (progress.progress.deep_dive) { + percentComplete = progress.progress.deep_dive.percent_complete; + progressText = `Deep-Dive: ${progress.progress.deep_dive.analyzed}/${progress.progress.deep_dive.total_findings} findings`; + } + + if (progress.phase === 'complete') { + percentComplete = 100; + progressText = 'Review Complete'; + } } document.getElementById('progressFill').style.width = `${percentComplete}%`; document.getElementById('progressText').textContent = progressText; } - // Load final results + // Load final results (now delegates to loadAvailableDimensions) async function loadFinalResults() { - try { - // Load review state - const stateResponse = await fetch('./review-state.json'); - reviewState = await stateResponse.json(); - - document.getElementById('sessionId').textContent = reviewState.session_id; - - // Update severity counts - updateSeverityCounts(reviewState.severity_distribution); - - // Load all dimension files - const dimensionPromises = reviewState.dimensions_reviewed.map(dim => - fetch(`./dimensions/${dim}.json`) - .then(r => r.json()) - .catch(err => { - console.error(`Failed to load ${dim}:`, err); - return null; - }) - ); - - const dimensions = await Promise.all(dimensionPromises); - - // Aggregate all findings - allFindings = []; - dimensions.forEach(dim => { - if (dim && dim.findings) { - allFindings.push(...dim.findings.map(f => ({ - ...f, - dimension: dim.dimension - }))); - } - }); - - // Update dimension summary table - updateDimensionSummary(); - - renderFindings(); - } catch (error) { - console.error('Error loading final results:', error); - } + // ✨ NEW: This function now just ensures one final load + // since loadAvailableDimensions() handles all the heavy lifting + await loadAvailableDimensions(); } // Update severity counts @@ -2082,10 +2145,12 @@ dimensionStats[dim][severity]++; } dimensionStats[dim].total++; + // ✨ NEW: Mark as reviewed if we have findings from this dimension + dimensionStats[dim].reviewed = true; } }); - // Check reviewed status from reviewState + // Also check reviewed status from reviewState (backward compatibility) if (reviewState && reviewState.dimensions_reviewed) { reviewState.dimensions_reviewed.forEach(dim => { if (dimensionStats[dim]) { @@ -2106,9 +2171,21 @@ return `${count}`; }; - // Status indicator - const statusClass = stats.reviewed ? 'reviewed' : 'pending'; - const statusIcon = stats.reviewed ? '✓' : '○'; + // ✨ NEW: Enhanced status indicator with 3 states + let statusClass, statusIcon, statusTooltip; + if (stats.reviewed && hasFindings) { + statusClass = 'reviewed'; + statusIcon = '✓'; + statusTooltip = 'Completed'; + } else if (stats.reviewed && !hasFindings) { + statusClass = 'reviewed'; + statusIcon = '✓'; + statusTooltip = 'Completed (no findings)'; + } else { + statusClass = 'pending'; + statusIcon = '⏳'; + statusTooltip = 'Processing...'; + } return ` @@ -2123,7 +2200,7 @@ ${createCountBadge(stats.medium, 'medium')} ${createCountBadge(stats.low, 'low')} ${stats.total} - ${statusIcon} + ${statusIcon} `; }).join('');