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:
@@ -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>
|
||||
))}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user