Fix: load adjusted score when reopening document from history

handleResult() now overlays accessibility_score/wcag_compliance from
.adjusted.json (if it exists) while keeping the original severity_counts
as the recalculation baseline — prevents double-subtraction.

displayResults() auto-calls applyScoreRecalc() on load when the result
was previously adjusted, restoring the (Adjusted) label and WCAG badges
without triggering another server save.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-03-18 11:04:07 +00:00
parent 7bcf31622f
commit 2a8db06f0d
2 changed files with 29 additions and 6 deletions

12
api.php
View file

@ -673,6 +673,18 @@ function handleResult() {
$result = json_decode(file_get_contents($result_file), true);
// If an adjusted result exists, overlay only the score/wcag fields so the
// frontend can display the adjusted score on reload while keeping the original
// severity_counts and score_breakdown as the recalculation baseline.
$adjusted_file = RESULTS_DIR . '/' . $job_id . '.adjusted.json';
if (file_exists($adjusted_file)) {
$adjusted = json_decode(file_get_contents($adjusted_file), true);
$result['accessibility_score'] = $adjusted['accessibility_score'] ?? $result['accessibility_score'];
$result['grade'] = $adjusted['grade'] ?? $result['grade'];
$result['wcag_compliance'] = $adjusted['wcag_compliance'] ?? $result['wcag_compliance'];
$result['score_breakdown']['adjusted'] = true;
}
// Inject dismissed indices so frontend can restore dismiss state on reload
$dismiss_file = RESULTS_DIR . '/' . $job_id . '.dismissed.json';
$result['dismissed_indices'] = file_exists($dismiss_file)

View file

@ -58,6 +58,10 @@ function displayResults(data) {
displayScoreBreakdown(data.score_breakdown);
renderRecalcButton();
displayIssues(allIssues);
// If this result was previously adjusted, restore the adjusted view without saving again
if (data.score_breakdown?.adjusted && (dismissedIndices.size > 0 || overriddenChecks.size > 0)) {
applyScoreRecalc();
}
initializePageViewer(data);
displayRemediationOptions(data);
lastMatterhornData = data.matterhorn_summary || null;
@ -623,8 +627,9 @@ function renderRecalcButton() {
if (btn) btn.style.display = 'inline-block';
}
async function recalculateScore() {
if (!scoreBreakdownData || !originalSeverityCounts) return;
// Pure DOM update — called both on user action and on initial load of adjusted result
function applyScoreRecalc() {
if (!scoreBreakdownData || !originalSeverityCounts) return null;
const bd = scoreBreakdownData;
const origSC = originalSeverityCounts;
@ -645,7 +650,7 @@ async function recalculateScore() {
const new_passed = Math.min(bd.checks_total, bd.checks_passed + overriddenChecks.size);
const new_base = bd.checks_total > 0 ? Math.round(100 * new_passed / bd.checks_total) : 0;
// 4. Final score + grade
// 4. Final score
const new_score = Math.max(0, new_base - new_penalty);
// 5. Update DOM
@ -656,7 +661,7 @@ async function recalculateScore() {
updateStatsGrid(adj_crit, adj_err);
updateBreakdownSummary(new_passed, bd.checks_total, new_base, new_penalty, new_score);
// 6. Recompute WCAG compliance badges based on remaining non-dismissed issues
// 6. Recompute WCAG compliance badges
const failingA = [], failingAA = [];
allIssues.forEach((issue, idx) => {
if (dismissedIndices.has(idx)) return;
@ -675,8 +680,14 @@ async function recalculateScore() {
level_aa_failures: failingAA,
});
// 7. Persist adjusted result to server so history + exports reflect the new score
if (!currentJobId) return;
return new_score;
}
async function recalculateScore() {
const new_score = applyScoreRecalc();
if (new_score === null || !currentJobId) return;
// Persist adjusted result so history + exports reflect the new score
try {
const btn = document.getElementById('recheckBtn');
if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }