Партия: совместное заполнение формы завершения на клиенте

Раскладка, комментарии, причина победы и комментарий о партии уходят в общий
черновик через ~0.6с после последнего действия, а чужие правки подтягиваются по
SSE. Чужой черновик применяется, только если с момента моего последнего действия
прошло больше 1.5с — иначе правка соседа перетирала бы тайл прямо под рукой.

Перед «Завершить» отложенная запись дожимается: иначе последняя правка попала бы
в результаты, но не в черновик, и второй участник увидел бы не то, что записалось.
Само завершение по-прежнему шлёт тело запроса, так что работает и без черновика.

Под подсказкой о перетаскивании появилась строка «Результаты заполняет также
<Ник>», а если партию завершил кто-то другой — тост вместо молча исчезающей формы.

В админке у идущей партии вместо формы правки — пояснение и переход на страницу
партии: сервер её результаты всё равно не примет.

#28

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BoiJK9ux8peeyjLb8TYjFf
This commit is contained in:
2026-09-09 19:39:42 +03:00
co-authored by Claude Opus 5
parent 8be9a23c0c
commit 32d64c94af
6 changed files with 254 additions and 8 deletions
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { ApiError } from "../../api/client";
import { MatchMedia } from "../../components/MatchMedia";
@@ -33,6 +34,7 @@ export function AdminMatchEdit({ matchId, onClose }: { matchId: number; onClose:
const uploadAtt = useAdminUploadAttachment(matchId);
const deleteAtt = useAdminDeleteAttachment(matchId);
const toast = useToast();
const navigate = useNavigate();
const [rows, setRows] = useState<Row[]>([]);
const [playedAt, setPlayedAt] = useState("");
@@ -110,6 +112,20 @@ export function AdminMatchEdit({ matchId, onClose }: { matchId: number; onClose:
{isLoading || !match ? (
<Spinner />
) : match.status === "in_progress" ? (
// Результаты идущей партии сервер не примет (409): места без завершения
// оставляли бы партию вне статистики. Объясняем это здесь, а не ошибкой.
<div className="card">
<h3>Партия ещё идёт</h3>
<p className="muted small">
Результаты незавершённой партии правкой не вносятся — их проставляют
при завершении, на странице самой партии. Там же участники заполняют
места совместно.
</p>
<button className="btn btn-block" onClick={() => navigate(`/match/${matchId}`)}>
Открыть страницу партии
</button>
</div>
) : (
<>
<div className="field">