Партия: правка истории и кнопка «Редактировать» у игрока

Правка завершённой партии проверяла состав теми же правилами, что и создание:
участник обязан состоять в группе сейчас, фракция — быть доступной сейчас. После
отключения дополнения партию, сыгранную на Тау, было уже не исправить, а после
удаления игрока из группы — любую партию с ним. Теперь то, что уже записано в
партии, проходит всегда, а новые игроки и фракции по-прежнему берутся только из
текущего состава: чинить историю можно, занести в неё постороннего — нет.
Создание партии не ослабло.

Править завершённую партию умел любой участник группы, но только через API —
кнопки не было, и на практике это мог сделать лишь админ через админку. Теперь у
блока «Результаты» есть «Редактировать», и правка идёт тем же перетаскиванием,
что и завершение: раскладка восстанавливается из сохранённых мест (одинаковое
место — ничья, выбывшие отдельно), рядом — фракции, причина победы и комментарии.
Сохранение шлёт версию партии, так что устаревшая правка отклоняется как раньше.

Фракции вынесены отдельным блоком, а не в PlaceEditor: он занят перетаскиванием,
и селекты внутри него — лишний риск. В списке доступных фракций к набору группы
добавляются те, что уже стоят в партии, — иначе фракцию из отключённого
дополнения нельзя было бы даже оставить как есть.

#29

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 20:16:33 +03:00
co-authored by Claude Opus 5
parent 2986a58ef1
commit 0d81901a63
4 changed files with 370 additions and 8 deletions
+23
View File
@@ -8,6 +8,7 @@ import type {
MatchFinish,
MatchFinishDraftData,
MatchRead,
MatchUpdate,
} from "../domain/types";
import { uploadFile } from "../lib/upload";
@@ -88,6 +89,28 @@ export function useFinishMatch() {
});
}
/** Правка завершённой партии участником группы (у админки свой эндпойнт). */
export function useUpdateMatch() {
const qc = useQueryClient();
return useMutation({
mutationFn: async (args: { matchId: number; body: MatchUpdate }): Promise<MatchRead> =>
unwrap(
await api.PATCH("/api/matches/{match_id}", {
params: { path: { match_id: args.matchId } },
body: args.body,
}),
),
onSuccess: (m) => {
qc.setQueryData(qk.match(m.id), m);
qc.invalidateQueries({ queryKey: qk.groupMatches(m.group_id) });
qc.invalidateQueries({ queryKey: qk.groupStats(m.group_id) });
// Места изменились — значит изменились лидерборд, история и профили.
for (const key of matchAffectedKeys) qc.invalidateQueries({ queryKey: key });
},
});
}
export function useDeleteMatch() {
const qc = useQueryClient();
return useMutation({