feat: add schedule section with location tabs, filters, and status badges
Two locations (Притыцкого 62/М, Машерова 17/4) with day-grid layout, class type/trainer/status filters, and badges for availability and recruiting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { Hero } from "@/components/sections/Hero";
|
|||||||
import { Team } from "@/components/sections/Team";
|
import { Team } from "@/components/sections/Team";
|
||||||
import { About } from "@/components/sections/About";
|
import { About } from "@/components/sections/About";
|
||||||
import { Classes } from "@/components/sections/Classes";
|
import { Classes } from "@/components/sections/Classes";
|
||||||
|
import { Schedule } from "@/components/sections/Schedule";
|
||||||
import { Pricing } from "@/components/sections/Pricing";
|
import { Pricing } from "@/components/sections/Pricing";
|
||||||
import { FAQ } from "@/components/sections/FAQ";
|
import { FAQ } from "@/components/sections/FAQ";
|
||||||
import { Contact } from "@/components/sections/Contact";
|
import { Contact } from "@/components/sections/Contact";
|
||||||
@@ -14,6 +15,7 @@ export default function HomePage() {
|
|||||||
<About />
|
<About />
|
||||||
<Team />
|
<Team />
|
||||||
<Classes />
|
<Classes />
|
||||||
|
<Schedule />
|
||||||
<Pricing />
|
<Pricing />
|
||||||
<FAQ />
|
<FAQ />
|
||||||
<Contact />
|
<Contact />
|
||||||
|
|||||||
270
src/components/sections/Schedule.tsx
Normal file
270
src/components/sections/Schedule.tsx
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useMemo } from "react";
|
||||||
|
import { MapPin, Clock, User, X, ChevronDown } from "lucide-react";
|
||||||
|
import { siteContent } from "@/data/content";
|
||||||
|
import { SectionHeading } from "@/components/ui/SectionHeading";
|
||||||
|
import { Reveal } from "@/components/ui/Reveal";
|
||||||
|
|
||||||
|
const TYPE_DOT: Record<string, string> = {
|
||||||
|
"Exotic Pole Dance": "bg-[#c9a96e]",
|
||||||
|
"Pole Dance": "bg-rose-500",
|
||||||
|
"Body Plastic": "bg-purple-500",
|
||||||
|
"Трюковые комбинации с пилоном": "bg-amber-500",
|
||||||
|
};
|
||||||
|
|
||||||
|
type StatusFilter = "all" | "hasSlots" | "recruiting";
|
||||||
|
|
||||||
|
export function Schedule() {
|
||||||
|
const { schedule } = siteContent;
|
||||||
|
const [locationIndex, setLocationIndex] = useState(0);
|
||||||
|
const [filterTrainer, setFilterTrainer] = useState<string | null>(null);
|
||||||
|
const [filterType, setFilterType] = useState<string | null>(null);
|
||||||
|
const [filterStatus, setFilterStatus] = useState<StatusFilter>("all");
|
||||||
|
const [showTrainers, setShowTrainers] = useState(false);
|
||||||
|
const location = schedule.locations[locationIndex];
|
||||||
|
|
||||||
|
const { trainers, types, hasAnySlots, hasAnyRecruiting } = useMemo(() => {
|
||||||
|
const trainerSet = new Set<string>();
|
||||||
|
const typeSet = new Set<string>();
|
||||||
|
let slots = false;
|
||||||
|
let recruiting = false;
|
||||||
|
for (const day of location.days) {
|
||||||
|
for (const cls of day.classes) {
|
||||||
|
trainerSet.add(cls.trainer);
|
||||||
|
typeSet.add(cls.type);
|
||||||
|
if (cls.hasSlots) slots = true;
|
||||||
|
if (cls.recruiting) recruiting = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
trainers: Array.from(trainerSet).sort(),
|
||||||
|
types: Array.from(typeSet).sort(),
|
||||||
|
hasAnySlots: slots,
|
||||||
|
hasAnyRecruiting: recruiting,
|
||||||
|
};
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
|
const filteredDays = useMemo(() => {
|
||||||
|
const noFilter = !filterTrainer && !filterType && filterStatus === "all";
|
||||||
|
if (noFilter) return location.days;
|
||||||
|
return location.days
|
||||||
|
.map((day) => ({
|
||||||
|
...day,
|
||||||
|
classes: day.classes.filter(
|
||||||
|
(cls) =>
|
||||||
|
(!filterTrainer || cls.trainer === filterTrainer) &&
|
||||||
|
(!filterType || cls.type === filterType) &&
|
||||||
|
(filterStatus === "all" ||
|
||||||
|
(filterStatus === "hasSlots" && cls.hasSlots) ||
|
||||||
|
(filterStatus === "recruiting" && cls.recruiting))
|
||||||
|
),
|
||||||
|
}))
|
||||||
|
.filter((day) => day.classes.length > 0);
|
||||||
|
}, [location.days, filterTrainer, filterType, filterStatus]);
|
||||||
|
|
||||||
|
const hasActiveFilter = filterTrainer || filterType || filterStatus !== "all";
|
||||||
|
|
||||||
|
function clearFilters() {
|
||||||
|
setFilterTrainer(null);
|
||||||
|
setFilterType(null);
|
||||||
|
setFilterStatus("all");
|
||||||
|
}
|
||||||
|
|
||||||
|
const pillBase = "inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-[11px] font-medium transition-all duration-200 cursor-pointer";
|
||||||
|
const pillActive = "bg-[#c9a96e]/20 text-[#a08050] border border-[#c9a96e]/40 dark:text-[#d4b87a] dark:border-[#c9a96e]/30";
|
||||||
|
const pillInactive = "border border-neutral-200 text-neutral-500 hover:border-neutral-300 dark:border-white/[0.08] dark:text-white/35 dark:hover:border-white/15";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
id="schedule"
|
||||||
|
className="section-glow relative section-padding bg-neutral-50 dark:bg-[#050505] overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="section-divider absolute top-0 left-0 right-0" />
|
||||||
|
|
||||||
|
<div className="section-container">
|
||||||
|
<Reveal>
|
||||||
|
<SectionHeading centered>{schedule.title}</SectionHeading>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
|
{/* Location tabs */}
|
||||||
|
<Reveal>
|
||||||
|
<div className="mt-8 flex justify-center gap-2">
|
||||||
|
{schedule.locations.map((loc, i) => (
|
||||||
|
<button
|
||||||
|
key={loc.name}
|
||||||
|
onClick={() => {
|
||||||
|
setLocationIndex(i);
|
||||||
|
clearFilters();
|
||||||
|
setShowTrainers(false);
|
||||||
|
}}
|
||||||
|
className={`inline-flex items-center gap-2 rounded-full px-5 py-2.5 text-sm font-medium transition-all duration-300 cursor-pointer ${
|
||||||
|
i === locationIndex
|
||||||
|
? "bg-[#c9a96e] text-black shadow-[0_0_20px_rgba(201,169,110,0.3)]"
|
||||||
|
: "border border-neutral-300 text-neutral-500 hover:border-neutral-400 hover:text-neutral-700 dark:border-white/10 dark:text-neutral-400 dark:hover:text-white dark:hover:border-white/20"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<MapPin size={14} />
|
||||||
|
{loc.name}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
|
{/* Compact filters */}
|
||||||
|
<Reveal>
|
||||||
|
<div className="mt-5 flex flex-wrap items-center justify-center gap-1.5">
|
||||||
|
{/* Class types */}
|
||||||
|
{types.map((type) => (
|
||||||
|
<button
|
||||||
|
key={type}
|
||||||
|
onClick={() => setFilterType(filterType === type ? null : type)}
|
||||||
|
className={`${pillBase} ${filterType === type ? pillActive : pillInactive}`}
|
||||||
|
>
|
||||||
|
<span className={`h-1.5 w-1.5 rounded-full ${TYPE_DOT[type] ?? "bg-white/30"}`} />
|
||||||
|
{type}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Divider */}
|
||||||
|
<span className="mx-1 h-4 w-px bg-neutral-200 dark:bg-white/10" />
|
||||||
|
|
||||||
|
{/* Status filters */}
|
||||||
|
{hasAnySlots && (
|
||||||
|
<button
|
||||||
|
onClick={() => setFilterStatus(filterStatus === "hasSlots" ? "all" : "hasSlots")}
|
||||||
|
className={`${pillBase} ${filterStatus === "hasSlots" ? "bg-emerald-500/20 text-emerald-700 border border-emerald-500/40 dark:text-emerald-400 dark:border-emerald-500/30" : pillInactive}`}
|
||||||
|
>
|
||||||
|
<span className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
|
||||||
|
Есть места
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{hasAnyRecruiting && (
|
||||||
|
<button
|
||||||
|
onClick={() => setFilterStatus(filterStatus === "recruiting" ? "all" : "recruiting")}
|
||||||
|
className={`${pillBase} ${filterStatus === "recruiting" ? "bg-sky-500/20 text-sky-700 border border-sky-500/40 dark:text-sky-400 dark:border-sky-500/30" : pillInactive}`}
|
||||||
|
>
|
||||||
|
<span className="h-1.5 w-1.5 rounded-full bg-sky-500" />
|
||||||
|
Набор
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Divider */}
|
||||||
|
<span className="mx-1 h-4 w-px bg-neutral-200 dark:bg-white/10" />
|
||||||
|
|
||||||
|
{/* Trainer dropdown toggle */}
|
||||||
|
<button
|
||||||
|
onClick={() => setShowTrainers(!showTrainers)}
|
||||||
|
className={`${pillBase} ${filterTrainer ? pillActive : pillInactive}`}
|
||||||
|
>
|
||||||
|
<User size={11} />
|
||||||
|
{filterTrainer ?? "Тренер"}
|
||||||
|
<ChevronDown size={10} className={`transition-transform duration-200 ${showTrainers ? "rotate-180" : ""}`} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Clear */}
|
||||||
|
{hasActiveFilter && (
|
||||||
|
<button
|
||||||
|
onClick={clearFilters}
|
||||||
|
className="inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-[11px] text-neutral-400 hover:text-neutral-600 dark:text-white/25 dark:hover:text-white/50 transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
<X size={11} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Trainer pills — expandable */}
|
||||||
|
{showTrainers && (
|
||||||
|
<div className="mt-2 flex flex-wrap items-center justify-center gap-1.5">
|
||||||
|
{trainers.map((trainer) => (
|
||||||
|
<button
|
||||||
|
key={trainer}
|
||||||
|
onClick={() => {
|
||||||
|
setFilterTrainer(filterTrainer === trainer ? null : trainer);
|
||||||
|
setShowTrainers(false);
|
||||||
|
}}
|
||||||
|
className={`${pillBase} ${filterTrainer === trainer ? pillActive : pillInactive}`}
|
||||||
|
>
|
||||||
|
{trainer}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Reveal>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Day columns — full width */}
|
||||||
|
<Reveal>
|
||||||
|
<div
|
||||||
|
key={`${locationIndex}-${filterTrainer}-${filterType}-${filterStatus}`}
|
||||||
|
className={`mt-8 grid grid-cols-1 gap-3 px-4 sm:grid-cols-2 sm:px-6 lg:grid-cols-3 lg:px-8 xl:px-6 ${filteredDays.length >= 7 ? "xl:grid-cols-7" : filteredDays.length >= 4 ? "xl:grid-cols-" + filteredDays.length : "xl:grid-cols-4"}`}
|
||||||
|
style={filteredDays.length < 4 && filteredDays.length > 0 ? { maxWidth: filteredDays.length * 320 + (filteredDays.length - 1) * 12, marginInline: "auto" } : undefined}
|
||||||
|
>
|
||||||
|
{filteredDays.map((day) => (
|
||||||
|
<div
|
||||||
|
key={day.day}
|
||||||
|
className="rounded-2xl border border-neutral-200 bg-white dark:border-white/[0.06] dark:bg-[#0a0a0a] overflow-hidden"
|
||||||
|
>
|
||||||
|
{/* Day header */}
|
||||||
|
<div className="border-b border-neutral-100 bg-neutral-50 px-5 py-4 dark:border-white/[0.04] dark:bg-white/[0.02]">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-[#c9a96e]/10 text-sm font-bold text-[#a08050] dark:bg-[#c9a96e]/10 dark:text-[#d4b87a]">
|
||||||
|
{day.dayShort}
|
||||||
|
</span>
|
||||||
|
<span className="text-base font-semibold text-neutral-900 dark:text-white/90">
|
||||||
|
{day.day}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Classes */}
|
||||||
|
<div className="divide-y divide-neutral-100 dark:divide-white/[0.04]">
|
||||||
|
{day.classes.map((cls, i) => (
|
||||||
|
<div key={i} className={`px-5 py-3.5 ${cls.hasSlots ? "bg-emerald-500/5" : cls.recruiting ? "bg-sky-500/5" : ""}`}>
|
||||||
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
<div className="flex items-center gap-2 text-sm text-neutral-500 dark:text-white/40">
|
||||||
|
<Clock size={13} />
|
||||||
|
<span className="font-semibold">{cls.time}</span>
|
||||||
|
</div>
|
||||||
|
{cls.hasSlots && (
|
||||||
|
<span className="shrink-0 rounded-full bg-emerald-500/15 border border-emerald-500/25 px-2 py-0.5 text-[10px] font-semibold text-emerald-600 dark:text-emerald-400">
|
||||||
|
есть места
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{cls.recruiting && (
|
||||||
|
<span className="shrink-0 rounded-full bg-sky-500/15 border border-sky-500/25 px-2 py-0.5 text-[10px] font-semibold text-sky-600 dark:text-sky-400">
|
||||||
|
набор
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="mt-1.5 flex items-center gap-2 text-sm font-medium text-neutral-800 dark:text-white/80">
|
||||||
|
<User size={13} className="shrink-0 text-neutral-400 dark:text-white/30" />
|
||||||
|
{cls.trainer}
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 flex items-center gap-2 flex-wrap">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className={`h-2 w-2 shrink-0 rounded-full ${TYPE_DOT[cls.type] ?? "bg-white/30"}`} />
|
||||||
|
<span className="text-xs text-neutral-500 dark:text-white/40">{cls.type}</span>
|
||||||
|
</div>
|
||||||
|
{cls.level && (
|
||||||
|
<span className="rounded-full bg-rose-500/15 border border-rose-500/25 px-2 py-0.5 text-[10px] font-semibold text-rose-600 dark:text-rose-400">
|
||||||
|
{cls.level}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{filteredDays.length === 0 && (
|
||||||
|
<div className="col-span-full py-12 text-center text-sm text-neutral-400 dark:text-white/30">
|
||||||
|
Нет занятий по выбранным фильтрам
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -307,6 +307,137 @@ export const siteContent: SiteContent = {
|
|||||||
"В случае болезни, подтверждённой больничным листом, возможно продление срока действия абонемента.",
|
"В случае болезни, подтверждённой больничным листом, возможно продление срока действия абонемента.",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
schedule: {
|
||||||
|
title: "Расписание",
|
||||||
|
locations: [
|
||||||
|
{
|
||||||
|
name: "Притыцкого 62/М",
|
||||||
|
address: "г. Минск, Притыцкого, 62/М",
|
||||||
|
days: [
|
||||||
|
{
|
||||||
|
day: "Понедельник",
|
||||||
|
dayShort: "ПН",
|
||||||
|
classes: [
|
||||||
|
{ time: "11:00–12:30", trainer: "Кристина Войтович", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "18:00–19:30", trainer: "Надежда Сыч", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "19:30–21:00", trainer: "Екатерина Матлахова", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "21:00–22:30", trainer: "Кристина Войтович", type: "Exotic Pole Dance" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Вторник",
|
||||||
|
dayShort: "ВТ",
|
||||||
|
classes: [
|
||||||
|
{ time: "10:00–11:30", trainer: "Анжела Бобко", type: "Pole Dance", recruiting: true },
|
||||||
|
{ time: "18:00–19:30", trainer: "Ирина Третьякович", type: "Exotic Pole Dance", hasSlots: true },
|
||||||
|
{ time: "19:30–21:00", trainer: "Ирина Третьякович", type: "Exotic Pole Dance", hasSlots: true },
|
||||||
|
{ time: "21:00–22:30", trainer: "Виктор Артёмов", type: "Трюковые комбинации с пилоном" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Среда",
|
||||||
|
dayShort: "СР",
|
||||||
|
classes: [
|
||||||
|
{ time: "18:30–20:00", trainer: "Виктор Артёмов", type: "Трюковые комбинации с пилоном", level: "Продвинутый" },
|
||||||
|
{ time: "20:00–21:30", trainer: "Алёна Чигилейчик", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "21:30–22:30", trainer: "Алёна Чигилейчик", type: "Pole Dance" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Четверг",
|
||||||
|
dayShort: "ЧТ",
|
||||||
|
classes: [
|
||||||
|
{ time: "11:00–12:30", trainer: "Кристина Войтович", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "18:00–19:30", trainer: "Надежда Сыч", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "19:30–21:00", trainer: "Екатерина Матлахова", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "21:00–22:30", trainer: "Кристина Войтович", type: "Exotic Pole Dance" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Пятница",
|
||||||
|
dayShort: "ПТ",
|
||||||
|
classes: [
|
||||||
|
{ time: "10:00–11:30", trainer: "Анжела Бобко", type: "Pole Dance", recruiting: true },
|
||||||
|
{ time: "18:00–19:30", trainer: "Ирина Третьякович", type: "Exotic Pole Dance", hasSlots: true },
|
||||||
|
{ time: "19:30–21:00", trainer: "Ирина Третьякович", type: "Exotic Pole Dance", hasSlots: true },
|
||||||
|
{ time: "21:00–22:30", trainer: "Виктор Артёмов", type: "Трюковые комбинации с пилоном" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Суббота",
|
||||||
|
dayShort: "СБ",
|
||||||
|
classes: [
|
||||||
|
{ time: "14:00–15:00", trainer: "Алёна Чигилейчик", type: "Pole Dance" },
|
||||||
|
{ time: "15:00–16:30", trainer: "Алёна Чигилейчик", type: "Exotic Pole Dance" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Воскресенье",
|
||||||
|
dayShort: "ВС",
|
||||||
|
classes: [
|
||||||
|
{ time: "12:00–13:30", trainer: "Кристина Войтович", type: "Body Plastic" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Машерова 17/4",
|
||||||
|
address: "г. Минск, Машерова, 17/4",
|
||||||
|
days: [
|
||||||
|
{
|
||||||
|
day: "Понедельник",
|
||||||
|
dayShort: "ПН",
|
||||||
|
classes: [
|
||||||
|
{ time: "18:00–19:00", trainer: "Ирина Карпусь", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "19:00–20:30", trainer: "Анна Тарыба", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "20:30–22:00", trainer: "Анна Тарыба", type: "Exotic Pole Dance" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Вторник",
|
||||||
|
dayShort: "ВТ",
|
||||||
|
classes: [
|
||||||
|
{ time: "18:30–20:00", trainer: "Анастасия Чалей", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "21:30–23:00", trainer: "Лилия Огурцова", type: "Exotic Pole Dance", hasSlots: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Среда",
|
||||||
|
dayShort: "СР",
|
||||||
|
classes: [
|
||||||
|
{ time: "18:00–19:30", trainer: "Ольга Демидова", type: "Pole Dance" },
|
||||||
|
{ time: "19:30–21:00", trainer: "Ольга Демидова", type: "Body Plastic" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Четверг",
|
||||||
|
dayShort: "ЧТ",
|
||||||
|
classes: [
|
||||||
|
{ time: "18:00–19:00", trainer: "Ирина Карпусь", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "19:00–20:30", trainer: "Анна Тарыба", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "20:30–22:00", trainer: "Анна Тарыба", type: "Exotic Pole Dance" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Пятница",
|
||||||
|
dayShort: "ПТ",
|
||||||
|
classes: [
|
||||||
|
{ time: "18:30–20:00", trainer: "Анастасия Чалей", type: "Exotic Pole Dance" },
|
||||||
|
{ time: "21:30–23:00", trainer: "Лилия Огурцова", type: "Exotic Pole Dance", hasSlots: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
day: "Суббота",
|
||||||
|
dayShort: "СБ",
|
||||||
|
classes: [
|
||||||
|
{ time: "10:30–12:00", trainer: "Елена Тарасевич", type: "Body Plastic" },
|
||||||
|
{ time: "12:00–13:30", trainer: "Ольга Демидова", type: "Pole Dance" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
contact: {
|
contact: {
|
||||||
title: "Контакты",
|
title: "Контакты",
|
||||||
addresses: [
|
addresses: [
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export const NAV_LINKS: NavLink[] = [
|
|||||||
{ label: "О нас", href: "#about" },
|
{ label: "О нас", href: "#about" },
|
||||||
{ label: "Команда", href: "#team" },
|
{ label: "Команда", href: "#team" },
|
||||||
{ label: "Направления", href: "#classes" },
|
{ label: "Направления", href: "#classes" },
|
||||||
|
{ label: "Расписание", href: "#schedule" },
|
||||||
{ label: "Стоимость", href: "#pricing" },
|
{ label: "Стоимость", href: "#pricing" },
|
||||||
{ label: "FAQ", href: "#faq" },
|
{ label: "FAQ", href: "#faq" },
|
||||||
{ label: "Контакты", href: "#contact" },
|
{ label: "Контакты", href: "#contact" },
|
||||||
|
|||||||
@@ -25,6 +25,27 @@ export interface PricingItem {
|
|||||||
note?: string;
|
note?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ScheduleClass {
|
||||||
|
time: string;
|
||||||
|
trainer: string;
|
||||||
|
type: string;
|
||||||
|
level?: string;
|
||||||
|
hasSlots?: boolean;
|
||||||
|
recruiting?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScheduleDay {
|
||||||
|
day: string;
|
||||||
|
dayShort: string;
|
||||||
|
classes: ScheduleClass[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScheduleLocation {
|
||||||
|
name: string;
|
||||||
|
address: string;
|
||||||
|
days: ScheduleDay[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ContactInfo {
|
export interface ContactInfo {
|
||||||
title: string;
|
title: string;
|
||||||
addresses: string[];
|
addresses: string[];
|
||||||
@@ -69,5 +90,9 @@ export interface SiteContent {
|
|||||||
rentalItems: PricingItem[];
|
rentalItems: PricingItem[];
|
||||||
rules: string[];
|
rules: string[];
|
||||||
};
|
};
|
||||||
|
schedule: {
|
||||||
|
title: string;
|
||||||
|
locations: ScheduleLocation[];
|
||||||
|
};
|
||||||
contact: ContactInfo;
|
contact: ContactInfo;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user