Партии: «выбывшие» игроки (красный крестик) = последнее место
Выбор места при завершении партии получает опцию «✕ выбыл»; выбывшему авто-проставляется общее последнее место (ничья последнего ранга), плюс флаг eliminated для отрисовки красным крестиком. В результатах и списке партий выбывшие показываются крестиком и идут в конец. Подсчёт очков не меняется (используются обычные числовые места). Бэкенд: модель MatchParticipant.eliminated + миграция 0009 (идемпотентная); схемы (place стал необязательным, +eliminated); match_service (хелпер _resolve_finish_places, finish/update); проброс eliminated в роутерах и stats. Фронт: выбор места с крестиком в MatchDetailPage и AdminMatchEdit, отрисовка в результатах и MatchList, стиль .place-badge.eliminated; регенерация schema.d.ts. Тесты: завершение с выбывшим (последнее место, засчитан), все-выбывшие → 422. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ interface Row {
|
||||
nickname: string;
|
||||
faction_id: number;
|
||||
place: number;
|
||||
eliminated: boolean;
|
||||
was_random: boolean;
|
||||
comment: string;
|
||||
}
|
||||
@@ -46,6 +47,7 @@ export function AdminMatchEdit({ matchId, onClose }: { matchId: number; onClose:
|
||||
nickname: p.nickname,
|
||||
faction_id: p.faction_id,
|
||||
place: p.place ?? 1,
|
||||
eliminated: p.eliminated,
|
||||
was_random: p.was_random,
|
||||
comment: p.comment ?? "",
|
||||
})),
|
||||
@@ -71,7 +73,8 @@ export function AdminMatchEdit({ matchId, onClose }: { matchId: number; onClose:
|
||||
participants: rows.map((r) => ({
|
||||
user_id: r.user_id,
|
||||
faction_id: r.faction_id,
|
||||
place: r.place,
|
||||
place: r.eliminated ? null : r.place,
|
||||
eliminated: r.eliminated,
|
||||
was_random: r.was_random,
|
||||
comment: r.comment.trim() || null,
|
||||
})),
|
||||
@@ -133,9 +136,19 @@ export function AdminMatchEdit({ matchId, onClose }: { matchId: number; onClose:
|
||||
type="number"
|
||||
min={1}
|
||||
value={r.place}
|
||||
disabled={r.eliminated}
|
||||
onChange={(e) => upd(i, { place: Math.max(1, Number(e.target.value) || 1) })}
|
||||
/>
|
||||
</div>
|
||||
<label className="row small" style={{ flex: "0 0 auto", gap: 4 }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
style={{ width: "auto" }}
|
||||
checked={r.eliminated}
|
||||
onChange={(e) => upd(i, { eliminated: e.target.checked })}
|
||||
/>
|
||||
выбыл
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
placeholder="Комментарий об игроке"
|
||||
|
||||
Reference in New Issue
Block a user