feat: same-time checkbox for multi-day class groups
- "Одинаковое время" checkbox (default on) syncs time across all days - Uncheck to set per-day times (e.g., Mon 12:00, Fri 18:00) - Checkbox only appears when 2+ days selected - Single day: just shows one time field, no checkbox - Unified day selector for both new and edit modes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -254,6 +254,13 @@ function ClassModal({
|
||||
|
||||
const [dayTimes, setDayTimes] = useState<Record<string, string>>(initialDayTimes);
|
||||
|
||||
// "Same time for all days" — default true if all existing times match
|
||||
const allTimesMatch = useMemo(() => {
|
||||
const vals = Object.values(initialDayTimes);
|
||||
return vals.length <= 1 || vals.every((t) => t === vals[0]);
|
||||
}, [initialDayTimes]);
|
||||
const [sameTime, setSameTime] = useState(allTimesMatch);
|
||||
|
||||
const selectedDays = new Set(Object.keys(dayTimes));
|
||||
|
||||
function toggleDay(day: string) {
|
||||
@@ -265,14 +272,35 @@ function ClassModal({
|
||||
delete next[day];
|
||||
return next;
|
||||
}
|
||||
// New day gets time from current day
|
||||
return { ...prev, [day]: prev[currentDay] || cls.time };
|
||||
// New day gets time from current day or first existing
|
||||
const refTime = sameTime
|
||||
? (Object.values(prev)[0] || cls.time)
|
||||
: (prev[currentDay] || cls.time);
|
||||
return { ...prev, [day]: refTime };
|
||||
});
|
||||
}
|
||||
|
||||
function updateDayTime(day: string, time: string) {
|
||||
if (sameTime) {
|
||||
// Update all days at once
|
||||
setDayTimes((prev) => {
|
||||
const next: Record<string, string> = {};
|
||||
for (const d of Object.keys(prev)) next[d] = time;
|
||||
return next;
|
||||
});
|
||||
} else {
|
||||
setDayTimes((prev) => ({ ...prev, [day]: time }));
|
||||
}
|
||||
}
|
||||
|
||||
function updateSharedTime(time: string) {
|
||||
setDraft({ ...draft, time });
|
||||
setDayTimes((prev) => {
|
||||
const next: Record<string, string> = {};
|
||||
for (const d of Object.keys(prev)) next[d] = time;
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
// Compute what changed for the hint (edit mode only)
|
||||
const originalDays = new Set(Object.keys(initialDayTimes));
|
||||
@@ -300,34 +328,9 @@ function ClassModal({
|
||||
<div>
|
||||
<label className="block text-sm text-neutral-400 mb-2">Дни</label>
|
||||
|
||||
{/* Selected days with per-day time (edit mode) or simple buttons (new mode) */}
|
||||
{!isNew && selectedDays.size > 0 && (
|
||||
<div className="space-y-1.5 mb-2">
|
||||
{allDays.filter((d) => selectedDays.has(d.day)).map((d) => (
|
||||
<div key={d.day} className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleDay(d.day)}
|
||||
className="shrink-0 rounded-lg px-2.5 py-1 text-xs font-medium bg-gold/20 text-gold border border-gold/40 min-w-[36px]"
|
||||
>
|
||||
{d.dayShort}
|
||||
</button>
|
||||
<div className="flex-1">
|
||||
<TimeRangeField
|
||||
label=""
|
||||
value={dayTimes[d.day] || ""}
|
||||
onChange={(v) => updateDayTime(d.day, v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Unselected days — toggle buttons */}
|
||||
{/* Day toggle buttons */}
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{isNew
|
||||
? allDays.map((d) => {
|
||||
{allDays.map((d) => {
|
||||
const active = selectedDays.has(d.day);
|
||||
return (
|
||||
<button
|
||||
@@ -343,18 +346,7 @@ function ClassModal({
|
||||
{d.dayShort}
|
||||
</button>
|
||||
);
|
||||
})
|
||||
: allDays.filter((d) => !selectedDays.has(d.day)).map((d) => (
|
||||
<button
|
||||
key={d.day}
|
||||
type="button"
|
||||
onClick={() => toggleDay(d.day)}
|
||||
className="rounded-lg px-3 py-1.5 text-xs font-medium border border-white/10 text-neutral-500 hover:text-white hover:border-white/20 transition-all"
|
||||
>
|
||||
+ {d.dayShort}
|
||||
</button>
|
||||
))
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
|
||||
{!isNew && (addedDays.length > 0 || removedDays.length > 0) && (
|
||||
@@ -374,21 +366,55 @@ function ClassModal({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Time — only for new class (edit mode has per-day times above) */}
|
||||
{isNew && (
|
||||
<TimeRangeField
|
||||
label="Время"
|
||||
value={draft.time}
|
||||
onChange={(v) => {
|
||||
setDraft({ ...draft, time: v });
|
||||
// Update all selected day times
|
||||
{/* Same time checkbox + time fields */}
|
||||
{selectedDays.size > 1 && (
|
||||
<label className="flex items-center gap-2 text-sm text-neutral-300 cursor-pointer select-none">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={sameTime}
|
||||
onChange={(e) => {
|
||||
const checked = e.target.checked;
|
||||
setSameTime(checked);
|
||||
if (checked) {
|
||||
// Sync all days to current day's time
|
||||
const refTime = dayTimes[currentDay] || Object.values(dayTimes)[0] || cls.time;
|
||||
setDayTimes((prev) => {
|
||||
const next: Record<string, string> = {};
|
||||
for (const day of Object.keys(prev)) next[day] = v;
|
||||
for (const d of Object.keys(prev)) next[d] = refTime;
|
||||
return next;
|
||||
});
|
||||
}
|
||||
}}
|
||||
className="rounded border-white/20 bg-neutral-800 text-gold focus:ring-gold/50"
|
||||
/>
|
||||
Одинаковое время
|
||||
</label>
|
||||
)}
|
||||
|
||||
{sameTime || selectedDays.size <= 1 ? (
|
||||
<TimeRangeField
|
||||
label="Время"
|
||||
value={Object.values(dayTimes)[0] || draft.time}
|
||||
onChange={updateSharedTime}
|
||||
/>
|
||||
) : (
|
||||
<div className="space-y-1.5">
|
||||
<label className="block text-sm text-neutral-400">Время по дням</label>
|
||||
{allDays.filter((d) => selectedDays.has(d.day)).map((d) => (
|
||||
<div key={d.day} className="flex items-center gap-2">
|
||||
<span className="shrink-0 text-xs font-medium text-neutral-400 min-w-[28px]">
|
||||
{d.dayShort}
|
||||
</span>
|
||||
<div className="flex-1">
|
||||
<TimeRangeField
|
||||
label=""
|
||||
value={dayTimes[d.day] || ""}
|
||||
onChange={(v) => updateDayTime(d.day, v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SelectField
|
||||
|
||||
Reference in New Issue
Block a user