Новички: серебряный рейтинг и порог 5 игр вместо 3

- рейтинг в блоках «Новички» (топ игроков и общая статистика) выводится
  серебряным (--silver, класс .lb-score.provisional), а не золотым;
- MIN_GAMES 3 -> 5 (scoring.py) — в ранжированный топ попадают с 5 игр;
  подпись «Новички (меньше N игр)» берёт порог из API, справка обновлена;
- pytest: 57 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 17:21:20 +03:00
co-authored by Claude Fable 5
parent c4f75d5de0
commit ac296a8f12
6 changed files with 14 additions and 9 deletions
+1 -1
View File
@@ -9,7 +9,7 @@
from __future__ import annotations
# Порог числа игр для попадания в ранжированный топ (ниже — «Новички»/provisional).
MIN_GAMES = 3
MIN_GAMES = 5
# Порог числа игр на фракцию для расчёта лучшей/худшей фракции.
FACTION_MIN_GAMES = 2
+3 -3
View File
@@ -6,7 +6,7 @@ function pct(v: number | null | undefined): string {
return v == null ? "—" : `${Math.round(v * 100)}%`;
}
function Row({ entry }: { entry: LeaderboardEntry }) {
function Row({ entry, provisional }: { entry: LeaderboardEntry; provisional?: boolean }) {
return (
// Вся строка — ссылка на профиль (на главной карточка-обёртка ведёт в общую
// статистику, а клик по строке игрока — в его профиль).
@@ -23,7 +23,7 @@ function Row({ entry }: { entry: LeaderboardEntry }) {
<span>WR: {pct(entry.win_rate)}</span>
</div>
</div>
<div className="lb-score">{entry.score ?? "—"}</div>
<div className={"lb-score" + (provisional ? " provisional" : "")}>{entry.score ?? "—"}</div>
</PlayerLink>
);
}
@@ -53,7 +53,7 @@ export function Leaderboard({
Новички (мало игр)
</div>
{provisional.map((e) => (
<Row key={e.user_id} entry={e} />
<Row key={e.user_id} entry={e} provisional />
))}
</>
)}
+1 -1
View File
@@ -55,7 +55,7 @@ export function HelpPage() {
<div className="card">
<h3>Попадание в топ</h3>
<p className="small">
Чтобы попасть в ранжированный топ, нужно сыграть минимум <b>3 партии</b>. До этого
Чтобы попасть в ранжированный топ, нужно сыграть минимум <b>5 партий</b>. До этого
игрок показывается в блоке «Новички» — предварительно, без места в рейтинге.
</p>
</div>
+6 -4
View File
@@ -42,7 +42,7 @@ export function OverallStatsPage() {
const provisional = filterSort(data.provisional);
const arrow = (k: SortKey) => (k === sortKey ? (dir === 1 ? " ↑" : " ↓") : "");
const row = (e: LeaderboardEntry) => (
const row = (e: LeaderboardEntry, isProvisional = false) => (
<PlayerLink key={e.user_id} userId={e.user_id} className="st-row">
<span className={"st-num" + (e.rank && e.rank <= 3 ? " gold" : "")}>{e.rank ?? "—"}</span>
<span className="st-player">
@@ -52,7 +52,9 @@ export function OverallStatsPage() {
<span className="st-num">{e.games}</span>
<span className="st-num">{e.wins}</span>
<span className="st-num">{pct(e.win_rate)}</span>
<span className="st-num lb-score">{e.score ?? "—"}</span>
<span className={"st-num lb-score" + (isProvisional ? " provisional" : "")}>
{e.score ?? "—"}
</span>
</PlayerLink>
);
@@ -77,14 +79,14 @@ export function OverallStatsPage() {
Никого не найдено.
</div>
) : (
entries.map(row)
entries.map((e) => row(e))
)}
</div>
{provisional.length > 0 && (
<div className="card">
<div className="label">Новички (меньше {data.min_games} игр)</div>
{provisional.map(row)}
{provisional.map((e) => row(e, true))}
</div>
)}
</div>
+2
View File
@@ -106,6 +106,8 @@
.lb-rank.top { color: var(--gold); }
.lb-metrics { display: flex; gap: 12px; font-size: 13px; color: var(--text-muted); }
.lb-score { font-weight: 700; color: var(--accent-2); }
/* Рейтинг новичков (мало игр) — серебряный, не золотой. */
.lb-score.provisional { color: var(--silver); }
/* Таблица общей статистики (сортируемая, кликабельные строки) */
.st-row, .st-head {
+1
View File
@@ -11,6 +11,7 @@
--success: #4caf7d;
--danger: #e0533d;
--gold: #f0c14b;
--silver: #c0c6cf;
/* Размеры */
--app-max-w: 480px;