refactor: design-fidelity pass on players page
This commit is contained in:
+40
-28
@@ -1,21 +1,27 @@
|
||||
<% var body = `
|
||||
<!-- Add Player Section -->
|
||||
<div class="card-section">
|
||||
<h3>Add Yourself to Tracked Players</h3>
|
||||
<div class="card-section-form">
|
||||
<input
|
||||
type="number"
|
||||
id="pdga-number-input"
|
||||
class="input"
|
||||
placeholder="Enter your PDGA number"
|
||||
min="1"
|
||||
style="width: 240px;"
|
||||
/>
|
||||
<button class="btn btn-add" onclick="searchAndAddPlayer()">
|
||||
<i class="fas fa-user-plus"></i> Add Player
|
||||
<!-- Add Player Card -->
|
||||
<form class="add-bar" onsubmit="searchAndAddPlayer(event)">
|
||||
<div class="add-bar-label">
|
||||
<span class="add-bar-kicker">Track a player</span>
|
||||
<span class="add-bar-hint">Add a PDGA number to start following their rating.</span>
|
||||
</div>
|
||||
<div class="add-bar-controls">
|
||||
<div class="input-wrap">
|
||||
<span class="input-prefix">#</span>
|
||||
<input
|
||||
type="text"
|
||||
id="pdga-number-input"
|
||||
inputmode="numeric"
|
||||
placeholder="277890"
|
||||
aria-label="PDGA number"
|
||||
oninput="this.value = this.value.replace(/[^0-9]/g, '')"
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">
|
||||
<i class="fas fa-user-plus"></i> Add player
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- KPI Summary Tiles -->
|
||||
<section class="kpi-strip" aria-label="Tracker overview">
|
||||
@@ -53,19 +59,25 @@
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px; margin-bottom: 16px;">
|
||||
<a href="#" onclick="clearCache(); return false;" style="color: var(--text-muted); font-size: 12px; text-decoration: none; opacity: 0.4;" title="Clear cache"><i class="fas fa-cog"></i></a>
|
||||
<!-- Players Table Card -->
|
||||
<div class="table-card">
|
||||
<div class="table-toolbar">
|
||||
<span class="kicker">TRACKED PLAYERS</span>
|
||||
<div class="toolbar-actions">
|
||||
<button id="trendchart-toggle" class="pill-toggle" type="button" aria-pressed="false">
|
||||
<svg class="pill-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M3 17l5-6 4 4 4-7 5 6" />
|
||||
</svg>
|
||||
<span class="pill-label">Trend chart</span>
|
||||
<span class="pill-dot"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ratings-table" hx-get="/partials/ratings-table" hx-trigger="load"></div>
|
||||
</div>
|
||||
<div class="table-toolbar">
|
||||
<button id="trendchart-toggle" class="pill-toggle" type="button" aria-pressed="false">
|
||||
<svg class="pill-icon" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||
<polyline points="1,9 4,5 7,7 11,2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span class="pill-label">Trend chart</span>
|
||||
<span class="pill-dot"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="ratings-table" hx-get="/partials/ratings-table" hx-trigger="load"></div>
|
||||
|
||||
<!-- Footnote -->
|
||||
<p class="footnote">Unofficial PDGA rating tracker. Ratings scraped from pdga.com on each refresh.</p>
|
||||
`; %>
|
||||
|
||||
<% var modals = `
|
||||
@@ -100,4 +112,4 @@
|
||||
initScript: 'setupTooltipsAfterSwap();',
|
||||
body: body,
|
||||
modals: modals
|
||||
}) %>
|
||||
}) %>
|
||||
|
||||
@@ -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><% } %>
|
||||
|
||||
@@ -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') { %>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user