Files
blackheart-website/src/components/sections/Pricing.tsx
diana.dolgolyova 604a52e04c feat: upgrade pricing admin with popular/featured selects and price input with BYN badge
Replace per-item toggles with top-level dropdown selects for popular and featured items.
Add PriceField component with inline gold BYN suffix badge.
Public Pricing component now uses dynamic popular/featured flags from data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:47:41 +03:00

195 lines
8.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import { CreditCard, Building2, ScrollText, Crown, Sparkles } from "lucide-react";
import { SectionHeading } from "@/components/ui/SectionHeading";
import { Reveal } from "@/components/ui/Reveal";
import { BookingModal } from "@/components/ui/BookingModal";
import type { SiteContent } from "@/types/content";
type Tab = "prices" | "rental" | "rules";
interface PricingProps {
data: SiteContent["pricing"];
}
export function Pricing({ data: pricing }: PricingProps) {
const [activeTab, setActiveTab] = useState<Tab>("prices");
const [bookingOpen, setBookingOpen] = useState(false);
const tabs: { id: Tab; label: string; icon: React.ReactNode }[] = [
{ id: "prices", label: "Абонементы", icon: <CreditCard size={16} /> },
{ id: "rental", label: "Аренда зала", icon: <Building2 size={16} /> },
{ id: "rules", label: "Правила", icon: <ScrollText size={16} /> },
];
// Split items: featured (big card) vs regular
const featuredItem = pricing.items.find((item) => item.featured);
const regularItems = pricing.items.filter((item) => !item.featured);
return (
<section id="pricing" className="section-glow relative section-padding bg-neutral-50 dark:bg-[#050505]">
<div className="section-divider absolute top-0 left-0 right-0" />
<div className="section-container">
<Reveal>
<SectionHeading centered>{pricing.title}</SectionHeading>
</Reveal>
{/* Tabs */}
<Reveal>
<div className="mt-12 flex flex-wrap justify-center gap-2">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`inline-flex items-center gap-2 rounded-full px-6 py-2.5 text-sm font-medium transition-all duration-300 cursor-pointer ${
activeTab === tab.id
? "bg-gold text-black shadow-lg shadow-gold/25"
: "bg-neutral-100 text-neutral-600 hover:bg-neutral-200 dark:bg-white/[0.06] dark:text-neutral-300 dark:hover:bg-white/[0.1]"
}`}
>
{tab.icon}
{tab.label}
</button>
))}
</div>
</Reveal>
{/* Prices tab */}
{activeTab === "prices" && (
<Reveal>
<div className="mx-auto mt-10 max-w-4xl">
<p className="mb-8 text-center text-sm text-neutral-500 dark:text-neutral-400">
{pricing.subtitle}
</p>
{/* Cards grid */}
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
{regularItems.map((item, i) => {
const isPopular = item.popular ?? false;
return (
<button
key={i}
onClick={() => setBookingOpen(true)}
className={`group relative cursor-pointer rounded-2xl border p-5 transition-all duration-300 text-left ${
isPopular
? "border-gold/40 bg-gradient-to-br from-gold/10 via-transparent to-gold/5 dark:from-gold/[0.07] dark:to-gold/[0.02] shadow-lg shadow-gold/10 hover:shadow-xl hover:shadow-gold/20"
: "border-neutral-200 bg-white hover:border-neutral-300 dark:border-white/[0.06] dark:bg-[#0a0a0a] dark:hover:border-white/[0.12]"
}`}
>
{/* Popular badge */}
{isPopular && (
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
<span className="inline-flex items-center gap-1 rounded-full bg-gold px-3 py-1 text-[10px] font-bold uppercase tracking-wider text-black shadow-md shadow-gold/30">
<Sparkles size={10} />
Популярный
</span>
</div>
)}
<div className={isPopular ? "mt-1" : ""}>
{/* Name */}
<p className={`text-sm font-medium ${isPopular ? "text-gold-dark dark:text-gold-light" : "text-neutral-700 dark:text-neutral-300"}`}>
{item.name}
</p>
{/* Note */}
{item.note && (
<p className="mt-1 text-xs text-neutral-400 dark:text-neutral-500">
{item.note}
</p>
)}
{/* Price */}
<p className={`mt-3 font-display text-2xl font-bold ${isPopular ? "text-gold" : "text-neutral-900 dark:text-white"}`}>
{item.price}
</p>
</div>
</button>
);
})}
</div>
{/* Featured — big card */}
{featuredItem && (
<button onClick={() => setBookingOpen(true)} className="mt-6 w-full cursor-pointer text-left team-card-glitter rounded-2xl border border-gold/30 bg-gradient-to-r from-gold/10 via-gold/5 to-gold/10 dark:from-gold/[0.06] dark:via-transparent dark:to-gold/[0.06] p-6 sm:p-8 transition-shadow duration-300 hover:shadow-xl hover:shadow-gold/20">
<div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-between">
<div className="text-center sm:text-left">
<div className="flex items-center justify-center gap-2 sm:justify-start">
<Crown size={18} className="text-gold" />
<p className="text-lg font-bold text-neutral-900 dark:text-white">
{featuredItem.name}
</p>
</div>
{featuredItem.note && (
<p className="mt-1 text-sm text-neutral-500 dark:text-neutral-400">
{featuredItem.note}
</p>
)}
</div>
<p className="shrink-0 font-display text-3xl font-bold text-gold">
{featuredItem.price}
</p>
</div>
</button>
)}
</div>
</Reveal>
)}
{/* Rental tab */}
{activeTab === "rental" && (
<Reveal>
<div className="mx-auto mt-10 max-w-2xl space-y-3">
{pricing.rentalItems.map((item, i) => (
<button
key={i}
onClick={() => setBookingOpen(true)}
className="w-full cursor-pointer text-left flex items-center justify-between gap-4 rounded-2xl border border-neutral-200 bg-white px-6 py-5 transition-colors hover:border-neutral-300 dark:border-white/[0.06] dark:bg-[#0a0a0a] dark:hover:border-white/[0.12]"
>
<div>
<p className="font-medium text-neutral-900 dark:text-white">
{item.name}
</p>
{item.note && (
<p className="mt-0.5 text-sm text-neutral-500 dark:text-neutral-400">
{item.note}
</p>
)}
</div>
<span className="shrink-0 font-display text-xl font-bold text-gold-dark dark:text-gold-light">
{item.price}
</span>
</button>
))}
</div>
</Reveal>
)}
{/* Rules tab */}
{activeTab === "rules" && (
<Reveal>
<div className="mx-auto mt-10 max-w-2xl space-y-3">
{pricing.rules.map((rule, i) => (
<div
key={i}
className="flex gap-4 rounded-2xl border border-neutral-200 bg-white px-5 py-4 dark:border-white/[0.06] dark:bg-[#0a0a0a]"
>
<span className="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-gold/10 text-xs font-bold text-gold-dark dark:bg-gold/10 dark:text-gold-light">
{i + 1}
</span>
<p className="text-sm leading-relaxed text-neutral-700 dark:text-neutral-300">
{rule}
</p>
</div>
))}
</div>
</Reveal>
)}
</div>
<BookingModal open={bookingOpen} onClose={() => setBookingOpen(false)} />
</section>
);
}