Compare commits

...

4 Commits

Author SHA1 Message Date
Samuel Enocsson 088e283dcf refactor: split round spread and rating range into separate accordion rows (#19) 2026-05-25 08:02:05 +02:00
Samuel Enocsson 5791d8e34f refactor: move std-dev info to accordion, remove tooltip (#19)
- Add "Round spread" row (±stdDev, range lo–hi) to desktop accordion
  (player-history.ejs) and mobile card expanded section (ratings-cards.ejs)
- Remove .std-dev-tooltip div and .std-dev-inline span from table partial
- Remove stdDevTooltipText, updateStdDevInline, initRatingsTooltips helpers
  and all call sites from players.js
- Remove .std-dev-tooltip and .std-dev-inline CSS rules; drop cursor:help
  from .rating-value
2026-05-25 07:54:46 +02:00
Samuel Enocsson 1ff768e2fa fix: address std-dev inline span refresh + style fixes (#19)
- A: create inline span when missing in refreshRoundHistory (was silently dropped)
- B: updateStdDevInline also called from refreshHistoryThenCalculate
- C: extract stdDevTooltipText + updateStdDevInline helpers; replace 3 call sites
- D: remove margin-left: 4px and bump font-size to 12px on .std-dev-inline
- E: guard against stdDev === 0 in EJS (truthy → != null)
2026-05-23 06:45:39 +02:00
Samuel Enocsson c3fb850de3 fix: reposition std-dev tooltip and surface ±-spread inline (#19) 2026-05-23 06:40:08 +02:00
5 changed files with 20 additions and 71 deletions
-18
View File
@@ -94,24 +94,6 @@
box-shadow: var(--shadow-lg); box-shadow: var(--shadow-lg);
} }
/* ── Tooltips ─────────────────────────────────── */
.std-dev-tooltip {
position: absolute;
background: var(--navy-900);
color: var(--text-inverse);
padding: 6px 10px;
border-radius: var(--radius-sm);
font-size: 12px;
font-family: var(--font-mono);
pointer-events: none;
z-index: 10000;
display: none;
white-space: nowrap;
box-shadow: var(--shadow-lg);
font-weight: 400;
}
/* ── Debug Icon ───────────────────────────────── */ /* ── Debug Icon ───────────────────────────────── */
.debug-icon:hover { .debug-icon:hover {
-51
View File
@@ -57,7 +57,6 @@ function setupAfterTableSwap() {
document.body.addEventListener('htmx:afterSwap', function(event) { document.body.addEventListener('htmx:afterSwap', function(event) {
const target = event.detail.target; const target = event.detail.target;
if (target.id === 'ratings-table') { if (target.id === 'ratings-table') {
initRatingsTooltips();
initChartsIn(target); initChartsIn(target);
return; return;
} }
@@ -67,31 +66,6 @@ function setupAfterTableSwap() {
}); });
} }
function initRatingsTooltips() {
document.querySelectorAll('.predicted-value').forEach(span => {
const pdgaNumber = span.dataset.pdga;
const stdDev = span.dataset.stddev;
const tooltip = document.getElementById(`tooltip-stddev-${pdgaNumber}`);
if (stdDev && tooltip) {
setupTooltip(span, tooltip, () => `Standard Deviation: \u00b1${stdDev}`);
}
});
document.querySelectorAll('.rating-value').forEach(span => {
const pdgaNumber = span.dataset.pdga;
const rating = parseInt(span.dataset.rating);
const stdDev = parseInt(span.dataset.stddev);
const tooltip = document.getElementById(`tooltip-rating-${pdgaNumber}`);
if (rating && stdDev && tooltip) {
const minRating = rating - stdDev;
const maxRating = rating + stdDev;
setupTooltip(span, tooltip, () => `Rating Range: ${minRating} - ${maxRating} (\u00b1${stdDev})`);
}
});
}
function togglePlayerHistory(pdgaNumber) { function togglePlayerHistory(pdgaNumber) {
const historyRow = document.getElementById('history-' + pdgaNumber); const historyRow = document.getElementById('history-' + pdgaNumber);
const contentDiv = document.getElementById('history-content-' + pdgaNumber); const contentDiv = document.getElementById('history-content-' + pdgaNumber);
@@ -191,16 +165,6 @@ async function refreshPlayer(pdgaNumber) {
if (ratingValue) { if (ratingValue) {
ratingValue.textContent = data.player.rating || 'N/A'; ratingValue.textContent = data.player.rating || 'N/A';
ratingValue.dataset.rating = data.player.rating || ''; ratingValue.dataset.rating = data.player.rating || '';
const stdDev = parseInt(ratingValue.dataset.stddev);
const rating = parseInt(data.player.rating);
const tooltip = document.getElementById(`tooltip-rating-${pdgaNumber}`);
if (rating && stdDev && tooltip) {
const minRating = rating - stdDev;
const maxRating = rating + stdDev;
replaceWithTooltip(ratingValue, tooltip, () => `Rating Range: ${minRating} - ${maxRating} (\u00b1${stdDev})`);
}
} }
const deltaMonthPill = ratingCell ? ratingCell.querySelector('.delta-pill') : null; const deltaMonthPill = ratingCell ? ratingCell.querySelector('.delta-pill') : null;
@@ -232,11 +196,6 @@ async function refreshRoundHistory(pdgaNumber) {
if (predictedValue) { if (predictedValue) {
predictedValue.textContent = data.predictedRating || 'N/A'; predictedValue.textContent = data.predictedRating || 'N/A';
predictedValue.dataset.stddev = data.stdDev || ''; predictedValue.dataset.stddev = data.stdDev || '';
const tooltip = document.getElementById(`tooltip-stddev-${pdgaNumber}`);
if (data.stdDev && tooltip) {
replaceWithTooltip(predictedValue, tooltip, () => `Standard Deviation: \u00b1${data.stdDev}`);
}
} }
} }
@@ -245,16 +204,6 @@ async function refreshRoundHistory(pdgaNumber) {
const ratingValue = ratingCell ? ratingCell.querySelector('.rating-value') : null; const ratingValue = ratingCell ? ratingCell.querySelector('.rating-value') : null;
if (ratingValue && data.stdDev) { if (ratingValue && data.stdDev) {
ratingValue.dataset.stddev = data.stdDev; ratingValue.dataset.stddev = data.stdDev;
const rating = parseInt(ratingValue.dataset.rating);
const stdDev = parseInt(data.stdDev);
const ratingTooltip = document.getElementById(`tooltip-rating-${pdgaNumber}`);
if (rating && stdDev && ratingTooltip) {
const minRating = rating - stdDev;
const maxRating = rating + stdDev;
replaceWithTooltip(ratingValue, ratingTooltip, () => `Rating Range: ${minRating} - ${maxRating} (\u00b1${stdDev})`);
}
} }
} }
} catch (error) { } catch (error) {
+10
View File
@@ -26,6 +26,16 @@ const chartPdgaNumber = hasPlayer ? player.pdgaNumber : pdgaNumber;
<dt>Gap to predicted</dt> <dt>Gap to predicted</dt>
<dd><%- include('delta-pill', { value: player.deltaPredicted, extraClass: 'delta-predicted-pill' }) %></dd> <dd><%- include('delta-pill', { value: player.deltaPredicted, extraClass: 'delta-predicted-pill' }) %></dd>
</div> </div>
<% if (player.stdDev != null && player.rating) { %>
<div>
<dt>Round spread</dt>
<dd>±<%= player.stdDev %></dd>
</div>
<div>
<dt>Rating range</dt>
<dd><%= player.rating - player.stdDev %><%= player.rating + player.stdDev %></dd>
</div>
<% } %>
</dl> </dl>
<button class="link-btn" onclick="showDebugInfo(<%= player.pdgaNumber %>)" style="margin-top: 4px;">View calculation details →</button> <button class="link-btn" onclick="showDebugInfo(<%= player.pdgaNumber %>)" style="margin-top: 4px;">View calculation details →</button>
</div> </div>
+10
View File
@@ -114,6 +114,16 @@ function renderSparkline(values, opts) {
<dt>Gap to predicted</dt> <dt>Gap to predicted</dt>
<dd><span class="delta-pill <%= predCls %> delta-predicted-pill"><span class="delta-glyph"><%= predGlyph %></span><span class="delta-num"><%= predNum %></span></span></dd> <dd><span class="delta-pill <%= predCls %> delta-predicted-pill"><span class="delta-glyph"><%= predGlyph %></span><span class="delta-num"><%= predNum %></span></span></dd>
</div> </div>
<% if (player.stdDev != null && player.rating) { %>
<div>
<dt>Round spread</dt>
<dd>±<%= player.stdDev %></dd>
</div>
<div>
<dt>Rating range</dt>
<dd><%= player.rating - player.stdDev %><%= player.rating + player.stdDev %></dd>
</div>
<% } %>
</dl> </dl>
</div> </div>
</div> </div>
-2
View File
@@ -67,7 +67,6 @@ function renderSparkline(values, opts) {
<% } else { %> <% } else { %>
<span class="rating-pending">Click to load</span> <span class="rating-pending">Click to load</span>
<% } %> <% } %>
<div class="std-dev-tooltip" id="tooltip-rating-<%= player.pdgaNumber %>"></div>
</td> </td>
<td class="col-predicted" id="predicted-<%= player.pdgaNumber %>"> <td class="col-predicted" id="predicted-<%= player.pdgaNumber %>">
<% if (player.predictedRating) { %> <% if (player.predictedRating) { %>
@@ -78,7 +77,6 @@ function renderSparkline(values, opts) {
<% } else { %> <% } else { %>
<span class="rating-pending">—</span> <span class="rating-pending">—</span>
<% } %> <% } %>
<div class="std-dev-tooltip" id="tooltip-stddev-<%= player.pdgaNumber %>"></div>
</td> </td>
<td class="col-actions cell-actions" onclick="event.stopPropagation()"> <td class="col-actions cell-actions" onclick="event.stopPropagation()">
<button class="icon-btn refresh-icon" onclick="refreshPlayerData(<%= player.pdgaNumber %>)" title="Refresh rating + prediction" aria-label="Refresh rating and prediction"> <button class="icon-btn refresh-icon" onclick="refreshPlayerData(<%= player.pdgaNumber %>)" title="Refresh rating + prediction" aria-label="Refresh rating and prediction">