- Extract shared sanitization to src/lib/validation.ts, apply to all 3 registration routes (#2) - Replace key={index} with stable keys in About and News (#4) - Add 5-min in-memory content cache in content.ts, invalidate on admin section save (#6) - Refactor Schedule from 8 useState calls to useReducer — single dispatch, fewer re-renders (#8) - Remove Hero scroll indicator, add auto-scroll to next section on wheel/swipe Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
67 lines
2.5 KiB
TypeScript
67 lines
2.5 KiB
TypeScript
import { Users, Layers, MapPin } from "lucide-react";
|
|
import { SectionHeading } from "@/components/ui/SectionHeading";
|
|
import { Reveal } from "@/components/ui/Reveal";
|
|
import type { SiteContent } from "@/types/content";
|
|
|
|
interface AboutStats {
|
|
trainers: number;
|
|
classes: number;
|
|
locations: number;
|
|
}
|
|
|
|
interface AboutProps {
|
|
data: SiteContent["about"];
|
|
stats: AboutStats;
|
|
}
|
|
|
|
export function About({ data: about, stats }: AboutProps) {
|
|
const statItems = [
|
|
{ icon: <Users size={22} />, value: String(stats.trainers), label: "тренеров" },
|
|
{ icon: <Layers size={22} />, value: String(stats.classes), label: "направлений" },
|
|
{ icon: <MapPin size={22} />, value: String(stats.locations), label: "зала в Минске" },
|
|
];
|
|
|
|
return (
|
|
<section id="about" className="section-glow relative section-padding bg-neutral-100 dark:bg-[#080808]">
|
|
<div className="section-divider absolute top-0 left-0 right-0" />
|
|
<div className="section-container">
|
|
<Reveal>
|
|
<SectionHeading centered>{about.title}</SectionHeading>
|
|
</Reveal>
|
|
|
|
<div className="mt-14 mx-auto max-w-2xl space-y-8 text-center">
|
|
{about.paragraphs.map((text) => (
|
|
<Reveal key={text}>
|
|
<p className="text-xl leading-relaxed text-neutral-600 dark:text-neutral-300 sm:text-2xl">
|
|
{text}
|
|
</p>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
|
|
{/* Stats */}
|
|
<Reveal>
|
|
<div className="mx-auto mt-14 grid max-w-3xl grid-cols-3 gap-4 sm:gap-8">
|
|
{statItems.map((stat, i) => (
|
|
<div
|
|
key={i}
|
|
className="group flex flex-col items-center gap-3 rounded-2xl border border-neutral-200 bg-white/50 p-6 transition-all duration-300 hover:border-gold/30 sm:p-8 dark:border-white/[0.06] dark:bg-white/[0.02] dark:hover:border-gold/20"
|
|
>
|
|
<div className="flex h-11 w-11 items-center justify-center rounded-xl bg-gold/10 text-gold-dark transition-colors group-hover:bg-gold/20 dark:text-gold-light">
|
|
{stat.icon}
|
|
</div>
|
|
<span className="font-display text-3xl font-bold text-neutral-900 sm:text-4xl dark:text-white">
|
|
{stat.value}
|
|
</span>
|
|
<span className="text-sm text-neutral-500 dark:text-neutral-400">
|
|
{stat.label}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|