fix: schedule status labels, Open Day halls, unsaved data guards
Schedule: - Status badges use admin config labels (not hardcoded text) everywhere - DayCard: level badge moved next to status badge - Single location: hide "Все студии" tab, auto-select the only hall - Group view: hide per-card address when all share same location - Filter tooltip z-index fixed (above dropdowns) - Trainer bio: status labels from config, not raw keys Open Day: - Hall name + address shown in schedule grid headers - Only one class card editable at a time (edit/create mutually exclusive) - Bigger action buttons (cancel/delete) on class cards - Create as empty draft (not pre-filled with published status) - Fix discount threshold input (allow delete to empty) - Skip auto-save during partial date input Admin: - SectionEditor: unsaved data guard (force-save before navigation) - Open Day + Team: same navigation guards - Contact: removed working hours field - TimeRangeField: allow end time hour changes - Schedule cards: visible borders, 90min default duration - Trainer bio: RichTextarea for description - Open Day: RichTextarea for description
This commit is contained in:
@@ -221,10 +221,10 @@ function ClassBlock({
|
||||
? { backgroundImage: "repeating-linear-gradient(135deg, transparent, transparent 4px, rgba(239,68,68,0.35) 4px, rgba(239,68,68,0.35) 8px)" }
|
||||
: {}),
|
||||
}}
|
||||
className={`absolute left-1 right-1 rounded-md border-l-3 px-2 py-0.5 text-left text-xs text-white cursor-grab active:cursor-grabbing overflow-hidden select-none ${colors} ${
|
||||
className={`absolute left-1 right-1 rounded-md border border-white/20 border-l-3 px-2 py-0.5 text-left text-xs text-white cursor-grab active:cursor-grabbing overflow-hidden select-none ${colors} ${
|
||||
isOverlapping ? "ring-2 ring-red-500 ring-offset-1 ring-offset-neutral-900" : ""
|
||||
} ${isDragging ? "opacity-30" : "hover:opacity-90"}`}
|
||||
title={`${cls.time}\n${cls.type}\n${cls.trainer}${cls.level ? ` (${cls.level})` : ""}`}
|
||||
} ${isDragging ? "opacity-30" : "hover:opacity-90 hover:border-white/40"}`}
|
||||
title={`${cls.time}\n${cls.type}\n${cls.trainer}${cls.level ? ` · ${cls.level}` : ""}${cls.status ? ` · ${cls.status}` : ""}`}
|
||||
>
|
||||
<div className="font-semibold truncate leading-tight">
|
||||
{parts[0]?.trim()}–{parts[1]?.trim()}
|
||||
@@ -235,6 +235,11 @@ function ClassBlock({
|
||||
{height > 48 && (
|
||||
<div className="truncate text-white/70 leading-tight">{cls.trainer}</div>
|
||||
)}
|
||||
{height > 64 && (cls.level || cls.status) && (
|
||||
<div className="truncate text-white/50 leading-tight text-[10px]">
|
||||
{[cls.level, cls.status].filter(Boolean).join(" · ")}
|
||||
</div>
|
||||
)}
|
||||
{isOverlapping && (
|
||||
<div className="text-red-200 font-bold leading-tight">⚠ Пересечение</div>
|
||||
)}
|
||||
@@ -876,10 +881,10 @@ function CalendarGrid({
|
||||
const y = e.clientY - rect.top;
|
||||
const rawMin = yToMinutes(y);
|
||||
snapped = Math.round((rawMin - 30) / SNAP_MINUTES) * SNAP_MINUTES;
|
||||
snapped = Math.max(HOUR_START * 60, Math.min(snapped, HOUR_END * 60 - 60));
|
||||
snapped = Math.max(HOUR_START * 60, Math.min(snapped, HOUR_END * 60 - 90));
|
||||
}
|
||||
const startTime = formatMinutes(snapped);
|
||||
const endTime = formatMinutes(snapped + 60);
|
||||
const endTime = formatMinutes(snapped + 90);
|
||||
|
||||
setHover(null);
|
||||
setNewClass({
|
||||
@@ -1005,8 +1010,8 @@ function CalendarGrid({
|
||||
{sortedDays.map((day, di) => {
|
||||
const showHover = hover && hover.dayIndex === di && !drag && !newClass && !editingClass;
|
||||
const hoverTop = showHover ? minutesToY(hover.startMin) : 0;
|
||||
const hoverHeight = HOUR_HEIGHT; // 1 hour
|
||||
const hoverEndMin = showHover ? hover.startMin + 60 : 0;
|
||||
const hoverHeight = HOUR_HEIGHT * 1.5; // 1.5 hours
|
||||
const hoverEndMin = showHover ? hover.startMin + 90 : 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -1026,7 +1031,7 @@ function CalendarGrid({
|
||||
const rawMin = yToMinutes(y);
|
||||
// Snap to 15-min and offset so the block is centered on cursor
|
||||
const snapped = Math.round((rawMin - 30) / SNAP_MINUTES) * SNAP_MINUTES;
|
||||
const clamped = Math.max(HOUR_START * 60, Math.min(snapped, HOUR_END * 60 - 60));
|
||||
const clamped = Math.max(HOUR_START * 60, Math.min(snapped, HOUR_END * 60 - 90));
|
||||
setHover({ dayIndex: di, startMin: clamped });
|
||||
}}
|
||||
onMouseLeave={() => setHover(null)}
|
||||
|
||||
Reference in New Issue
Block a user