- .btn-block в global.css заменяет 9 повторов style={{width:100%}};
- .badge-live — единый бейдж «идёт» (3 копии inline-стиля);
- незавершённые партии на главной используют стандартный .card,
как список партий (была единственная карточка на самодельных стилях);
- в списке партий добавлен бейдж 🎲 случайной фракции — как на странице
партии (раньше в списке он терялся);
- переключатель групп: кнопка «+» подписана «+ Создать группу»
(по образцу «+ Добавить игрока»), убран пустой span.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
import { useState } from "react";
|
|
|
|
import { ApiError } from "../api/client";
|
|
import { useToast } from "../context/ToastContext";
|
|
import { useCreateGroup } from "../hooks/groups";
|
|
import { useExpansions } from "../hooks/reference";
|
|
import { Switch } from "./Switch";
|
|
|
|
export function CreateGroupForm({ onCreated }: { onCreated?: () => void }) {
|
|
const { data: expansions } = useExpansions();
|
|
const createGroup = useCreateGroup();
|
|
const toast = useToast();
|
|
const [name, setName] = useState("");
|
|
const [selected, setSelected] = useState<Set<number>>(new Set());
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const toggle = (id: number) =>
|
|
setSelected((s) => {
|
|
const next = new Set(s);
|
|
next.has(id) ? next.delete(id) : next.add(id);
|
|
return next;
|
|
});
|
|
|
|
const submit = async () => {
|
|
setError(null);
|
|
try {
|
|
await createGroup.mutateAsync({ name: name.trim(), expansion_ids: [...selected] });
|
|
toast.show("Группа создана");
|
|
onCreated?.();
|
|
} catch (e) {
|
|
setError(e instanceof ApiError ? e.message : "Не удалось создать группу");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="card">
|
|
<h3>Новая группа</h3>
|
|
<div className="field">
|
|
<label className="label">Название</label>
|
|
<input value={name} onChange={(e) => setName(e.target.value)} placeholder="Например: Вечер 40k" />
|
|
</div>
|
|
<div className="field">
|
|
<label className="label">Дополнения группы</label>
|
|
<div className="stack">
|
|
{(expansions ?? []).map((e) => (
|
|
<label key={e.id} className="row" style={{ justifyContent: "space-between" }}>
|
|
<span>
|
|
{e.name_ru}
|
|
{e.is_base && <span className="muted small"> (всегда включено)</span>}
|
|
</span>
|
|
<Switch
|
|
disabled={e.is_base}
|
|
checked={e.is_base || selected.has(e.id)}
|
|
onChange={() => toggle(e.id)}
|
|
/>
|
|
</label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
{error && <p className="error-text">{error}</p>}
|
|
<button className="btn btn-primary btn-block" onClick={submit} disabled={createGroup.isPending}>
|
|
Создать
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|