fix: schedule status system — auto-key, config order, label lookup
- Auto-generate status key from label (admin doesn't need to set keys) - Remove visible key field from status config editor - Order statuses/levels in filters by config order (matches admin panel) - Shared findStatusConfig() for robust label lookup (by key, label, or derived key) - Custom status badges in DayCard, GroupCard, MobileSchedule - Simplified filter logic with clsStatus helper - Removed dead code: TIME_PRESETS, StatusFilter type - SelectField: blur input after selection to prevent re-open
This commit is contained in:
@@ -251,6 +251,7 @@ export function SelectField({
|
||||
onChange(opt.value);
|
||||
setOpen(false);
|
||||
setSearch("");
|
||||
inputRef.current?.blur();
|
||||
}}
|
||||
className={`w-full px-4 py-2 text-left text-sm transition-colors hover:bg-white/5 ${
|
||||
opt.value === value ? "text-gold bg-gold/5" : "text-white"
|
||||
|
||||
@@ -50,11 +50,16 @@ function buildLevelOptions(config: ScheduleConfig) {
|
||||
];
|
||||
}
|
||||
|
||||
function statusKey(s: { key: string; label: string }): string {
|
||||
// Use explicit key if set, otherwise derive from label
|
||||
return s.key || s.label.toLowerCase().replace(/[^a-zа-яё0-9]/gi, "_").replace(/_+/g, "_");
|
||||
}
|
||||
|
||||
function buildStatusOptions(config: ScheduleConfig) {
|
||||
const statuses = config.statuses ?? DEFAULT_CONFIG.statuses;
|
||||
return [
|
||||
{ value: "", label: "Без статуса" },
|
||||
...statuses.map((s) => ({ value: s.key, label: s.label })),
|
||||
...statuses.map((s) => ({ value: statusKey(s), label: s.label })),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -275,7 +280,16 @@ function ClassModal({
|
||||
statusOptions: { value: string; label: string }[];
|
||||
statusHint: string;
|
||||
}) {
|
||||
const [draft, setDraft] = useState<ScheduleClass>(cls);
|
||||
const [draft, setDraft] = useState<ScheduleClass>(() => {
|
||||
// Migrate legacy booleans to status field
|
||||
if (!cls.status && (cls.hasSlots || cls.recruiting)) {
|
||||
return {
|
||||
...cls,
|
||||
status: cls.hasSlots ? "hasSlots" : "recruiting",
|
||||
};
|
||||
}
|
||||
return cls;
|
||||
});
|
||||
const trainerOptions = trainers.map((t) => ({ value: t, label: t }));
|
||||
const typeOptions = classTypes.map((t) => ({ value: t, label: t }));
|
||||
const isNew = !onDelete;
|
||||
@@ -514,7 +528,7 @@ function ClassModal({
|
||||
/>
|
||||
<SelectField
|
||||
label="Статус"
|
||||
value={draft.status || (draft.recruiting ? "recruiting" : draft.hasSlots ? "hasSlots" : "")}
|
||||
value={draft.status || ""}
|
||||
onChange={(v) => setDraft({
|
||||
...draft,
|
||||
status: v || undefined,
|
||||
@@ -1199,23 +1213,17 @@ function ConfigEditor({ cfg, updateCfg, onSync }: { cfg: ScheduleConfig; updateC
|
||||
<ArrayEditor
|
||||
items={normalized.statuses}
|
||||
onChange={(statuses) => updateCfg({ ...normalized, statuses })}
|
||||
createItem={() => ({ key: `status_${Date.now()}`, label: "", description: "" })}
|
||||
createItem={() => ({ key: "", label: "", description: "" })}
|
||||
label="Статусы групп"
|
||||
addLabel="Добавить статус"
|
||||
collapsible
|
||||
getItemTitle={(item) => item.label || "Новый статус"}
|
||||
renderItem={(item, _i, update) => (
|
||||
<div className="space-y-2">
|
||||
<InputField
|
||||
label="Ключ (латиницей)"
|
||||
value={item.key}
|
||||
onChange={(v) => update({ ...item, key: v.replace(/[^a-zA-Z0-9_]/g, "") })}
|
||||
placeholder="например: intensive"
|
||||
/>
|
||||
<InputField
|
||||
label="Название"
|
||||
value={item.label}
|
||||
onChange={(v) => update({ ...item, label: v })}
|
||||
onChange={(v) => update({ ...item, label: v, key: v.toLowerCase().replace(/[^a-zа-яё0-9]/gi, "_").replace(/_+/g, "_") })}
|
||||
placeholder="Название статуса"
|
||||
/>
|
||||
<InputField
|
||||
@@ -1262,7 +1270,12 @@ export default function ScheduleEditorPage() {
|
||||
|
||||
adminFetch("/api/admin/sections/scheduleConfig")
|
||||
.then((r) => r.json())
|
||||
.then((c: ScheduleConfig) => { if (c?.levels) setConfig(c); })
|
||||
.then((c: Partial<ScheduleConfig>) => {
|
||||
setConfig({
|
||||
levels: c?.levels ?? DEFAULT_CONFIG.levels,
|
||||
statuses: c?.statuses ?? DEFAULT_CONFIG.statuses,
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user