Рейтинг: ввод деталей партии и предупреждения в формах

Форма завершения и правка результатов (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:
2026-09-14 22:45:25 +03:00
co-authored by Claude Opus 5
parent 6ea4a67672
commit cc64bb4644
10 changed files with 683 additions and 130 deletions
+18 -4
View File
@@ -2,7 +2,10 @@ import type { components } from "../api/schema";
export type WinReason = components["schemas"]["MatchFinish"]["win_reason"];
// Фиксированный порядок (сверху вниз).
export const LAST_STANDING: WinReason = "last_standing";
// Причины для ручного выбора, фиксированный порядок (сверху вниз). «Последний выживший»
// сюда не входит: она ставится сама, когда невыбывший игрок остался один.
export const WIN_REASONS: { code: WinReason; label: string }[] = [
{ code: "objectives", label: "По целям" },
{ code: "worlds", label: "По мирам" },
@@ -10,10 +13,21 @@ export const WIN_REASONS: { code: WinReason; label: string }[] = [
{ code: "resources", label: "По ресурсам" },
];
const LABELS: Record<string, string> = Object.fromEntries(
WIN_REASONS.map((w) => [w.code, w.label]),
);
const LABELS: Record<string, string> = {
...Object.fromEntries(WIN_REASONS.map((w) => [w.code, w.label])),
[LAST_STANDING]: "Последний выживший",
};
export function winReasonLabel(code: string | null | undefined): string {
return code ? LABELS[code] ?? code : "—";
}
/**
* Причина победы после изменения состава выбывших. Правило сервера: невыбывший ровно
* один ⇔ «последний выживший». Поэтому при одном выжившем причина ставится сама, а когда
* выживших снова двое — сбрасывается (null): её нужно выбрать заново, а не угадывать.
*/
export function reasonForSurvivors(current: WinReason | null, survivors: number): WinReason | null {
if (survivors === 1) return LAST_STANDING;
return current === LAST_STANDING ? null : current;
}