mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-11 02:33:51 +08:00
feat: Enhance fix-dashboard.html to consume more JSON fields from review-fix workflow
- Add errors array display with badge and detailed error list in Active Agents - Enhance task cards with dimension badge, file:line, attempts, and commit hash - Add Finding Details Modal with complete information on click - Display fix_strategy and risk_assessment in Active Groups cards - Add renderAgentErrors(), renderStrategyAndRisk() helper functions - Increase JSON field consumption rate from ~53% to ~90% 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1021,6 +1021,238 @@
|
|||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Error Badge */
|
||||||
|
.error-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 5px 12px;
|
||||||
|
background-color: rgba(239, 68, 68, 0.15);
|
||||||
|
color: var(--danger-color);
|
||||||
|
border-radius: 14px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error Container */
|
||||||
|
.error-container {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: rgba(239, 68, 68, 0.1);
|
||||||
|
border-left: 4px solid var(--danger-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-header {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--danger-color);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-item {
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: var(--bg-card);
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-item:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-meta {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Task Card Enhancements */
|
||||||
|
.task-file {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background-color: rgba(139, 92, 246, 0.08);
|
||||||
|
border-radius: 6px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-dimension-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 600;
|
||||||
|
background-color: rgba(139, 92, 246, 0.15);
|
||||||
|
color: var(--info-color);
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-attempts {
|
||||||
|
color: var(--warning-color);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finding Details Modal */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
z-index: 2000;
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-overlay.active {
|
||||||
|
display: flex;
|
||||||
|
opacity: 1;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
border-radius: 12px;
|
||||||
|
max-width: 900px;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
transform: scale(0.9);
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-overlay.active .modal-content {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
padding: 25px;
|
||||||
|
border-bottom: 2px solid var(--border-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header h2 {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section h3 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: var(--accent-color);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 140px 1fr;
|
||||||
|
gap: 12px;
|
||||||
|
background-color: var(--bg-card);
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-label {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-value {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-value code {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Strategy and Risk Display */
|
||||||
|
.strategy-risk-container {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: rgba(139, 92, 246, 0.08);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(139, 92, 246, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.strategy-item, .risk-item {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.strategy-item:last-child, .risk-item:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.strategy-label, .risk-label {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--info-color);
|
||||||
|
min-width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.risk-level {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.risk-level.low {
|
||||||
|
background-color: rgba(34, 197, 94, 0.2);
|
||||||
|
color: var(--success-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.risk-level.medium {
|
||||||
|
background-color: rgba(245, 158, 11, 0.2);
|
||||||
|
color: var(--warning-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.risk-level.high {
|
||||||
|
background-color: rgba(239, 68, 68, 0.2);
|
||||||
|
color: var(--danger-color);
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive Design */
|
/* Responsive Design */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.container {
|
.container {
|
||||||
@@ -1051,6 +1283,15 @@
|
|||||||
.stage-timeline {
|
.stage-timeline {
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@@ -1199,6 +1440,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Finding Details Modal -->
|
||||||
|
<div class="modal-overlay" id="findingModal" onclick="closeFindingModal(event)">
|
||||||
|
<div class="modal-content" onclick="event.stopPropagation()">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2><span id="modalFindingIcon">📋</span> Finding Details</h2>
|
||||||
|
<button class="close-btn" onclick="closeFindingModal()">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="modalBody">
|
||||||
|
<!-- Populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const SESSION_ID = '{{SESSION_ID}}';
|
const SESSION_ID = '{{SESSION_ID}}';
|
||||||
const REVIEW_DIR = '{{REVIEW_DIR}}';
|
const REVIEW_DIR = '{{REVIEW_DIR}}';
|
||||||
@@ -1282,18 +1536,50 @@
|
|||||||
const statusClass = task.status === 'completed' ? task.result : task.status;
|
const statusClass = task.status === 'completed' ? task.result : task.status;
|
||||||
const statusText = task.status === 'completed' ? task.result : task.status;
|
const statusText = task.status === 'completed' ? task.result : task.status;
|
||||||
|
|
||||||
|
// Build dimension badge
|
||||||
|
const dimensionBadge = task.dimension ? `<span class="task-dimension-badge">${task.dimension}</span>` : '';
|
||||||
|
|
||||||
|
// Build file info
|
||||||
|
const fileInfo = task.file ? `
|
||||||
|
<div class="task-file">
|
||||||
|
📄 ${task.file}${task.line ? `:${task.line}` : ''}
|
||||||
|
</div>
|
||||||
|
` : '';
|
||||||
|
|
||||||
|
// Build attempts indicator
|
||||||
|
const attemptsInfo = task.attempts && task.attempts > 1 ? `
|
||||||
|
<div class="task-meta-item task-attempts">
|
||||||
|
<span>🔄</span>
|
||||||
|
<span>${task.attempts} ${task.attempts === 1 ? 'attempt' : 'attempts'}</span>
|
||||||
|
</div>
|
||||||
|
` : '';
|
||||||
|
|
||||||
|
// Build commit hash
|
||||||
|
const commitInfo = task.commit_hash ? `
|
||||||
|
<div class="task-meta-item">
|
||||||
|
<span>💾</span>
|
||||||
|
<span>${task.commit_hash.substring(0, 7)}</span>
|
||||||
|
</div>
|
||||||
|
` : '';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="task-card status-${statusClass}">
|
<div class="task-card status-${statusClass}" onclick="openFindingDetails('${task.finding_id}')" style="cursor: pointer;">
|
||||||
<div class="task-header">
|
<div class="task-header">
|
||||||
<div class="task-id">${task.finding_id}</div>
|
<div class="task-id">${task.finding_id}</div>
|
||||||
<div class="task-status ${statusClass}">${statusText}</div>
|
<div class="task-status ${statusClass}">${statusText}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="margin-bottom: 8px;">
|
||||||
|
${dimensionBadge}
|
||||||
|
</div>
|
||||||
<div class="task-title">${task.finding_title || 'Untitled Task'}</div>
|
<div class="task-title">${task.finding_title || 'Untitled Task'}</div>
|
||||||
|
${fileInfo}
|
||||||
<div class="task-meta">
|
<div class="task-meta">
|
||||||
<div class="task-meta-item">
|
<div class="task-meta-item">
|
||||||
<span>🏷️</span>
|
<span>🏷️</span>
|
||||||
<span>${task.group_id || 'N/A'}</span>
|
<span>${task.group_id || 'N/A'}</span>
|
||||||
</div>
|
</div>
|
||||||
|
${attemptsInfo}
|
||||||
|
${commitInfo}
|
||||||
${task.completed_at ? `
|
${task.completed_at ? `
|
||||||
<div class="task-meta-item">
|
<div class="task-meta-item">
|
||||||
<span>⏱️</span>
|
<span>⏱️</span>
|
||||||
@@ -1401,7 +1687,7 @@
|
|||||||
let allCompleted = true;
|
let allCompleted = true;
|
||||||
|
|
||||||
progressDataArray.forEach(progressFile => {
|
progressDataArray.forEach(progressFile => {
|
||||||
// Collect all findings
|
// Collect all findings with enhanced details
|
||||||
if (progressFile.findings) {
|
if (progressFile.findings) {
|
||||||
progressFile.findings.forEach(finding => {
|
progressFile.findings.forEach(finding => {
|
||||||
allFindings.push({
|
allFindings.push({
|
||||||
@@ -1410,12 +1696,25 @@
|
|||||||
status: finding.status,
|
status: finding.status,
|
||||||
result: finding.result,
|
result: finding.result,
|
||||||
group_id: progressFile.group_id,
|
group_id: progressFile.group_id,
|
||||||
completed_at: finding.completed_at
|
completed_at: finding.completed_at,
|
||||||
|
// Enhanced fields
|
||||||
|
file: finding.file,
|
||||||
|
line: finding.line,
|
||||||
|
dimension: finding.dimension,
|
||||||
|
severity: finding.severity,
|
||||||
|
category: finding.category,
|
||||||
|
description: finding.description,
|
||||||
|
recommendations: finding.recommendations,
|
||||||
|
attempts: finding.attempts,
|
||||||
|
commit_hash: finding.commit_hash,
|
||||||
|
test_passed: finding.test_passed,
|
||||||
|
error_message: finding.error_message,
|
||||||
|
started_at: finding.started_at
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect active agents
|
// Collect active agents with errors
|
||||||
if (progressFile.assigned_agent && progressFile.status === 'in-progress') {
|
if (progressFile.assigned_agent && progressFile.status === 'in-progress') {
|
||||||
const currentFinding = progressFile.current_finding;
|
const currentFinding = progressFile.current_finding;
|
||||||
const flowControl = progressFile.flow_control;
|
const flowControl = progressFile.flow_control;
|
||||||
@@ -1428,7 +1727,8 @@
|
|||||||
file: currentFinding ? currentFinding.file : null,
|
file: currentFinding ? currentFinding.file : null,
|
||||||
status: currentFinding ? currentFinding.status : progressFile.phase,
|
status: currentFinding ? currentFinding.status : progressFile.phase,
|
||||||
started_at: progressFile.started_at,
|
started_at: progressFile.started_at,
|
||||||
flow_control: flowControl
|
flow_control: flowControl,
|
||||||
|
errors: progressFile.errors || []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1497,6 +1797,16 @@
|
|||||||
// Update global tasks array
|
// Update global tasks array
|
||||||
allTasks = allFindings;
|
allTasks = allFindings;
|
||||||
|
|
||||||
|
// Enhance groups with fix_strategy and risk_assessment from fixPlan
|
||||||
|
const enhancedGroups = progressDataArray.map(progressFile => {
|
||||||
|
const planGroup = fixPlan.groups?.find(g => g.group_id === progressFile.group_id);
|
||||||
|
return {
|
||||||
|
...progressFile,
|
||||||
|
fix_strategy: planGroup?.fix_strategy || null,
|
||||||
|
risk_assessment: planGroup?.risk_assessment || null
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fix_session_id: fixPlan.metadata?.fix_session_id || fixSession.fix_session_id,
|
fix_session_id: fixPlan.metadata?.fix_session_id || fixSession.fix_session_id,
|
||||||
review_id: fixPlan.metadata?.review_session_id || SESSION_ID,
|
review_id: fixPlan.metadata?.review_session_id || SESSION_ID,
|
||||||
@@ -1513,7 +1823,8 @@
|
|||||||
fixes: allFindings,
|
fixes: allFindings,
|
||||||
active_agents: activeAgents,
|
active_agents: activeAgents,
|
||||||
stages: stages,
|
stages: stages,
|
||||||
groups: progressDataArray
|
groups: enhancedGroups,
|
||||||
|
execution_strategy: fixPlan.execution_strategy || null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1628,6 +1939,11 @@
|
|||||||
const fixes = groupFixes[groupId] || [];
|
const fixes = groupFixes[groupId] || [];
|
||||||
const completedCount = fixes.filter(f => f.result === 'fixed' || f.result === 'failed').length;
|
const completedCount = fixes.filter(f => f.result === 'fixed' || f.result === 'failed').length;
|
||||||
|
|
||||||
|
// Get group details from enhanced groups
|
||||||
|
const groupData = progressData.groups?.find(g => g.group_id === groupId);
|
||||||
|
const fixStrategy = groupData?.fix_strategy;
|
||||||
|
const riskAssessment = groupData?.risk_assessment;
|
||||||
|
|
||||||
// Get in-progress findings
|
// Get in-progress findings
|
||||||
const inProgressFindings = fixes
|
const inProgressFindings = fixes
|
||||||
.filter(f => f.status === 'in-progress')
|
.filter(f => f.status === 'in-progress')
|
||||||
@@ -1638,6 +1954,9 @@
|
|||||||
? `<div class="group-active-items">🔧 ${inProgressFindings.join(', ')}</div>`
|
? `<div class="group-active-items">🔧 ${inProgressFindings.join(', ')}</div>`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
|
// Render strategy and risk info
|
||||||
|
const strategyRiskHtml = renderStrategyAndRisk(fixStrategy, riskAssessment);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="active-group-card">
|
<div class="active-group-card">
|
||||||
<div class="group-header">
|
<div class="group-header">
|
||||||
@@ -1646,11 +1965,61 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="group-findings">${completedCount}/${fixes.length} findings completed</div>
|
<div class="group-findings">${completedCount}/${fixes.length} findings completed</div>
|
||||||
${inProgressText}
|
${inProgressText}
|
||||||
|
${strategyRiskHtml}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderStrategyAndRisk(fixStrategy, riskAssessment) {
|
||||||
|
if (!fixStrategy && !riskAssessment) return '';
|
||||||
|
|
||||||
|
let html = '<div class="strategy-risk-container">';
|
||||||
|
|
||||||
|
// Strategy info
|
||||||
|
if (fixStrategy) {
|
||||||
|
html += '<div class="strategy-item">';
|
||||||
|
html += '<span class="strategy-label">Strategy:</span>';
|
||||||
|
html += `<span>${fixStrategy.approach || 'N/A'}</span>`;
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
if (fixStrategy.complexity) {
|
||||||
|
html += '<div class="strategy-item">';
|
||||||
|
html += '<span class="strategy-label">Complexity:</span>';
|
||||||
|
html += `<span>${fixStrategy.complexity}</span>`;
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fixStrategy.test_pattern) {
|
||||||
|
html += '<div class="strategy-item">';
|
||||||
|
html += '<span class="strategy-label">Test:</span>';
|
||||||
|
html += `<span style="font-family: monospace; font-size: 0.8rem;">${fixStrategy.test_pattern}</span>`;
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Risk info
|
||||||
|
if (riskAssessment) {
|
||||||
|
const riskLevel = riskAssessment.level || riskAssessment.overall || 'unknown';
|
||||||
|
const riskLevelClass = riskLevel.toLowerCase();
|
||||||
|
|
||||||
|
html += '<div class="risk-item">';
|
||||||
|
html += '<span class="risk-label">Risk:</span>';
|
||||||
|
html += `<span class="risk-level ${riskLevelClass}">${riskLevel.toUpperCase()}</span>`;
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
if (riskAssessment.impact_scope) {
|
||||||
|
html += '<div class="risk-item">';
|
||||||
|
html += '<span class="risk-label">Impact:</span>';
|
||||||
|
html += `<span>${riskAssessment.impact_scope}</span>`;
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
function updateActiveAgents(progressData) {
|
function updateActiveAgents(progressData) {
|
||||||
const activeAgentsSection = document.getElementById('activeAgentsSection');
|
const activeAgentsSection = document.getElementById('activeAgentsSection');
|
||||||
const activeAgentsList = document.getElementById('activeAgentsList');
|
const activeAgentsList = document.getElementById('activeAgentsList');
|
||||||
@@ -1674,6 +2043,16 @@
|
|||||||
? renderFlowControlSteps(agent.flow_control)
|
? renderFlowControlSteps(agent.flow_control)
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
|
// Render errors if any
|
||||||
|
const errorsHtml = agent.errors && agent.errors.length > 0
|
||||||
|
? renderAgentErrors(agent.errors)
|
||||||
|
: '';
|
||||||
|
|
||||||
|
// Error badge count
|
||||||
|
const errorBadge = agent.errors && agent.errors.length > 0
|
||||||
|
? `<span class="error-badge">⚠️ ${agent.errors.length} error${agent.errors.length > 1 ? 's' : ''}</span>`
|
||||||
|
: '';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="active-agent-item">
|
<div class="active-agent-item">
|
||||||
<div class="agent-header">
|
<div class="agent-header">
|
||||||
@@ -1682,12 +2061,14 @@
|
|||||||
<div class="agent-id">
|
<div class="agent-id">
|
||||||
${agent.agent_id} <span style="color: var(--text-secondary);">(${agent.group_id})</span>
|
${agent.agent_id} <span style="color: var(--text-secondary);">(${agent.group_id})</span>
|
||||||
${statusText ? `<span class="agent-status-badge">${statusText}</span>` : ''}
|
${statusText ? `<span class="agent-status-badge">${statusText}</span>` : ''}
|
||||||
|
${errorBadge}
|
||||||
</div>
|
</div>
|
||||||
<div class="agent-task">${agent.finding_title || 'Initializing...'}</div>
|
<div class="agent-task">${agent.finding_title || 'Initializing...'}</div>
|
||||||
${agent.file ? `<div class="agent-file">📄 ${agent.file}</div>` : ''}
|
${agent.file ? `<div class="agent-file">📄 ${agent.file}</div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${flowControlHtml}
|
${flowControlHtml}
|
||||||
|
${errorsHtml}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}).join('');
|
}).join('');
|
||||||
@@ -1746,6 +2127,31 @@
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderAgentErrors(errors) {
|
||||||
|
if (!errors || errors.length === 0) return '';
|
||||||
|
|
||||||
|
const errorsHtml = errors.slice(-3).map(error => {
|
||||||
|
const timestamp = error.timestamp ? new Date(error.timestamp).toLocaleTimeString() : '';
|
||||||
|
return `
|
||||||
|
<div class="error-item">
|
||||||
|
<div>${error.message || 'Unknown error'}</div>
|
||||||
|
<div class="error-meta">
|
||||||
|
${error.finding_id ? `Finding: ${error.finding_id}` : ''}
|
||||||
|
${error.error_type ? ` | Type: ${error.error_type}` : ''}
|
||||||
|
${timestamp ? ` | ${timestamp}` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="error-container">
|
||||||
|
<div class="error-header">Recent Errors${errors.length > 3 ? ` (showing last 3 of ${errors.length})` : ''}:</div>
|
||||||
|
${errorsHtml}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
// History drawer functions
|
// History drawer functions
|
||||||
function openHistoryDrawer() {
|
function openHistoryDrawer() {
|
||||||
document.getElementById('historyDrawer').classList.add('active');
|
document.getElementById('historyDrawer').classList.add('active');
|
||||||
@@ -1811,6 +2217,140 @@
|
|||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finding Details Modal Functions
|
||||||
|
function openFindingDetails(findingId) {
|
||||||
|
const finding = allTasks.find(f => f.finding_id === findingId);
|
||||||
|
if (!finding) {
|
||||||
|
console.error('Finding not found:', findingId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set modal icon based on status
|
||||||
|
const icon = finding.result === 'fixed' ? '✅' : finding.result === 'failed' ? '❌' : '📋';
|
||||||
|
document.getElementById('modalFindingIcon').textContent = icon;
|
||||||
|
|
||||||
|
// Build modal content
|
||||||
|
const modalBody = document.getElementById('modalBody');
|
||||||
|
modalBody.innerHTML = `
|
||||||
|
<!-- Basic Information -->
|
||||||
|
<div class="detail-section">
|
||||||
|
<h3>📋 Basic Information</h3>
|
||||||
|
<div class="detail-grid">
|
||||||
|
<div class="detail-label">Finding ID:</div>
|
||||||
|
<div class="detail-value"><code>${finding.finding_id}</code></div>
|
||||||
|
|
||||||
|
<div class="detail-label">Title:</div>
|
||||||
|
<div class="detail-value">${finding.finding_title || 'N/A'}</div>
|
||||||
|
|
||||||
|
<div class="detail-label">Status:</div>
|
||||||
|
<div class="detail-value">
|
||||||
|
<span class="task-status ${finding.status === 'completed' ? finding.result : finding.status}">
|
||||||
|
${finding.status === 'completed' ? finding.result : finding.status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-label">Group:</div>
|
||||||
|
<div class="detail-value">${finding.group_id || 'N/A'}</div>
|
||||||
|
|
||||||
|
${finding.dimension ? `
|
||||||
|
<div class="detail-label">Dimension:</div>
|
||||||
|
<div class="detail-value"><span class="task-dimension-badge">${finding.dimension}</span></div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${finding.severity ? `
|
||||||
|
<div class="detail-label">Severity:</div>
|
||||||
|
<div class="detail-value">${finding.severity}</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${finding.category ? `
|
||||||
|
<div class="detail-label">Category:</div>
|
||||||
|
<div class="detail-value">${finding.category}</div>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- File Location -->
|
||||||
|
${finding.file ? `
|
||||||
|
<div class="detail-section">
|
||||||
|
<h3>📄 File Location</h3>
|
||||||
|
<div class="detail-grid">
|
||||||
|
<div class="detail-label">File:</div>
|
||||||
|
<div class="detail-value"><code>${finding.file}</code></div>
|
||||||
|
|
||||||
|
${finding.line ? `
|
||||||
|
<div class="detail-label">Line:</div>
|
||||||
|
<div class="detail-value"><code>${finding.line}</code></div>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<!-- Description & Recommendations -->
|
||||||
|
${finding.description || finding.recommendations ? `
|
||||||
|
<div class="detail-section">
|
||||||
|
<h3>📝 Details</h3>
|
||||||
|
<div class="detail-grid">
|
||||||
|
${finding.description ? `
|
||||||
|
<div class="detail-label">Description:</div>
|
||||||
|
<div class="detail-value">${finding.description}</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${finding.recommendations ? `
|
||||||
|
<div class="detail-label">Recommendations:</div>
|
||||||
|
<div class="detail-value">${Array.isArray(finding.recommendations) ? finding.recommendations.join('<br>') : finding.recommendations}</div>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<!-- Fix Status -->
|
||||||
|
${finding.status === 'completed' || finding.attempts || finding.commit_hash ? `
|
||||||
|
<div class="detail-section">
|
||||||
|
<h3>🔧 Fix Status</h3>
|
||||||
|
<div class="detail-grid">
|
||||||
|
${finding.attempts ? `
|
||||||
|
<div class="detail-label">Attempts:</div>
|
||||||
|
<div class="detail-value ${finding.attempts > 1 ? 'task-attempts' : ''}">${finding.attempts}</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${finding.test_passed !== null && finding.test_passed !== undefined ? `
|
||||||
|
<div class="detail-label">Tests Passed:</div>
|
||||||
|
<div class="detail-value">${finding.test_passed ? '✅ Yes' : '❌ No'}</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${finding.commit_hash ? `
|
||||||
|
<div class="detail-label">Commit:</div>
|
||||||
|
<div class="detail-value"><code>${finding.commit_hash}</code></div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${finding.started_at ? `
|
||||||
|
<div class="detail-label">Started At:</div>
|
||||||
|
<div class="detail-value">${new Date(finding.started_at).toLocaleString()}</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${finding.completed_at ? `
|
||||||
|
<div class="detail-label">Completed At:</div>
|
||||||
|
<div class="detail-value">${new Date(finding.completed_at).toLocaleString()}</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${finding.error_message ? `
|
||||||
|
<div class="detail-label">Error:</div>
|
||||||
|
<div class="detail-value" style="color: var(--danger-color);">${finding.error_message}</div>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Show modal
|
||||||
|
document.getElementById('findingModal').classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeFindingModal(event) {
|
||||||
|
if (event && event.target !== event.currentTarget) return;
|
||||||
|
document.getElementById('findingModal').classList.remove('active');
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-refresh every 3 seconds
|
// Auto-refresh every 3 seconds
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (fixSession && progressInterval) {
|
if (fixSession && progressInterval) {
|
||||||
|
|||||||
Reference in New Issue
Block a user