Карточка топа целиком ведёт в общую статистику — вложенные ссылки на профили конфликтовали с этим переходом. У Leaderboard появился флаг profileLinks: на главной выключен, в «Списке игроков» группы (карточка никуда не ведёт) строки-ссылки остаются, как и в общей статистике. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
89 lines
2.6 KiB
TypeScript
89 lines
2.6 KiB
TypeScript
import type { LeaderboardEntry } from "../domain/types";
|
|
import { Avatar } from "./Avatar";
|
|
import { PlayerLink } from "./PlayerLink";
|
|
|
|
function pct(v: number | null | undefined): string {
|
|
return v == null ? "—" : `${Math.round(v * 100)}%`;
|
|
}
|
|
|
|
function Row({
|
|
entry,
|
|
provisional,
|
|
profileLinks,
|
|
}: {
|
|
entry: LeaderboardEntry;
|
|
provisional?: boolean;
|
|
profileLinks: boolean;
|
|
}) {
|
|
const content = (
|
|
<>
|
|
<div className={"lb-rank" + (entry.rank && entry.rank <= 3 ? " top" : "")}>
|
|
{entry.rank ?? "—"}
|
|
</div>
|
|
<Avatar url={entry.avatar_url} nickname={entry.nickname} size={32} />
|
|
<div>
|
|
<div style={{ fontWeight: 600 }}>{entry.nickname}</div>
|
|
<div className="lb-metrics">
|
|
<span>Игр: {entry.games}</span>
|
|
<span>Побед: {entry.wins}</span>
|
|
<span>WR: {pct(entry.win_rate)}</span>
|
|
</div>
|
|
</div>
|
|
<div className={"lb-score" + (provisional ? " provisional" : "")}>{entry.score ?? "—"}</div>
|
|
</>
|
|
);
|
|
// Строка-ссылка на профиль — только там, где сама карточка никуда не ведёт
|
|
// (на главной карточка топа целиком открывает общую статистику).
|
|
if (!profileLinks) return <div className="lb-row">{content}</div>;
|
|
return (
|
|
<PlayerLink userId={entry.user_id} className="lb-row">
|
|
{content}
|
|
</PlayerLink>
|
|
);
|
|
}
|
|
|
|
export function Leaderboard({
|
|
entries,
|
|
provisional,
|
|
inactive,
|
|
profileLinks = true,
|
|
}: {
|
|
entries: LeaderboardEntry[];
|
|
provisional?: LeaderboardEntry[];
|
|
inactive?: LeaderboardEntry[];
|
|
profileLinks?: boolean;
|
|
}) {
|
|
const empty =
|
|
entries.length === 0 && !provisional?.length && !inactive?.length;
|
|
if (empty) {
|
|
return <div className="muted small">Сыгранных партий пока нет.</div>;
|
|
}
|
|
return (
|
|
<div>
|
|
{entries.map((e) => (
|
|
<Row key={e.user_id} entry={e} profileLinks={profileLinks} />
|
|
))}
|
|
{provisional && provisional.length > 0 && (
|
|
<>
|
|
<div className="label" style={{ marginTop: 10 }}>
|
|
Новички (мало игр)
|
|
</div>
|
|
{provisional.map((e) => (
|
|
<Row key={e.user_id} entry={e} provisional profileLinks={profileLinks} />
|
|
))}
|
|
</>
|
|
)}
|
|
{inactive && inactive.length > 0 && (
|
|
<>
|
|
<div className="label" style={{ marginTop: 10 }}>
|
|
Ещё не играли
|
|
</div>
|
|
{inactive.map((e) => (
|
|
<Row key={e.user_id} entry={e} profileLinks={profileLinks} />
|
|
))}
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|