Рейтинг: ввод деталей партии и предупреждения в формах
Форма завершения и правка результатов (MatchDetailPage): у каждого игрока
в PlaceEditor поля «цели» и «миры» на конец партии (у выбывшего миры
заблокированы нулём), общий блок «Итог партии» — причина победы и раунд
окончания из max_rounds партии. Всё новое уходит в общий черновик.
Причина «последний выживший» ставится сама при одном невыбывшем, выбор
заблокирован; вернули второго — причина сбрасывается и её нужно выбрать.
То же в админской правке партии.
Предупреждения (domain/finishWarnings.ts, отправку не блокируют): тип победы
и цели/миры лидеров не согласуются, досрочный конец без N целей у победителя,
у соперника целей больше, чем у победителя. Настройки группы: переключатель
«9 раундов при 5–6 игроках» через PATCH /groups/{id}. schema.d.ts
перегенерирован. #23
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
This commit is contained in:
@@ -10,6 +10,10 @@ export interface PlacePlayer {
|
||||
avatar_url?: string | null;
|
||||
}
|
||||
|
||||
export type CountField = "objectives" | "worlds";
|
||||
/** Цели и миры на конец партии по user_id; отсутствие ключа или null — не указано. */
|
||||
export type Counts = Record<number, { objectives: number | null; worlds: number | null }>;
|
||||
|
||||
type Target =
|
||||
| { type: "merge"; idx: number }
|
||||
| { type: "insert"; idx: number }
|
||||
@@ -28,7 +32,8 @@ interface DragState {
|
||||
* Редактор мест перетаскиванием (макет A2): вертикальный список блоков игроков,
|
||||
* верхний — 1-е место. Бросок между блоками — порядок, на середину чужого блока —
|
||||
* слияние в ничью (общая рамка и место, ✂ выносит обратно), в пунктирную зону —
|
||||
* выбывший. В каждом блоке — строка комментария об игроке.
|
||||
* выбывший. В каждом блоке — строка комментария об игроке и (если переданы counts)
|
||||
* цели и миры на конец партии; у выбывшего миров нет — поле заблокировано нулём.
|
||||
*
|
||||
* Контролируемый: blocks (упорядоченные группы user_id, длина >1 = ничья) и
|
||||
* eliminated живут у родителя. Во время drag DOM двигается напрямую (transform,
|
||||
@@ -41,6 +46,8 @@ export function PlaceEditor({
|
||||
comments,
|
||||
onChange,
|
||||
onComment,
|
||||
counts,
|
||||
onCount,
|
||||
}: {
|
||||
players: PlacePlayer[];
|
||||
blocks: number[][];
|
||||
@@ -48,6 +55,8 @@ export function PlaceEditor({
|
||||
comments: Record<number, string>;
|
||||
onChange: (blocks: number[][], eliminated: number[]) => void;
|
||||
onComment: (userId: number, text: string) => void;
|
||||
counts?: Counts;
|
||||
onCount?: (userId: number, field: CountField, value: number | null) => void;
|
||||
}) {
|
||||
const listRef = useRef<HTMLDivElement>(null);
|
||||
const elimRef = useRef<HTMLDivElement>(null);
|
||||
@@ -225,15 +234,49 @@ export function PlaceEditor({
|
||||
);
|
||||
};
|
||||
|
||||
const commentInput = (userId: number) => (
|
||||
<input
|
||||
className="rank-comment"
|
||||
placeholder="Комментарий об игроке"
|
||||
value={comments[userId] ?? ""}
|
||||
onChange={(e) => onComment(userId, e.target.value)}
|
||||
/>
|
||||
const countInput = (userId: number, field: CountField, label: string, locked = false) => (
|
||||
<label className="rank-count">
|
||||
<span>{label}</span>
|
||||
<input
|
||||
type="number"
|
||||
inputMode="numeric"
|
||||
min={0}
|
||||
max={99}
|
||||
placeholder="—"
|
||||
disabled={locked}
|
||||
value={locked ? 0 : (counts?.[userId]?.[field] ?? "")}
|
||||
onChange={(e) => {
|
||||
const raw = e.target.value.trim();
|
||||
const num = Number(raw);
|
||||
onCount?.(
|
||||
userId,
|
||||
field,
|
||||
raw === "" || !Number.isFinite(num) ? null : Math.min(99, Math.max(0, Math.trunc(num))),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
|
||||
const commentInput = (userId: number) => {
|
||||
const comment = (
|
||||
<input
|
||||
className="rank-comment"
|
||||
placeholder="Комментарий об игроке"
|
||||
value={comments[userId] ?? ""}
|
||||
onChange={(e) => onComment(userId, e.target.value)}
|
||||
/>
|
||||
);
|
||||
if (!counts) return comment;
|
||||
return (
|
||||
<div className="rank-details">
|
||||
{comment}
|
||||
{countInput(userId, "objectives", "цели")}
|
||||
{countInput(userId, "worlds", "миры", eliminated.includes(userId))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rank-list" ref={listRef}>
|
||||
|
||||
Reference in New Issue
Block a user