@@ -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