refactor: design-fidelity pass on players page

This commit is contained in:
Samuel Enocsson
2026-05-21 15:15:29 +02:00
parent 686d7ca00c
commit 16d375ae10
7 changed files with 463 additions and 189 deletions
+5 -4
View File
@@ -4,7 +4,8 @@
extraClass {string} — optional additional CSS class (e.g. 'delta-predicted-pill')
*/%>
<% if (typeof value !== 'undefined' && value != null) {
const _cls = value > 0 ? 'up' : value < 0 ? 'down' : 'flat';
const _text = value > 0 ? '+' + value : value.toString();
const _xtra = (typeof extraClass !== 'undefined' && extraClass) ? ' ' + extraClass : '';
%><span class="delta-pill <%= _cls %><%= _xtra %>"><%= _text %></span><% } %>
const _cls = value > 0 ? 'up' : value < 0 ? 'down' : 'flat';
const _glyph = value > 0 ? '' : value < 0 ? '▼' : '';
const _num = value > 0 ? '+' + value : value.toString();
const _xtra = (typeof extraClass !== 'undefined' && extraClass) ? ' ' + extraClass : '';
%><span class="delta-pill <%= _cls %><%= _xtra %>"><span class="delta-glyph"><%= _glyph %></span><span class="delta-num"><%= _num %></span></span><% } %>
+1 -1
View File
@@ -7,7 +7,7 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,300..700;1,300..700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,300..800;1,300..800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<link rel="stylesheet" href="/css/shared.css">
<% if (typeof cssFiles !== 'undefined') { %>
+39 -31
View File
@@ -21,7 +21,7 @@ function renderSparkline(values) {
var last = pts[pts.length - 1];
var areaPath = linePath + ' L ' + last.x + ' ' + h + ' L 0 ' + h + ' Z';
return '<svg width="' + w + '" height="' + h + '" viewBox="0 0 ' + w + ' ' + h + '" style="display:block;overflow:visible">' +
return '<svg width="' + w + '" height="' + h + '" viewBox="0 0 ' + w + ' ' + h + '" class="spark" aria-hidden="true">' +
'<path d="' + areaPath + '" style="fill:var(--accent);fill-opacity:0.10"/>' +
'<path d="' + linePath + '" style="stroke:var(--accent);stroke-width:1.5;fill:none;stroke-linejoin:round;stroke-linecap:round"/>' +
'<circle cx="' + last.x + '" cy="' + last.y + '" r="2.5" style="fill:var(--accent)"/>' +
@@ -29,16 +29,16 @@ function renderSparkline(values) {
}
%>
<% if (ratings.length === 0) { %>
<p style="text-align: center; color: var(--text-muted); padding: 40px 0;">No ratings found.</p>
<p style="text-align: center; color: var(--ink-3); padding: 40px 0;">No players tracked yet.</p>
<% } else { %>
<table>
<thead>
<tr>
<th class="mobile-hide">Rank</th>
<th>Player Name</th>
<th class="mobile-hide">PDGA #</th>
<th>Rating</th>
<th class="mobile-hide">Predicted<span class="th-hint">next official update</span></th>
<th class="col-rank">#</th>
<th class="col-player">Player</th>
<th class="col-rating">Rating<span class="th-hint">+ Δ since last update</span></th>
<th class="col-predicted">Predicted<span class="th-hint">+ gap from today</span></th>
<th class="col-actions"></th>
</tr>
</thead>
<tbody>
@@ -46,41 +46,49 @@ function renderSparkline(values) {
const sparklineSvg = renderSparkline(player.monthlyHistory || []);
%>
<tr id="row-<%= player.pdgaNumber %>" class="expandable-row" tabindex="0" onclick="togglePlayerHistory(<%= player.pdgaNumber %>)">
<td class="mobile-hide"><%= index + 1 %></td>
<td class="player-name">
<a href="https://www.pdga.com/player/<%= player.pdgaNumber %>" target="_blank" onclick="event.stopPropagation()"><%= player.name %></a>
<div class="mobile-only pdga-number" style="font-size: 11px; margin-top: 2px;">PDGA #<%= player.pdgaNumber %></div>
<td class="col-rank">
<span class="rank-num mono"><%= index + 1 %></span>
</td>
<td class="pdga-number mobile-hide">#<%= player.pdgaNumber %></td>
<td class="rating">
<div class="refresh-section">
<span class="rating-value" data-rating="<%= player.rating || '' %>" data-stddev="<%= player.stdDev || '' %>" data-pdga="<%= player.pdgaNumber %>" style="cursor: help;"><%- player.rating || '<span style="color: var(--text-muted); font-style: italic;">Click refresh</span>' %></span>
<i class="fas fa-sync-alt refresh-icon" onclick="refreshPlayer(<%= player.pdgaNumber %>)" title="Refresh player data"></i>
<td class="col-player">
<div class="cell-name">
<span class="player-name"><a href="https://www.pdga.com/player/<%= player.pdgaNumber %>" target="_blank" onclick="event.stopPropagation()"><%= player.name %></a></span>
<span class="pdga-num mono">#<%= player.pdgaNumber %></span>
</div>
<%- include('delta-pill', { value: player.ratingChange }) %>
<% if (sparklineSvg) { %>
<span class="sparkline"><%- sparklineSvg %></span>
</td>
<td class="col-rating cell-rating">
<% if (player.rating) { %>
<div class="rating-stack">
<span class="rating-value rating-big mono" data-rating="<%= player.rating || '' %>" data-stddev="<%= player.stdDev || '' %>" data-pdga="<%= player.pdgaNumber %>"><%= player.rating %></span>
<%- include('delta-pill', { value: player.ratingChange }) %>
<% if (sparklineSvg) { %><span class="sparkline"><%- sparklineSvg %></span><% } %>
</div>
<% } else { %>
<span class="rating-pending">Click to load</span>
<% } %>
<div class="std-dev-tooltip" id="tooltip-rating-<%= player.pdgaNumber %>"></div>
</td>
<td class="predicted-rating mobile-hide" id="predicted-<%= player.pdgaNumber %>">
<div class="refresh-section">
<span class="predicted-value" data-stddev="<%= player.stdDev || '' %>" data-pdga="<%= player.pdgaNumber %>" style="cursor: help;"><%= player.predictedRating || 'N/A' %></span>
<i class="fas fa-question-circle debug-icon" onclick="showDebugInfo(<%= player.pdgaNumber %>)" title="Show calculation details" style="margin-left: 5px; color: var(--text-muted); cursor: pointer; opacity: 0.6;"></i>
<i class="fas fa-sync-alt refresh-icon" onclick="refreshRoundHistory(<%= player.pdgaNumber %>)" title="Refresh prediction data"></i>
<td class="col-predicted" id="predicted-<%= player.pdgaNumber %>">
<% if (player.predictedRating) { %>
<div class="pred-stack">
<span class="predicted-value pred-num mono" data-stddev="<%= player.stdDev || '' %>" data-pdga="<%= player.pdgaNumber %>"><%= player.predictedRating %></span>
<%- include('delta-pill', { value: player.deltaPredicted, extraClass: 'delta-predicted-pill' }) %>
</div>
<%- include('delta-pill', { value: player.deltaPredicted, extraClass: 'delta-predicted-pill' }) %>
<% } else { %>
<span class="rating-pending">—</span>
<% } %>
<div class="std-dev-tooltip" id="tooltip-stddev-<%= player.pdgaNumber %>"></div>
</td>
<td class="col-actions cell-actions" onclick="event.stopPropagation()">
<button class="icon-btn refresh-icon" onclick="refreshPlayer(<%= player.pdgaNumber %>)" title="Refresh player data" aria-label="Refresh">
<i class="fas fa-sync-alt"></i>
</button>
<button class="icon-btn icon-chev" onclick="togglePlayerHistory(<%= player.pdgaNumber %>)" title="Expand row" aria-label="Expand">
<i class="fas fa-chevron-down"></i>
</button>
</td>
</tr>
<tr id="history-<%= player.pdgaNumber %>" class="expanded-content">
<td colspan="5" class="expanded-cell">
<div class="chart-title">
<div class="refresh-section">
Rating History for <%= player.name %>
<i class="fas fa-sync-alt refresh-icon" onclick="refreshRatingHistory(<%= player.pdgaNumber %>)" title="Refresh rating history"></i>
</div>
</div>
<div id="history-content-<%= player.pdgaNumber %>">
<div class="loading-chart">Click to load rating history...</div>
</div>