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:
2026-03-30 22:57:36 +03:00
parent 06be6b48ce
commit ae30be8f9d
19 changed files with 286 additions and 129 deletions
+21 -15
View File
@@ -2,7 +2,7 @@
import { useReducer, useMemo, useCallback } from "react";
import { SignupModal } from "@/components/ui/SignupModal";
import { CalendarDays, Users, LayoutGrid, SlidersHorizontal } from "lucide-react";
import { CalendarDays, Users, LayoutGrid, SlidersHorizontal, MapPin } from "lucide-react";
import { SectionHeading } from "@/components/ui/SectionHeading";
import { Reveal } from "@/components/ui/Reveal";
import { DayCard } from "./schedule/DayCard";
@@ -102,7 +102,10 @@ interface ScheduleProps {
export function Schedule({ data: schedule, scheduleConfig, classItems, teamMembers }: ScheduleProps) {
if (!schedule?.locations?.length) return null;
const [state, dispatch] = useReducer(scheduleReducer, initialState);
const [state, dispatch] = useReducer(scheduleReducer, {
...initialState,
locationMode: schedule.locations.length === 1 ? 0 : "all",
});
const { locationMode, viewMode, filterTrainerSet, filterTypes, filterStatusSet, filterLevel, filterTime, filterDaySet, bookingGroup } = state;
const isAllMode = locationMode === "all";
@@ -313,17 +316,19 @@ export function Schedule({ data: schedule, scheduleConfig, classItems, teamMembe
{/* Location tabs */}
<Reveal>
<div className="mt-8 flex justify-center gap-2 flex-wrap">
{/* "All studios" tab */}
<button
onClick={() => switchLocation("all")}
className={`inline-flex items-center gap-2 rounded-full px-5 py-2.5 text-sm font-medium transition-all duration-300 cursor-pointer ${
isAllMode ? activeTabClass : inactiveTabClass
}`}
>
<LayoutGrid size={14} />
<span className="hidden sm:inline">Все студии</span>
<span className="sm:hidden">Все</span>
</button>
{/* "All studios" tab — only when multiple locations */}
{schedule.locations.length > 1 && (
<button
onClick={() => switchLocation("all")}
className={`inline-flex items-center gap-2 rounded-full px-5 py-2.5 text-sm font-medium transition-all duration-300 cursor-pointer ${
isAllMode ? activeTabClass : inactiveTabClass
}`}
>
<LayoutGrid size={14} />
<span className="hidden sm:inline">Все студии</span>
<span className="sm:hidden">Все</span>
</button>
)}
{/* Per-location tabs */}
{schedule.locations.map((loc, i) => (
@@ -347,7 +352,7 @@ export function Schedule({ data: schedule, scheduleConfig, classItems, teamMembe
</button>
))}
</div>
</Reveal>
</Reveal>
{/* Mobile filter button — visible only on small screens */}
<Reveal>
@@ -450,6 +455,7 @@ export function Schedule({ data: schedule, scheduleConfig, classItems, teamMembe
hasActiveFilter={hasActiveFilter}
clearFilters={clearFilters}
showLocation={isAllMode}
statuses={scheduleConfig?.statuses}
/>
</Reveal>
@@ -464,7 +470,7 @@ export function Schedule({ data: schedule, scheduleConfig, classItems, teamMembe
key={day.day}
className={filteredDays.length === 1 ? "w-full max-w-[340px]" : ""}
>
<DayCard day={day} typeDots={typeDots} showLocation={isAllMode} filterTrainerSet={filterTrainerSet} toggleFilterTrainer={toggleFilterTrainerFromCard} filterTypes={filterTypes} toggleFilterType={toggleFilterTypeFromCard} />
<DayCard day={day} typeDots={typeDots} showLocation={isAllMode} filterTrainerSet={filterTrainerSet} toggleFilterTrainer={toggleFilterTrainerFromCard} filterTypes={filterTypes} toggleFilterType={toggleFilterTypeFromCard} statuses={scheduleConfig?.statuses} />
</div>
))}