UI: единый PlayerLink — ники игроков кликабельны во всех местах просмотра

Новый компонент PlayerLink (ссылка на /u/:id, кликабельно всё поле строки,
stopPropagation для вложенности в кликабельные карточки). Применён:
- Leaderboard и общая статистика (рефакторинг ручных Link);
- страница партии: результаты и состав;
- настройки группы: список участников;
- список партий и незавершённые на главной (карточки button -> div,
  т.к. ссылки внутри button — невалидная разметка).
«пригласил: X» в приглашениях остаётся текстом — в API нет id пригласившего.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 17:37:19 +03:00
co-authored by Claude Fable 5
parent d81f5d8964
commit cbf3cfc6e6
7 changed files with 68 additions and 28 deletions
+4 -2
View File
@@ -2,6 +2,7 @@ import { Trash2, UserPlus } from "lucide-react";
import { useEffect, useState } from "react";
import { ApiError } from "../api/client";
import { PlayerLink } from "../components/PlayerLink";
import { Spinner } from "../components/Spinner";
import { MAX_GROUP_SIZE } from "../domain/constants";
import { useToast } from "../context/ToastContext";
@@ -118,9 +119,10 @@ export function GroupSettingsPage() {
<div className="stack">
{(members ?? []).map((m) => (
<div key={m.user_id} className="row-between">
<span>
{/* Кликабельна вся левая часть строки, не только текст ника. */}
<PlayerLink userId={m.user_id} style={{ flex: 1 }}>
{m.nickname} <span className="muted small">· {m.role}</span>
</span>
</PlayerLink>
{isOwner && m.role !== "owner" && (
<button className="btn btn-ghost btn-danger small" onClick={() => remove(m.user_id)}>
<Trash2 size={16} />