- Sync all content from Instagram: fix addresses, trainer names, add 5 new trainers, remove 2 inactive, update class descriptions - Add FAQ section (11 Q&A items) and Pricing section (tabs: subscriptions, rental, rules) - Redesign with editorial magazine feel: centered headings, generous spacing, section glow effects, glassmorphism cards - Migrate entire accent palette from rose to warm gold (#c9a96e) - Replace low-res PNG logo with vector SVG traced via potrace — crisp at any size, animated gradient (black↔gold), heartbeat pulse animation - Make header brand name gold Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
120 lines
4.0 KiB
TypeScript
120 lines
4.0 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect } from "react";
|
|
import Image from "next/image";
|
|
import { X, Flame, Sparkles, Wind, Zap, Star, Monitor } from "lucide-react";
|
|
import type { ClassItem } from "@/types";
|
|
|
|
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} />,
|
|
};
|
|
|
|
interface ClassModalProps {
|
|
classItem: ClassItem | null;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export function ClassModal({ classItem, onClose }: ClassModalProps) {
|
|
useEffect(() => {
|
|
if (!classItem) return;
|
|
|
|
document.body.style.overflow = "hidden";
|
|
|
|
function handleKeyDown(e: KeyboardEvent) {
|
|
if (e.key === "Escape") onClose();
|
|
}
|
|
|
|
document.addEventListener("keydown", handleKeyDown);
|
|
return () => {
|
|
document.body.style.overflow = "";
|
|
document.removeEventListener("keydown", handleKeyDown);
|
|
};
|
|
}, [classItem, onClose]);
|
|
|
|
if (!classItem) return null;
|
|
|
|
const heroImage = classItem.images?.[0];
|
|
|
|
return (
|
|
<div
|
|
className="modal-overlay fixed inset-0 z-50 flex items-end justify-center bg-black/70 backdrop-blur-lg sm:items-center sm:p-4"
|
|
onClick={onClose}
|
|
>
|
|
<div
|
|
className="modal-content relative flex w-full max-h-[90vh] flex-col overflow-hidden rounded-t-3xl bg-white sm:max-w-2xl sm:rounded-3xl dark:bg-[#111]"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
{/* Hero image banner */}
|
|
{heroImage && (
|
|
<div className="relative h-52 w-full shrink-0 sm:h-64">
|
|
<Image
|
|
src={heroImage}
|
|
alt={classItem.name}
|
|
fill
|
|
className="object-cover"
|
|
/>
|
|
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/30 to-transparent" />
|
|
|
|
{/* Close button */}
|
|
<button
|
|
onClick={onClose}
|
|
className="absolute right-4 top-4 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-black/40 text-white/80 backdrop-blur-sm transition-all hover:bg-black/60 hover:text-white"
|
|
aria-label="Закрыть"
|
|
>
|
|
<X size={16} />
|
|
</button>
|
|
|
|
{/* Title on image */}
|
|
<div className="absolute bottom-0 left-0 right-0 p-6">
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-white/15 text-white backdrop-blur-sm">
|
|
{iconMap[classItem.icon]}
|
|
</div>
|
|
<h3 className="text-2xl font-bold text-white">
|
|
{classItem.name}
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Content */}
|
|
<div className="overflow-y-auto">
|
|
{/* Title fallback when no image */}
|
|
{!heroImage && (
|
|
<div className="flex items-center justify-between p-6 pb-0">
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-[#c9a96e]/10 text-[#a08050] dark:bg-[#c9a96e]/10 dark:text-[#d4b87a]">
|
|
{iconMap[classItem.icon]}
|
|
</div>
|
|
<h3 className="heading-text text-xl font-bold">
|
|
{classItem.name}
|
|
</h3>
|
|
</div>
|
|
<button
|
|
onClick={onClose}
|
|
className="rounded-full p-1.5 text-neutral-400 transition-all hover:bg-neutral-100 hover:text-neutral-900 dark:text-neutral-500 dark:hover:bg-white/[0.05] dark:hover:text-white"
|
|
aria-label="Закрыть"
|
|
>
|
|
<X size={18} />
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{classItem.detailedDescription && (
|
|
<div className="p-6 text-sm leading-relaxed whitespace-pre-line text-neutral-600 dark:text-neutral-300">
|
|
{classItem.detailedDescription}
|
|
</div>
|
|
)}
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|