refactor: design-fidelity pass on players page
This commit is contained in:
+1
-15
@@ -2,15 +2,7 @@
|
|||||||
Players Page
|
Players Page
|
||||||
═══════════════════════════════════════════════════ */
|
═══════════════════════════════════════════════════ */
|
||||||
|
|
||||||
/* ── Add Player Button ─────────────────────────── */
|
/* ── (Add player now uses .btn-primary from shared.css) ── */
|
||||||
|
|
||||||
.btn-add {
|
|
||||||
background: var(--green);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-add:hover {
|
|
||||||
background: #059669;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Mobile helpers ───────────────────────────── */
|
/* ── Mobile helpers ───────────────────────────── */
|
||||||
|
|
||||||
@@ -30,12 +22,6 @@
|
|||||||
|
|
||||||
/* ── Rating values ────────────────────────────── */
|
/* ── Rating values ────────────────────────────── */
|
||||||
|
|
||||||
.rating {
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--accent);
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rating-value {
|
.rating-value {
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
|
|||||||
+343
-89
@@ -293,9 +293,12 @@ body {
|
|||||||
/* ── Container ────────────────────────────────── */
|
/* ── Container ────────────────────────────────── */
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 960px;
|
max-width: 1180px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 28px 24px 60px;
|
padding: 28px 32px 60px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
@@ -303,7 +306,7 @@ body {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
letter-spacing: -0.03em;
|
letter-spacing: -0.03em;
|
||||||
margin: 0 0 24px 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Tables ───────────────────────────────────── */
|
/* ── Tables ───────────────────────────────────── */
|
||||||
@@ -322,28 +325,23 @@ thead {
|
|||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
padding: 10px 16px;
|
padding: 0 16px;
|
||||||
|
height: 48px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.08em;
|
||||||
color: var(--text-secondary);
|
color: var(--ink-3);
|
||||||
background: var(--surface-2);
|
background: var(--paper-2);
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--line);
|
||||||
}
|
vertical-align: middle;
|
||||||
|
|
||||||
th:first-child {
|
|
||||||
border-radius: var(--radius-md) 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
th:last-child {
|
|
||||||
border-radius: 0 var(--radius-md) 0 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
padding: 12px 16px;
|
padding: 0 16px;
|
||||||
border-bottom: 1px solid var(--border-light);
|
height: 64px;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,13 +349,116 @@ tr:last-child td {
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Column widths */
|
||||||
|
.col-rank { width: 56px; text-align: center; }
|
||||||
|
.col-player { min-width: 200px; }
|
||||||
|
.col-rating { min-width: 220px; }
|
||||||
|
.col-predicted { min-width: 180px; }
|
||||||
|
.col-actions { width: 72px; }
|
||||||
|
|
||||||
|
/* Rank number */
|
||||||
|
.rank-num {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--ink-3);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Player name cell */
|
||||||
|
.cell-name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-name a {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
letter-spacing: -0.005em;
|
||||||
|
color: var(--ink);
|
||||||
|
text-decoration: none;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-name a:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdga-num {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--ink-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rating cell stack */
|
||||||
|
.cell-rating {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-stack {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-big {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ink);
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-pending {
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--ink-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Predicted cell stack */
|
||||||
|
.pred-stack {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pred-num {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ink-2);
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actions cell */
|
||||||
|
.cell-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chevron rotation when row open */
|
||||||
|
tr.row-open .icon-chev i {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-chev i {
|
||||||
|
transition: transform 180ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
.expandable-row {
|
.expandable-row {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background var(--transition);
|
transition: background 120ms ease;
|
||||||
|
outline: none;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expandable-row:hover {
|
.expandable-row:hover {
|
||||||
background: var(--accent-subtle);
|
background: var(--hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.expanded-content {
|
.expanded-content {
|
||||||
@@ -376,7 +477,7 @@ tr:last-child td {
|
|||||||
.expanded-content td {
|
.expanded-content td {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: color-mix(in oklab, var(--accent) 4%, var(--paper-2));
|
background: color-mix(in oklab, var(--accent) 4%, var(--paper-2));
|
||||||
border-top: 2px solid var(--accent);
|
border-bottom: 1px solid var(--line);
|
||||||
}
|
}
|
||||||
|
|
||||||
.expanded-cell {
|
.expanded-cell {
|
||||||
@@ -506,31 +607,129 @@ a:hover {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Card Section ─────────────────────────────── */
|
/* ── Add Player Bar ───────────────────────────── */
|
||||||
|
|
||||||
.card-section {
|
.add-bar {
|
||||||
background: var(--surface-1);
|
background: var(--paper);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--line);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius);
|
||||||
padding: 24px;
|
padding: 20px 24px;
|
||||||
margin-bottom: 28px;
|
display: flex;
|
||||||
box-shadow: var(--shadow-sm);
|
align-items: center;
|
||||||
}
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
.card-section h3 {
|
box-shadow: var(--shadow-card);
|
||||||
margin: 0 0 14px 0;
|
}
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 15px;
|
.add-bar-label {
|
||||||
font-weight: 600;
|
display: flex;
|
||||||
text-align: center;
|
flex-direction: column;
|
||||||
}
|
gap: 2px;
|
||||||
|
}
|
||||||
.card-section-form {
|
|
||||||
|
.add-bar-kicker {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-bar-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--ink-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-bar-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
flex-wrap: wrap;
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrap {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--paper-2);
|
||||||
|
border: 1px solid var(--line-2);
|
||||||
|
border-radius: 10px;
|
||||||
|
height: 40px;
|
||||||
|
width: 220px;
|
||||||
|
transition: border-color 150ms ease, box-shadow 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrap:focus-within {
|
||||||
|
border-color: var(--accent);
|
||||||
|
box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 14%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-prefix {
|
||||||
|
padding: 0 0 0 14px;
|
||||||
|
color: var(--ink-3);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrap input {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
padding: 0 14px 0 6px;
|
||||||
|
height: 100%;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--ink);
|
||||||
|
font-feature-settings: "tnum";
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrap input::placeholder {
|
||||||
|
color: var(--ink-3);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 18px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 1px 0 color-mix(in oklab, var(--accent) 50%, black), 0 1px 3px color-mix(in oklab, var(--accent) 40%, transparent);
|
||||||
|
transition: transform 80ms ease, box-shadow 150ms ease, background 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background: color-mix(in oklab, var(--accent) 92%, black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.add-bar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-bar-controls {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrap {
|
||||||
|
flex: 1;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Inputs ───────────────────────────────────── */
|
/* ── Inputs ───────────────────────────────────── */
|
||||||
@@ -556,20 +755,6 @@ a:hover {
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.card-section {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-section-form {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-section-form .input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Refresh Icons ────────────────────────────── */
|
/* ── Refresh Icons ────────────────────────────── */
|
||||||
|
|
||||||
.refresh-icon {
|
.refresh-icon {
|
||||||
@@ -601,13 +786,10 @@ a:hover {
|
|||||||
|
|
||||||
/* ── Responsive ───────────────────────────────── */
|
/* ── Responsive ───────────────────────────────── */
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 880px) {
|
||||||
.container {
|
.container {
|
||||||
padding: 16px 12px 40px;
|
padding: 18px 16px 40px;
|
||||||
}
|
gap: 16px;
|
||||||
|
|
||||||
.page-title {
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
@@ -615,25 +797,17 @@ a:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
th, td {
|
th, td {
|
||||||
padding: 10px 8px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
.col-rank { width: 40px; }
|
||||||
font-size: 10px;
|
.col-actions { width: 40px; }
|
||||||
}
|
.col-predicted { display: none; }
|
||||||
|
|
||||||
.mobile-hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead {
|
|
||||||
top: var(--topbar-height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 768px) {
|
||||||
th, td {
|
.mobile-hide {
|
||||||
padding: 8px 6px;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,13 +816,27 @@ a:hover {
|
|||||||
.delta-pill {
|
.delta-pill {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 2px 8px;
|
gap: 4px;
|
||||||
|
padding: 3px 8px 3px 6px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delta-glyph {
|
||||||
|
font-size: 9px;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1;
|
||||||
|
position: relative;
|
||||||
|
top: -0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delta-num {
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
font-feature-settings: "tnum", "zero";
|
font-feature-settings: "tnum", "zero";
|
||||||
font-size: 11px;
|
letter-spacing: 0;
|
||||||
font-weight: 500;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.delta-pill.up {
|
.delta-pill.up {
|
||||||
@@ -664,6 +852,7 @@ a:hover {
|
|||||||
.delta-pill.flat {
|
.delta-pill.flat {
|
||||||
background: var(--paper-2);
|
background: var(--paper-2);
|
||||||
color: var(--ink-3);
|
color: var(--ink-3);
|
||||||
|
border: 1px solid var(--line);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Table Header Hints ───────────────────────── */
|
/* ── Table Header Hints ───────────────────────── */
|
||||||
@@ -684,9 +873,9 @@ a:hover {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
gap: 14px;
|
gap: 14px;
|
||||||
margin-bottom: 16px;
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kpi-tile {
|
.kpi-tile {
|
||||||
@@ -749,14 +938,62 @@ a:hover {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Table Toolbar + Pill Toggle ─────────────── */
|
/* ── Table Card + Toolbar ─────────────────────── */
|
||||||
|
|
||||||
|
.table-card {
|
||||||
|
background: var(--paper);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
box-shadow: var(--shadow-card);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kicker {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.10em;
|
||||||
|
color: var(--ink-3);
|
||||||
|
}
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
justify-content: space-between;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbar-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Icon Buttons ─────────────────────────────── */
|
||||||
|
|
||||||
|
.icon-btn {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
color: var(--ink-3);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn:hover {
|
||||||
|
background: var(--hover);
|
||||||
|
color: var(--ink);
|
||||||
|
border-color: var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Table Pill Toggle ────────────────────────── */
|
||||||
|
|
||||||
.pill-toggle {
|
.pill-toggle {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -808,12 +1045,29 @@ a:hover {
|
|||||||
/* ── Sparklines ───────────────────────────────── */
|
/* ── Sparklines ───────────────────────────────── */
|
||||||
|
|
||||||
.sparkline {
|
.sparkline {
|
||||||
display: inline-block;
|
display: block;
|
||||||
vertical-align: middle;
|
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
margin-top: 4px;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body[data-sparklines="off"] .sparkline {
|
.spark {
|
||||||
|
display: block;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[data-sparklines="off"] .sparkline,
|
||||||
|
body[data-sparklines="off"] .spark {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Footnote ─────────────────────────────────── */
|
||||||
|
|
||||||
|
.footnote {
|
||||||
|
font-size: 11.5px;
|
||||||
|
color: var(--ink-3);
|
||||||
|
text-align: center;
|
||||||
|
max-width: 640px;
|
||||||
|
margin: 0 auto;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|||||||
+34
-21
@@ -5,9 +5,25 @@ let openPdgaNumber = null;
|
|||||||
// ── Delta-pill helper ─────────────────────────────
|
// ── Delta-pill helper ─────────────────────────────
|
||||||
function renderDeltaPill(value, extraClass) {
|
function renderDeltaPill(value, extraClass) {
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
const cls = value > 0 ? 'up' : value < 0 ? 'down' : 'flat';
|
const cls = value > 0 ? 'up' : value < 0 ? 'down' : 'flat';
|
||||||
const text = value > 0 ? '+' + value : String(value);
|
const glyph = value > 0 ? '▲' : value < 0 ? '▼' : '–';
|
||||||
return { text, cls };
|
const num = value > 0 ? '+' + value : String(value);
|
||||||
|
return { glyph, num, cls };
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDeltaPill(pillEl, value) {
|
||||||
|
if (!pillEl || value == null) return;
|
||||||
|
const pill = renderDeltaPill(value);
|
||||||
|
pillEl.className = 'delta-pill ' + pill.cls;
|
||||||
|
while (pillEl.firstChild) pillEl.removeChild(pillEl.firstChild);
|
||||||
|
const glyphSpan = document.createElement('span');
|
||||||
|
glyphSpan.className = 'delta-glyph';
|
||||||
|
glyphSpan.textContent = pill.glyph;
|
||||||
|
const numSpan = document.createElement('span');
|
||||||
|
numSpan.className = 'delta-num';
|
||||||
|
numSpan.textContent = pill.num;
|
||||||
|
pillEl.appendChild(glyphSpan);
|
||||||
|
pillEl.appendChild(numSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupTooltipsAfterSwap() {
|
function setupTooltipsAfterSwap() {
|
||||||
@@ -120,8 +136,8 @@ async function clearCache() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function refreshPlayer(pdgaNumber) {
|
async function refreshPlayer(pdgaNumber) {
|
||||||
const icon = document.querySelector(`#row-${pdgaNumber} .rating .refresh-icon`);
|
const icon = document.querySelector(`#row-${pdgaNumber} .cell-actions .refresh-icon`);
|
||||||
icon.classList.add('spinning');
|
if (icon) icon.classList.add('spinning');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/refresh-player/${pdgaNumber}`, { method: 'POST' });
|
const response = await fetch(`/api/refresh-player/${pdgaNumber}`, { method: 'POST' });
|
||||||
@@ -129,12 +145,12 @@ async function refreshPlayer(pdgaNumber) {
|
|||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
const row = document.getElementById(`row-${pdgaNumber}`);
|
const row = document.getElementById(`row-${pdgaNumber}`);
|
||||||
const ratingCell = row.querySelector('.rating');
|
const ratingCell = row.querySelector('.cell-rating');
|
||||||
|
|
||||||
const nameLink = row.querySelector('.player-name a');
|
const nameLink = row.querySelector('.player-name a');
|
||||||
nameLink.textContent = data.player.name;
|
if (nameLink) nameLink.textContent = data.player.name;
|
||||||
|
|
||||||
const ratingValue = ratingCell.querySelector('.rating-value');
|
const ratingValue = ratingCell ? ratingCell.querySelector('.rating-value') : null;
|
||||||
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 || '';
|
||||||
@@ -150,24 +166,20 @@ async function refreshPlayer(pdgaNumber) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const deltaMonthPill = ratingCell.querySelector('.delta-pill');
|
const deltaMonthPill = ratingCell ? ratingCell.querySelector('.delta-pill') : null;
|
||||||
if (deltaMonthPill && data.player.ratingChange != null) {
|
applyDeltaPill(deltaMonthPill, data.player.ratingChange);
|
||||||
const pill = renderDeltaPill(data.player.ratingChange);
|
|
||||||
deltaMonthPill.textContent = pill.text;
|
|
||||||
deltaMonthPill.className = `delta-pill ${pill.cls}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error refreshing player:', error);
|
console.error('Error refreshing player:', error);
|
||||||
alert('Failed to refresh player data');
|
alert('Failed to refresh player data');
|
||||||
} finally {
|
} finally {
|
||||||
icon.classList.remove('spinning');
|
if (icon) icon.classList.remove('spinning');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshRoundHistory(pdgaNumber) {
|
async function refreshRoundHistory(pdgaNumber) {
|
||||||
const icon = document.querySelector(`#predicted-${pdgaNumber} .refresh-icon`);
|
const icon = document.querySelector(`#row-${pdgaNumber} .cell-actions .refresh-icon`);
|
||||||
icon.classList.add('spinning');
|
if (icon) icon.classList.add('spinning');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/refresh-round-history/${pdgaNumber}`, { method: 'POST' });
|
const response = await fetch(`/api/refresh-round-history/${pdgaNumber}`, { method: 'POST' });
|
||||||
@@ -197,8 +209,8 @@ async function refreshRoundHistory(pdgaNumber) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const row = document.getElementById(`row-${pdgaNumber}`);
|
const row = document.getElementById(`row-${pdgaNumber}`);
|
||||||
const ratingCell = row.querySelector('.rating');
|
const ratingCell = row.querySelector('.cell-rating');
|
||||||
const ratingValue = ratingCell.querySelector('.rating-value');
|
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;
|
||||||
|
|
||||||
@@ -244,7 +256,7 @@ async function refreshRoundHistory(pdgaNumber) {
|
|||||||
const fullMessage = errorDetails ? errorMessage + '\n\n' + errorDetails : errorMessage;
|
const fullMessage = errorDetails ? errorMessage + '\n\n' + errorDetails : errorMessage;
|
||||||
alert(fullMessage);
|
alert(fullMessage);
|
||||||
} finally {
|
} finally {
|
||||||
icon.classList.remove('spinning');
|
if (icon) icon.classList.remove('spinning');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +319,8 @@ function closeDebugModal(event) {
|
|||||||
document.getElementById('debug-modal').style.display = 'none';
|
document.getElementById('debug-modal').style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchAndAddPlayer() {
|
async function searchAndAddPlayer(event) {
|
||||||
|
if (event) event.preventDefault();
|
||||||
const input = document.getElementById('pdga-number-input');
|
const input = document.getElementById('pdga-number-input');
|
||||||
const pdgaNumber = input.value.trim();
|
const pdgaNumber = input.value.trim();
|
||||||
|
|
||||||
|
|||||||
+40
-28
@@ -1,21 +1,27 @@
|
|||||||
<% var body = `
|
<% var body = `
|
||||||
<!-- Add Player Section -->
|
<!-- Add Player Card -->
|
||||||
<div class="card-section">
|
<form class="add-bar" onsubmit="searchAndAddPlayer(event)">
|
||||||
<h3>Add Yourself to Tracked Players</h3>
|
<div class="add-bar-label">
|
||||||
<div class="card-section-form">
|
<span class="add-bar-kicker">Track a player</span>
|
||||||
<input
|
<span class="add-bar-hint">Add a PDGA number to start following their rating.</span>
|
||||||
type="number"
|
</div>
|
||||||
id="pdga-number-input"
|
<div class="add-bar-controls">
|
||||||
class="input"
|
<div class="input-wrap">
|
||||||
placeholder="Enter your PDGA number"
|
<span class="input-prefix">#</span>
|
||||||
min="1"
|
<input
|
||||||
style="width: 240px;"
|
type="text"
|
||||||
/>
|
id="pdga-number-input"
|
||||||
<button class="btn btn-add" onclick="searchAndAddPlayer()">
|
inputmode="numeric"
|
||||||
<i class="fas fa-user-plus"></i> Add Player
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
|
|
||||||
<!-- KPI Summary Tiles -->
|
<!-- KPI Summary Tiles -->
|
||||||
<section class="kpi-strip" aria-label="Tracker overview">
|
<section class="kpi-strip" aria-label="Tracker overview">
|
||||||
@@ -53,19 +59,25 @@
|
|||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div style="display: flex; justify-content: flex-end; gap: 12px; margin-bottom: 16px;">
|
<!-- Players Table Card -->
|
||||||
<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>
|
<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>
|
||||||
<div class="table-toolbar">
|
|
||||||
<button id="trendchart-toggle" class="pill-toggle" type="button" aria-pressed="false">
|
<!-- Footnote -->
|
||||||
<svg class="pill-icon" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
<p class="footnote">Unofficial PDGA rating tracker. Ratings scraped from pdga.com on each refresh.</p>
|
||||||
<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>
|
|
||||||
`; %>
|
`; %>
|
||||||
|
|
||||||
<% var modals = `
|
<% var modals = `
|
||||||
@@ -100,4 +112,4 @@
|
|||||||
initScript: 'setupTooltipsAfterSwap();',
|
initScript: 'setupTooltipsAfterSwap();',
|
||||||
body: body,
|
body: body,
|
||||||
modals: modals
|
modals: modals
|
||||||
}) %>
|
}) %>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
extraClass {string} — optional additional CSS class (e.g. 'delta-predicted-pill')
|
extraClass {string} — optional additional CSS class (e.g. 'delta-predicted-pill')
|
||||||
*/%>
|
*/%>
|
||||||
<% if (typeof value !== 'undefined' && value != null) {
|
<% if (typeof value !== 'undefined' && value != null) {
|
||||||
const _cls = value > 0 ? 'up' : value < 0 ? 'down' : 'flat';
|
const _cls = value > 0 ? 'up' : value < 0 ? 'down' : 'flat';
|
||||||
const _text = value > 0 ? '+' + value : value.toString();
|
const _glyph = value > 0 ? '▲' : value < 0 ? '▼' : '–';
|
||||||
const _xtra = (typeof extraClass !== 'undefined' && extraClass) ? ' ' + extraClass : '';
|
const _num = value > 0 ? '+' + value : value.toString();
|
||||||
%><span class="delta-pill <%= _cls %><%= _xtra %>"><%= _text %></span><% } %>
|
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="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.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<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>
|
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||||
<link rel="stylesheet" href="/css/shared.css">
|
<link rel="stylesheet" href="/css/shared.css">
|
||||||
<% if (typeof cssFiles !== 'undefined') { %>
|
<% if (typeof cssFiles !== 'undefined') { %>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ function renderSparkline(values) {
|
|||||||
var last = pts[pts.length - 1];
|
var last = pts[pts.length - 1];
|
||||||
var areaPath = linePath + ' L ' + last.x + ' ' + h + ' L 0 ' + h + ' Z';
|
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="' + 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"/>' +
|
'<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)"/>' +
|
'<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) { %>
|
<% 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 { %>
|
<% } else { %>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="mobile-hide">Rank</th>
|
<th class="col-rank">#</th>
|
||||||
<th>Player Name</th>
|
<th class="col-player">Player</th>
|
||||||
<th class="mobile-hide">PDGA #</th>
|
<th class="col-rating">Rating<span class="th-hint">+ Δ since last update</span></th>
|
||||||
<th>Rating</th>
|
<th class="col-predicted">Predicted<span class="th-hint">+ gap from today</span></th>
|
||||||
<th class="mobile-hide">Predicted<span class="th-hint">next official update</span></th>
|
<th class="col-actions"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -46,41 +46,49 @@ function renderSparkline(values) {
|
|||||||
const sparklineSvg = renderSparkline(player.monthlyHistory || []);
|
const sparklineSvg = renderSparkline(player.monthlyHistory || []);
|
||||||
%>
|
%>
|
||||||
<tr id="row-<%= player.pdgaNumber %>" class="expandable-row" tabindex="0" onclick="togglePlayerHistory(<%= player.pdgaNumber %>)">
|
<tr id="row-<%= player.pdgaNumber %>" class="expandable-row" tabindex="0" onclick="togglePlayerHistory(<%= player.pdgaNumber %>)">
|
||||||
<td class="mobile-hide"><%= index + 1 %></td>
|
<td class="col-rank">
|
||||||
<td class="player-name">
|
<span class="rank-num mono"><%= index + 1 %></span>
|
||||||
<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>
|
</td>
|
||||||
<td class="pdga-number mobile-hide">#<%= player.pdgaNumber %></td>
|
<td class="col-player">
|
||||||
<td class="rating">
|
<div class="cell-name">
|
||||||
<div class="refresh-section">
|
<span class="player-name"><a href="https://www.pdga.com/player/<%= player.pdgaNumber %>" target="_blank" onclick="event.stopPropagation()"><%= player.name %></a></span>
|
||||||
<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>
|
<span class="pdga-num mono">#<%= player.pdgaNumber %></span>
|
||||||
<i class="fas fa-sync-alt refresh-icon" onclick="refreshPlayer(<%= player.pdgaNumber %>)" title="Refresh player data"></i>
|
|
||||||
</div>
|
</div>
|
||||||
<%- include('delta-pill', { value: player.ratingChange }) %>
|
</td>
|
||||||
<% if (sparklineSvg) { %>
|
<td class="col-rating cell-rating">
|
||||||
<span class="sparkline"><%- sparklineSvg %></span>
|
<% 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>
|
<div class="std-dev-tooltip" id="tooltip-rating-<%= player.pdgaNumber %>"></div>
|
||||||
</td>
|
</td>
|
||||||
<td class="predicted-rating mobile-hide" id="predicted-<%= player.pdgaNumber %>">
|
<td class="col-predicted" id="predicted-<%= player.pdgaNumber %>">
|
||||||
<div class="refresh-section">
|
<% if (player.predictedRating) { %>
|
||||||
<span class="predicted-value" data-stddev="<%= player.stdDev || '' %>" data-pdga="<%= player.pdgaNumber %>" style="cursor: help;"><%= player.predictedRating || 'N/A' %></span>
|
<div class="pred-stack">
|
||||||
<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>
|
<span class="predicted-value pred-num mono" data-stddev="<%= player.stdDev || '' %>" data-pdga="<%= player.pdgaNumber %>"><%= player.predictedRating %></span>
|
||||||
<i class="fas fa-sync-alt refresh-icon" onclick="refreshRoundHistory(<%= player.pdgaNumber %>)" title="Refresh prediction data"></i>
|
<%- include('delta-pill', { value: player.deltaPredicted, extraClass: 'delta-predicted-pill' }) %>
|
||||||
</div>
|
</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>
|
<div class="std-dev-tooltip" id="tooltip-stddev-<%= player.pdgaNumber %>"></div>
|
||||||
</td>
|
</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>
|
||||||
<tr id="history-<%= player.pdgaNumber %>" class="expanded-content">
|
<tr id="history-<%= player.pdgaNumber %>" class="expanded-content">
|
||||||
<td colspan="5" class="expanded-cell">
|
<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 id="history-content-<%= player.pdgaNumber %>">
|
||||||
<div class="loading-chart">Click to load rating history...</div>
|
<div class="loading-chart">Click to load rating history...</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user