UI: согласованные тексты и терминология
- «Winrate» -> «Винрейт» в карточке статистики; в справке добавлена расшифровка «Винрейт (WR)» — WR остаётся в компактных таблицах; - кнопки сохранения: «ОК» -> «Сохранить» (аккаунт, фракции, аккаунты в админке); отмена в модалках/формах — везде «Отмена», не «Назад»; - пустые состояния приведены к шаблону «X пока нет.»; - роли участников группы переведены: owner/member -> владелец/участник, бейдж admin -> «админ»; «Имя обновлено» -> «Ник обновлён»; - «N игр.» в значении числа игроков -> «N игроков» со склонением (новый хелпер plural в domain/format.ts); - «минимум 2 участника» -> «минимум 2 игрока» (в партии — игроки, в группе — участники). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,7 @@ export function AdminAccountsPage() {
|
||||
if (editId == null || !name) return;
|
||||
try {
|
||||
await update.mutateAsync({ userId: editId, nickname: name });
|
||||
toast.show("Имя обновлено");
|
||||
toast.show("Ник обновлён");
|
||||
setEditId(null);
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Не удалось переименовать");
|
||||
@@ -63,7 +63,7 @@ export function AdminAccountsPage() {
|
||||
disabled={update.isPending || !editName.trim() || editName.trim() === u.nickname}
|
||||
onClick={saveName}
|
||||
>
|
||||
ОК
|
||||
Сохранить
|
||||
</button>
|
||||
<button className="btn btn-ghost small" onClick={() => setEditId(null)}>
|
||||
Отмена
|
||||
@@ -72,7 +72,7 @@ export function AdminAccountsPage() {
|
||||
) : (
|
||||
<div className="row-between">
|
||||
<div>
|
||||
<b>{u.nickname}</b> {u.role === "admin" && <span className="badge">admin</span>}
|
||||
<b>{u.nickname}</b> {u.role === "admin" && <span className="badge">админ</span>}
|
||||
<div className="muted small">
|
||||
#{u.id} · {u.auth_provider} · {u.is_active ? "активен" : "отключён"}
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,7 @@ export function AdminFactionsPage() {
|
||||
disabled={!names[f.id] || names[f.id] === f.name_ru}
|
||||
onClick={() => save(f.id)}
|
||||
>
|
||||
ОК
|
||||
Сохранить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,7 +59,7 @@ export function AdminLogsPage() {
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{(data?.items ?? []).length === 0 && <div className="muted small">Событий нет.</div>}
|
||||
{(data?.items ?? []).length === 0 && <div className="muted small">Событий пока нет.</div>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useMemo, useState } from "react";
|
||||
|
||||
import { ApiError } from "../../api/client";
|
||||
import { ConfirmDialog } from "../../components/ConfirmDialog";
|
||||
import { formatDate } from "../../domain/format";
|
||||
import { formatDate, plural } from "../../domain/format";
|
||||
import { Spinner } from "../../components/Spinner";
|
||||
import { Switch } from "../../components/Switch";
|
||||
import { useToast } from "../../context/ToastContext";
|
||||
@@ -58,7 +58,8 @@ export function AdminRecordsPage() {
|
||||
<b>#{m.id}</b> · {formatDate(m.played_at)}
|
||||
{!grouped && <span className="muted small"> · {m.group_name}</span>}
|
||||
<div className="muted small">
|
||||
{m.player_count} игр. · автор {m.created_by}
|
||||
{m.player_count} {plural(m.player_count, "игрок", "игрока", "игроков")} · автор{" "}
|
||||
{m.created_by}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row" style={{ gap: 4 }}>
|
||||
@@ -120,7 +121,7 @@ export function AdminRecordsPage() {
|
||||
{isLoading ? (
|
||||
<Spinner />
|
||||
) : (matches ?? []).length === 0 ? (
|
||||
<div className="muted small">Записей нет.</div>
|
||||
<div className="muted small">Записей пока нет.</div>
|
||||
) : grouped ? (
|
||||
byGroup.map(([gid, { name, items }]) => (
|
||||
<div key={gid} style={{ marginBottom: 16 }}>
|
||||
|
||||
Reference in New Issue
Block a user