v0.1 - макет интерфейса, аутентификация через логин, аккаунт админа, создание партии в 2 этапа, базовые настройки профиля и группы, переключение между группами, статистика

This commit is contained in:
2026-06-16 17:41:06 +03:00
parent 6ab74f01aa
commit 56b5d09a4d
123 changed files with 13572 additions and 21 deletions
+49
View File
@@ -0,0 +1,49 @@
import { useNavigate } from "react-router-dom";
import { formatDuration } from "../domain/format";
import { winReasonLabel } from "../domain/winReasons";
import type { MatchListItem } from "../domain/types";
export function MatchListView({ items }: { items: MatchListItem[] }) {
const navigate = useNavigate();
if (items.length === 0) return <div className="muted small">Партий пока нет.</div>;
return (
<div className="stack">
{items.map((m) => {
const inProgress = m.status === "in_progress";
const sorted = [...m.participants].sort((a, b) => (a.place ?? 99) - (b.place ?? 99));
return (
<button
key={m.id}
className="card"
style={{ textAlign: "left", width: "100%", margin: 0 }}
onClick={() => navigate(`/match/${m.id}`)}
>
<div className="row-between">
<b>{m.played_at}</b>
{inProgress ? (
<span className="badge" style={{ color: "var(--accent-2)" }}>идёт</span>
) : (
<span className="muted small">
{formatDuration(m.duration_minutes)} · {winReasonLabel(m.win_reason)}
</span>
)}
</div>
<div className="stack" style={{ marginTop: 6, gap: 4 }}>
{sorted.map((p) => (
<div key={p.user_id} className="row" style={{ gap: 8 }}>
<span className={"place-badge" + (p.place === 1 ? " first" : "")}>
{p.place ?? "—"}
</span>
<span>{p.nickname}</span>
<span className="muted small">· {p.faction_name}</span>
</div>
))}
</div>
</button>
);
})}
</div>
);
}