feat: upgrade schedule with cross-location views, day/time filters, and clickable trainers

- Add "Все студии" tab merging all locations by weekday with location sub-headers
- Location tabs show hall name + address subtitle for clarity
- Add day multi-select and time-of-day preset filters (Утро/День/Вечер) behind collapsible "Когда" button
- Make trainer and type names clickable in day cards for inline filtering
- Add group view clustering classes by trainer+type+location
- Remove trainer dropdown from filter bar — filter by clicking names in schedule
- Add searchable icon picker and lucide-react icon rendering for classes admin/section

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 21:25:11 +03:00
parent 8ff7713cf2
commit 46ad10e8a0
8 changed files with 891 additions and 222 deletions

View File

@@ -1,7 +1,7 @@
"use client";
import Image from "next/image";
import { Flame, Sparkles, Wind, Zap, Star, Monitor } from "lucide-react";
import { icons } from "lucide-react";
import { SectionHeading } from "@/components/ui/SectionHeading";
import { Reveal } from "@/components/ui/Reveal";
import { ShowcaseLayout } from "@/components/ui/ShowcaseLayout";
@@ -9,14 +9,15 @@ import { useShowcaseRotation } from "@/hooks/useShowcaseRotation";
import type { ClassItem, SiteContent } from "@/types";
import { UI_CONFIG } from "@/lib/config";
const iconMap: Record<string, React.ReactNode> = {
flame: <Flame size={20} />,
sparkles: <Sparkles size={20} />,
wind: <Wind size={20} />,
zap: <Zap size={20} />,
star: <Star size={20} />,
monitor: <Monitor size={20} />,
};
// kebab "heart-pulse" → PascalCase "HeartPulse"
function toPascal(kebab: string) {
return kebab.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
}
function getIcon(key: string) {
const Icon = icons[toPascal(key) as keyof typeof icons];
return Icon ? <Icon size={20} /> : null;
}
interface ClassesProps {
data: SiteContent["classes"];
@@ -60,7 +61,7 @@ export function Classes({ data: classes }: ClassesProps) {
{/* Icon + name overlay */}
<div className="absolute bottom-0 left-0 right-0 p-6">
<div className="mb-2 inline-flex h-9 w-9 items-center justify-center rounded-lg bg-gold/20 text-gold-light backdrop-blur-sm">
{iconMap[item.icon]}
{getIcon(item.icon)}
</div>
<h3 className="text-2xl font-bold text-white">
{item.name}
@@ -87,7 +88,7 @@ export function Classes({ data: classes }: ClassesProps) {
: "bg-neutral-200/50 text-neutral-500 dark:bg-white/[0.06] dark:text-neutral-400"
}`}
>
{iconMap[item.icon]}
{getIcon(item.icon)}
</div>
<div className="min-w-0">
<p