feat: add target rating calculator (#2)
This commit is contained in:
@@ -540,3 +540,124 @@ document.addEventListener('keydown', function(e) {
|
||||
const pdgaNumber = row.id.replace('row-', '');
|
||||
togglePlayerHistory(parseInt(pdgaNumber, 10));
|
||||
});
|
||||
|
||||
// ── Target Rating Calculator ───────────────────────
|
||||
function openTargetRatingModal(pdgaNumber) {
|
||||
const modal = document.getElementById('target-rating-modal');
|
||||
const header = document.getElementById('target-rating-modal-header');
|
||||
const pdgaField = document.getElementById('target-rating-pdga');
|
||||
const result = document.getElementById('target-rating-result');
|
||||
const targetInput = document.getElementById('target-rating-input');
|
||||
const roundsInput = document.getElementById('target-rounds-input');
|
||||
const submitBtn = document.getElementById('target-rating-submit');
|
||||
|
||||
const playerNameEl = document.querySelector('#row-' + pdgaNumber + ' .player-name a');
|
||||
const playerName = playerNameEl ? playerNameEl.textContent : 'PDGA #' + pdgaNumber;
|
||||
|
||||
header.textContent = 'Calculate Target Rating — ' + playerName;
|
||||
pdgaField.value = pdgaNumber;
|
||||
result.style.display = 'none';
|
||||
while (result.firstChild) result.removeChild(result.firstChild);
|
||||
targetInput.value = '';
|
||||
roundsInput.value = '4';
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Calculate';
|
||||
modal.style.display = 'flex';
|
||||
targetInput.focus();
|
||||
}
|
||||
|
||||
function _targetResultMsg(parent, cls, text) {
|
||||
const d = document.createElement('div');
|
||||
d.className = cls;
|
||||
d.textContent = text;
|
||||
parent.appendChild(d);
|
||||
}
|
||||
|
||||
async function calculateTargetRating(event) {
|
||||
if (event) event.preventDefault();
|
||||
const pdgaNumber = document.getElementById('target-rating-pdga').value;
|
||||
const targetRating = parseInt(document.getElementById('target-rating-input').value, 10);
|
||||
const rounds = parseInt(document.getElementById('target-rounds-input').value, 10);
|
||||
const result = document.getElementById('target-rating-result');
|
||||
const submitBtn = document.getElementById('target-rating-submit');
|
||||
|
||||
function clearResult() {
|
||||
while (result.firstChild) result.removeChild(result.firstChild);
|
||||
}
|
||||
|
||||
if (!Number.isInteger(targetRating) || targetRating < 400 || targetRating > 1200) {
|
||||
result.style.display = 'block';
|
||||
clearResult();
|
||||
_targetResultMsg(result, 'error', 'Target rating must be 400-1200.');
|
||||
return;
|
||||
}
|
||||
if (!Number.isInteger(rounds) || rounds < 1 || rounds > 20) {
|
||||
result.style.display = 'block';
|
||||
clearResult();
|
||||
_targetResultMsg(result, 'error', 'Rounds must be an integer 1-20.');
|
||||
return;
|
||||
}
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'Calculating...';
|
||||
result.style.display = 'block';
|
||||
clearResult();
|
||||
_targetResultMsg(result, 'loading', 'Calculating...');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/calculate-target-rating/' + pdgaNumber, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ targetRating: targetRating, rounds: rounds })
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
clearResult();
|
||||
|
||||
if (!response.ok || !data.success) {
|
||||
const msg = data.details ? data.error + ': ' + data.details : (data.error || 'Calculation failed');
|
||||
_targetResultMsg(result, 'error', msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const summary = document.createElement('div');
|
||||
summary.className = 'target-summary';
|
||||
|
||||
const avgLine = document.createElement('div');
|
||||
const avgStrong = document.createElement('strong');
|
||||
avgStrong.textContent = 'Required round average: ';
|
||||
avgLine.appendChild(avgStrong);
|
||||
avgLine.appendChild(document.createTextNode(String(data.requiredAverage)));
|
||||
summary.appendChild(avgLine);
|
||||
|
||||
const currentLine = document.createElement('div');
|
||||
currentLine.textContent = 'Current predicted rating: ' + data.currentRating;
|
||||
summary.appendChild(currentLine);
|
||||
|
||||
const simLine = document.createElement('div');
|
||||
simLine.textContent = 'Simulated predicted rating at this average: ' + data.predictedRating;
|
||||
summary.appendChild(simLine);
|
||||
|
||||
const mutedLine = document.createElement('div');
|
||||
mutedLine.className = 'muted';
|
||||
mutedLine.textContent = 'Across ' + data.rounds + ' synthetic rounds before the next PDGA update.';
|
||||
summary.appendChild(mutedLine);
|
||||
|
||||
if (data.warning) {
|
||||
_targetResultMsg(summary, 'warning', data.warning);
|
||||
}
|
||||
|
||||
result.appendChild(summary);
|
||||
} catch (err) {
|
||||
console.error('Error calculating target rating:', err);
|
||||
clearResult();
|
||||
_targetResultMsg(result, 'error', 'Network error. Please try again.');
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Calculate';
|
||||
}
|
||||
}
|
||||
|
||||
function closeTargetRatingModal(event) {
|
||||
document.getElementById('target-rating-modal').style.display = 'none';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user