Skip to content

Commit fefce4b

Browse files
committed
Enhance HTML report with challenged antipattern UX
- Add CSS styling for greyed-out challenged items with strikethrough - Create dedicated 'Challenged Issues' section with count badge - Implement dynamic movement of challenged items to separate section - Add localStorage persistence for challenged state across page loads - Update JavaScript to grey out items and move them when challenged - Group challenged items by spec file with timestamps and challenge types - Improve UX by visually distinguishing active vs challenged issues
1 parent ebd36df commit fefce4b

File tree

1 file changed

+252
-10
lines changed

1 file changed

+252
-10
lines changed

.pipelines/prchecks/CveSpecFilePRCheck/ResultAnalyzer.py

Lines changed: 252 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,21 @@ def generate_html_report(self, analysis_result: 'MultiSpecAnalysisResult', pr_me
647647
</details>
648648
"""
649649

650+
# Add challenged issues section (initially empty, populated by JavaScript)
651+
html += """
652+
<!-- Challenged Issues Section -->
653+
<div id="challenged-section" style="display: none;">
654+
<h3>
655+
💬 Challenged Issues
656+
<span class="challenged-badge" id="challenged-count">0</span>
657+
</h3>
658+
<p style="color: #8b949e; font-size: 13px; margin-bottom: 12px;">
659+
Issues that have been challenged by PR authors or reviewers. These findings remain documented but are marked for further review.
660+
</p>
661+
<div id="challenged-issues-container"></div>
662+
</div>
663+
"""
664+
650665
html += """
651666
</div>
652667
"""
@@ -803,6 +818,82 @@ def generate_multi_spec_report(self, analysis_result: 'MultiSpecAnalysisResult',
803818
cursor: not-allowed;
804819
}}
805820
821+
/* Challenged/Greyed out items */
822+
.challenged-item {{
823+
opacity: 0.5;
824+
text-decoration: line-through;
825+
color: #6e7681 !important;
826+
background: #161b2280 !important;
827+
border-left: 3px solid #58a6ff !important;
828+
padding-left: 8px;
829+
transition: opacity 0.3s ease, background 0.3s ease;
830+
}}
831+
832+
.challenged-item:hover {{
833+
opacity: 0.7;
834+
}}
835+
836+
/* Challenged section */
837+
#challenged-section {{
838+
margin-top: 20px;
839+
background: #161b22;
840+
border: 1px solid #30363d;
841+
border-radius: 6px;
842+
padding: 16px;
843+
}}
844+
845+
#challenged-section h3 {{
846+
color: #58a6ff;
847+
margin: 0 0 12px 0;
848+
font-size: 16px;
849+
display: flex;
850+
align-items: center;
851+
gap: 8px;
852+
}}
853+
854+
.challenged-badge {{
855+
background: #1f6feb20;
856+
color: #58a6ff;
857+
padding: 2px 8px;
858+
border-radius: 12px;
859+
font-size: 11px;
860+
font-weight: 600;
861+
}}
862+
863+
#challenged-issues-container {{
864+
display: none;
865+
}}
866+
867+
#challenged-issues-container.has-items {{
868+
display: block;
869+
}}
870+
871+
.challenged-spec-group {{
872+
margin-bottom: 16px;
873+
background: #0d1117;
874+
border: 1px solid #30363d;
875+
border-radius: 6px;
876+
padding: 12px;
877+
}}
878+
879+
.challenged-spec-name {{
880+
font-weight: 600;
881+
color: #8b949e;
882+
margin-bottom: 8px;
883+
font-size: 14px;
884+
}}
885+
886+
.challenged-issue {{
887+
padding: 8px;
888+
margin: 4px 0;
889+
background: #161b22;
890+
border-left: 3px solid #58a6ff;
891+
border-radius: 3px;
892+
font-size: 13px;
893+
color: #6e7681;
894+
text-decoration: line-through;
895+
}}
896+
806897
/* Challenge Modal */
807898
#challenge-modal {{
808899
display: none;
@@ -1127,12 +1218,143 @@ def generate_multi_spec_report(self, analysis_result: 'MultiSpecAnalysisResult',
11271218
document.addEventListener('DOMContentLoaded', () => {{
11281219
RADAR_AUTH.init();
11291220
initializeChallengeButtons();
1221+
restoreChallengedState(); // Restore previously challenged items
11301222
}});
11311223
11321224
// ============================================================================
11331225
// Challenge/Feedback Module
11341226
// ============================================================================
11351227
1228+
// LocalStorage key for challenged issues
1229+
const CHALLENGED_ITEMS_KEY = 'radar_challenged_items_pr_{pr_number or 0}';
1230+
1231+
function getChallengedItems() {{
1232+
const stored = localStorage.getItem(CHALLENGED_ITEMS_KEY);
1233+
return stored ? JSON.parse(stored) : {{}};
1234+
}}
1235+
1236+
function saveChallengedItems(items) {{
1237+
localStorage.setItem(CHALLENGED_ITEMS_KEY, JSON.stringify(items));
1238+
}}
1239+
1240+
function moveToChallengedSection(item) {{
1241+
console.log('📦 Moving to challenged section:', item);
1242+
1243+
// Get or create challenged items store
1244+
const challengedItems = getChallengedItems();
1245+
1246+
// Add to store (keyed by issue hash for deduplication)
1247+
challengedItems[item.issueHash] = {{
1248+
spec: item.spec,
1249+
issueType: item.issueType,
1250+
description: item.description,
1251+
challengeType: item.challengeType,
1252+
timestamp: new Date().toISOString()
1253+
}};
1254+
1255+
saveChallengedItems(challengedItems);
1256+
renderChallengedSection();
1257+
updateIssueCounts();
1258+
}}
1259+
1260+
function renderChallengedSection() {{
1261+
const section = document.getElementById('challenged-section');
1262+
const container = document.getElementById('challenged-issues-container');
1263+
const countBadge = document.getElementById('challenged-count');
1264+
1265+
if (!section || !container || !countBadge) {{
1266+
console.warn('⚠️ Challenged section elements not found');
1267+
return;
1268+
}}
1269+
1270+
const challengedItems = getChallengedItems();
1271+
const itemsArray = Object.entries(challengedItems);
1272+
1273+
if (itemsArray.length === 0) {{
1274+
section.style.display = 'none';
1275+
return;
1276+
}}
1277+
1278+
// Show section and update count
1279+
section.style.display = 'block';
1280+
countBadge.textContent = itemsArray.length;
1281+
1282+
// Group by spec file
1283+
const bySpec = {{}};
1284+
itemsArray.forEach(([hash, item]) => {{
1285+
if (!bySpec[item.spec]) {{
1286+
bySpec[item.spec] = [];
1287+
}}
1288+
bySpec[item.spec].push({{ hash, ...item }});
1289+
}});
1290+
1291+
// Render grouped items
1292+
let html = '';
1293+
Object.entries(bySpec).forEach(([spec, items]) => {{
1294+
html += `
1295+
<div class="challenged-spec-group">
1296+
<div class="challenged-spec-name">📄 ${{spec}}</div>
1297+
`;
1298+
items.forEach(item => {{
1299+
const typeIcon = item.challengeType === 'false-positive' ? '🟢' :
1300+
item.challengeType === 'needs-context' ? '🟡' : '🔴';
1301+
html += `
1302+
<div class="challenged-issue">
1303+
${{typeIcon}} <strong>${{item.issueType}}:</strong> ${{item.description}}
1304+
<br><span style="font-size: 11px; color: #6e7681;">Challenged: ${{new Date(item.timestamp).toLocaleString()}}</span>
1305+
</div>
1306+
`;
1307+
}});
1308+
html += `</div>`;
1309+
}});
1310+
1311+
container.innerHTML = html;
1312+
console.log(`✅ Rendered ${{itemsArray.length}} challenged items`);
1313+
}}
1314+
1315+
function updateIssueCounts() {{
1316+
// Update error/warning counts based on challenged items
1317+
const challengedItems = getChallengedItems();
1318+
const challengedCount = Object.keys(challengedItems).length;
1319+
1320+
console.log(`📊 ${{challengedCount}} items challenged`);
1321+
1322+
// You could also update the main stats cards here if needed
1323+
// For example, decrement the error/warning counts
1324+
}}
1325+
1326+
function restoreChallengedState() {{
1327+
console.log('🔄 Restoring challenged items from localStorage...');
1328+
1329+
const challengedItems = getChallengedItems();
1330+
const itemsArray = Object.entries(challengedItems);
1331+
1332+
if (itemsArray.length === 0) {{
1333+
console.log(' No challenged items to restore');
1334+
return;
1335+
}}
1336+
1337+
console.log(` Restoring ${{itemsArray.length}} challenged items`);
1338+
1339+
// Grey out buttons and list items for challenged issues
1340+
itemsArray.forEach(([hash, item]) => {{
1341+
const btn = document.querySelector(`button[data-issue-hash="${{hash}}"]`);
1342+
1343+
if (btn && btn.tagName === 'BUTTON') {{
1344+
const listItem = btn.closest('li');
1345+
1346+
if (listItem) {{
1347+
listItem.classList.add('challenged-item');
1348+
btn.textContent = '✅ Challenged';
1349+
btn.disabled = true;
1350+
}}
1351+
}}
1352+
}});
1353+
1354+
// Render the challenged section
1355+
renderChallengedSection();
1356+
}}
1357+
11361358
function initializeChallengeButtons() {{
11371359
const challengeButtons = document.querySelectorAll('.challenge-btn');
11381360
challengeButtons.forEach(btn => {{
@@ -1377,26 +1599,46 @@ def generate_multi_spec_report(self, analysis_result: 'MultiSpecAnalysisResult',
13771599
}}
13781600
13791601
alert(message);
1380-
closeChallengeModal();
13811602
1382-
// Mark button as submitted - MUST be a button element, not the modal!
1603+
// Move item to challenged section and grey it out
13831604
const findingId = modal.dataset.findingId;
1384-
console.log('🔍 Looking for button with finding-id:', findingId);
1605+
const issueHash = modal.dataset.issueHash;
1606+
const spec = modal.dataset.spec;
1607+
const issueType = modal.dataset.issueType;
1608+
const description = modal.dataset.description;
1609+
1610+
console.log('🔍 Moving item to challenged section:', findingId);
13851611
1386-
if (findingId) {{
1387-
const btn = document.querySelector(`button[data-finding-id="${{findingId}}"]`);
1388-
console.log('🔍 Button found:', !!btn, 'tagName:', btn ? btn.tagName : 'N/A');
1612+
// Find the list item containing the challenge button
1613+
const btn = document.querySelector(`button[data-finding-id="${{findingId}}"]`);
1614+
1615+
if (btn && btn.tagName === 'BUTTON') {{
1616+
const listItem = btn.closest('li');
13891617
1390-
if (btn && btn.tagName === 'BUTTON') {{
1618+
if (listItem) {{
1619+
// Apply greyed-out styling
1620+
listItem.classList.add('challenged-item');
13911621
btn.textContent = '✅ Challenged';
13921622
btn.disabled = true;
1393-
console.log('✅ Button marked as challenged');
1623+
1624+
// Store in challenged issues
1625+
moveToChallengedSection({{
1626+
spec: spec,
1627+
issueType: issueType,
1628+
description: description,
1629+
issueHash: issueHash,
1630+
challengeType: challengeType.value
1631+
}});
1632+
1633+
console.log('✅ Item marked as challenged and moved');
13941634
}} else {{
1395-
console.warn('⚠️ Could not find challenge button or wrong element type');
1635+
console.warn('⚠️ Could not find list item parent');
13961636
}}
13971637
}} else {{
1398-
console.warn('⚠️ No findingId in modal dataset');
1638+
console.warn('⚠️ Could not find challenge button');
13991639
}}
1640+
1641+
closeChallengeModal();
14001642
}} else {{
14011643
console.error('❌ Server error:', result);
14021644

0 commit comments

Comments
 (0)