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
+77
View File
@@ -0,0 +1,77 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { ApiError } from "../api/client";
import { GroupSwitcher } from "../components/GroupSwitcher";
import { ProfileStatsCard } from "../components/ProfileStatsCard";
import { Spinner } from "../components/Spinner";
import { useToast } from "../context/ToastContext";
import { useLogout, useMe, useUpdateNickname } from "../hooks/auth";
import { useMyStats } from "../hooks/stats";
export function AccountPage() {
const { data: me, isLoading } = useMe();
const { data: stats } = useMyStats();
const updateNick = useUpdateNickname();
const logout = useLogout();
const toast = useToast();
const navigate = useNavigate();
const [nickname, setNickname] = useState("");
if (isLoading || !me) return <Spinner />;
const save = async () => {
const nick = nickname.trim();
if (!nick) return;
try {
await updateNick.mutateAsync(nick);
setNickname("");
toast.show("Никнейм обновлён");
} catch (e) {
toast.show(e instanceof ApiError ? e.message : "Ошибка");
}
};
const doLogout = async () => {
await logout.mutateAsync().catch(() => {});
navigate("/login", { replace: true });
};
return (
<div>
<div className="card">
<h3>Профиль</h3>
<div className="row-between">
<span className="muted">Никнейм</span>
<b>{me.nickname}</b>
</div>
<div className="field" style={{ marginTop: 10 }}>
<label className="label">Сменить никнейм</label>
<div className="row">
<input value={nickname} onChange={(e) => setNickname(e.target.value)} placeholder={me.nickname} />
<button className="btn btn-primary" onClick={save} disabled={updateNick.isPending}>
ОК
</button>
</div>
</div>
</div>
{stats && <ProfileStatsCard stats={stats} />}
<GroupSwitcher />
<button className="btn btn-danger" style={{ width: "100%" }} onClick={doLogout}>
Выйти
</button>
<p className="center" style={{ marginTop: 24 }}>
<button
className="btn btn-ghost small muted"
onClick={() => navigate("/admin/login")}
>
Вход для администратора
</button>
</p>
</div>
);
}