Add stats, profiles, avatars and palaute
Step 6. The surfaces around the review loop. - Nine leaderboards, ordered and limited in SQL, each with a deterministic tie-break so a tied board doesn't reshuffle between reloads. Min 3 reviews to qualify, for reviewer boards too - Profiles show counts and history-wide averages and the member's songs, never a list of their reviews — per-song opinion stays gated - Avatars: 5MB in, 256px JPEG out, ffmpeg's re-encode being the validation. No upload still means initials, and avatars are public - Changing your own password requires the current one and drops your other sessions - Palaute: free text plus the page you were on, carried in a footer link, and the user agent from the header. Reporters see their own; the admin resolves them with a timestamp rather than a status enum - Admin gained the song list with delete, the reports page, and an open-report count on the dashboard Two theme fixes the screenshots caught: leaderboard ranks need a CSS counter because display:grid suppresses list markers, and count-based boards were printing 3.0 where they mean 3.
This commit is contained in:
@@ -551,3 +551,42 @@ code { background: var(--surface-raised); padding: 0.1rem var(--space-1);
|
||||
*, *::before, *::after { animation-duration: 1ms !important; transition-duration: 1ms !important; }
|
||||
.songcard:hover { transform: none; }
|
||||
}
|
||||
|
||||
/* --- stats --- */
|
||||
|
||||
.boards { display: grid; grid-template-columns: 1fr; gap: var(--space-5); }
|
||||
@media (min-width: 700px) { .boards { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (min-width: 1100px) { .boards { grid-template-columns: repeat(3, 1fr); } }
|
||||
|
||||
.board { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--radius);
|
||||
padding: var(--space-4); margin: 0; box-shadow: var(--shadow-card); }
|
||||
.board h2 { font-size: 1.1rem; margin-bottom: var(--space-3); }
|
||||
/* A counter rather than a list marker: `display: grid` on the <li> suppresses markers. */
|
||||
.board-list { list-style: none; counter-reset: rank; margin: 0; padding: 0; }
|
||||
.board-list li { display: grid; grid-template-columns: 1.6rem 1fr auto;
|
||||
column-gap: var(--space-2); align-items: baseline;
|
||||
padding: var(--space-2) 0; border-bottom: 1px solid var(--hairline); }
|
||||
.board-list li:last-child { border-bottom: 0; }
|
||||
.board-list li::before { counter-increment: rank; content: counter(rank) "."; color: var(--muted);
|
||||
font-family: var(--font-display); }
|
||||
.board-list li a { grid-column: 2; }
|
||||
.board-list li .value { grid-column: 3; grid-row: 1; font-family: var(--font-display);
|
||||
font-size: 1.1rem; color: var(--gold-1); }
|
||||
.board-list li .small { grid-column: 2; }
|
||||
.board-list li .small:last-child { grid-column: 3; text-align: right; }
|
||||
|
||||
/* --- profile --- */
|
||||
|
||||
.profilehead { display: flex; align-items: center; gap: var(--space-4); margin-bottom: var(--space-5); }
|
||||
.profilehead h1 { margin: 0; }
|
||||
.avatar.big { width: 4.5rem; height: 4.5rem; font-size: 1.6rem; object-fit: cover; }
|
||||
img.avatar { object-fit: cover; }
|
||||
|
||||
.statgrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4);
|
||||
margin-bottom: var(--space-6); }
|
||||
@media (min-width: 700px) { .statgrid { grid-template-columns: repeat(4, 1fr); } }
|
||||
|
||||
.statcard { background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--radius);
|
||||
padding: var(--space-5); text-align: center; display: flex; flex-direction: column;
|
||||
gap: var(--space-1); box-shadow: var(--shadow-card); }
|
||||
.statvalue { font-family: var(--font-display); font-size: 2rem; font-weight: 700; color: var(--gold-1); }
|
||||
|
||||
Reference in New Issue
Block a user