Files
blackheart-website/src/components/ui/BookingModal.tsx
diana.dolgolyova 6981376171 feat: add Записаться button to group cards with pre-filled Instagram DM
- BookingModal now accepts optional groupInfo for pre-filled message
- Trainer profile: each group card has Записаться button
- Schedule group view: each group card has Записаться button
- Message includes class type, trainer, days, and time

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 16:45:27 +03:00

203 lines
7.5 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, useEffect, useCallback } from "react";
import { createPortal } from "react-dom";
import { X, Instagram, Send, CheckCircle, Phone } from "lucide-react";
import { siteContent } from "@/data/content";
interface BookingModalProps {
open: boolean;
onClose: () => void;
groupInfo?: string;
}
export function BookingModal({ open, onClose, groupInfo }: BookingModalProps) {
const { contact } = siteContent;
const [name, setName] = useState("");
const [phone, setPhone] = useState("+375 ");
// Format phone: +375 (XX) XXX-XX-XX
function handlePhoneChange(raw: string) {
// Strip everything except digits
let digits = raw.replace(/\D/g, "");
// Ensure starts with 375
if (!digits.startsWith("375")) {
digits = "375" + digits.replace(/^375?/, "");
}
// Limit to 12 digits (375 + 9 digits)
digits = digits.slice(0, 12);
// Format
let formatted = "+375";
const rest = digits.slice(3);
if (rest.length > 0) formatted += " (" + rest.slice(0, 2);
if (rest.length >= 2) formatted += ") ";
if (rest.length > 2) formatted += rest.slice(2, 5);
if (rest.length > 5) formatted += "-" + rest.slice(5, 7);
if (rest.length > 7) formatted += "-" + rest.slice(7, 9);
setPhone(formatted);
}
const [submitted, setSubmitted] = useState(false);
// Close on Escape
useEffect(() => {
if (!open) return;
function onKey(e: KeyboardEvent) {
if (e.key === "Escape") onClose();
}
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, [open, onClose]);
// Lock body scroll
useEffect(() => {
if (open) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}
return () => {
document.body.style.overflow = "";
};
}, [open]);
const handleSubmit = useCallback(
(e: React.FormEvent) => {
e.preventDefault();
// Build Instagram DM message with pre-filled text
const groupText = groupInfo ? ` (${groupInfo})` : "";
const message = `Здравствуйте! Меня зовут ${name}, хочу записаться на занятие${groupText}. Мой телефон: ${phone}`;
const instagramUrl = `https://ig.me/m/blackheartdancehouse?text=${encodeURIComponent(message)}`;
window.open(instagramUrl, "_blank");
setSubmitted(true);
},
[name, phone]
);
const handleClose = useCallback(() => {
onClose();
// Reset after animation
setTimeout(() => {
setName("");
setPhone("+375 ");
setSubmitted(false);
}, 300);
}, [onClose]);
if (!open) return null;
return createPortal(
<div
className="modal-overlay fixed inset-0 z-50 flex items-center justify-center p-4"
onClick={handleClose}
>
{/* Backdrop */}
<div className="absolute inset-0 bg-black/70 backdrop-blur-sm" />
{/* Modal */}
<div
className="modal-content relative w-full max-w-md rounded-2xl border border-white/[0.08] bg-[#0a0a0a] p-6 sm:p-8 shadow-2xl"
onClick={(e) => e.stopPropagation()}
>
{/* Close button */}
<button
onClick={handleClose}
className="absolute right-4 top-4 flex h-8 w-8 items-center justify-center rounded-full text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-white cursor-pointer"
>
<X size={18} />
</button>
{submitted ? (
/* Success state */
<div className="py-4 text-center">
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-emerald-500/10">
<CheckCircle size={28} className="text-emerald-500" />
</div>
<h3 className="text-lg font-bold text-white">Отлично!</h3>
<p className="mt-2 text-sm text-neutral-400">
Сообщение отправлено в Instagram. Мы свяжемся с вами в ближайшее время!
</p>
<button
onClick={handleClose}
className="mt-6 rounded-full bg-gold px-6 py-2.5 text-sm font-semibold text-black transition-all hover:bg-gold-light cursor-pointer"
>
Закрыть
</button>
</div>
) : (
<>
{/* Header */}
<div className="mb-6">
<h3 className="text-xl font-bold text-white">Записаться</h3>
<p className="mt-1 text-sm text-neutral-400">
Оставьте данные и мы свяжемся с вами, или напишите нам напрямую
</p>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="space-y-3">
<div>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Ваше имя"
required
className="w-full rounded-xl border border-white/[0.08] bg-white/[0.04] px-4 py-3 text-sm text-white placeholder-neutral-500 outline-none transition-colors focus:border-gold/40 focus:bg-white/[0.06]"
/>
</div>
<div>
<input
type="tel"
value={phone}
onChange={(e) => handlePhoneChange(e.target.value)}
placeholder="+375 (__) ___-__-__"
required
className="w-full rounded-xl border border-white/[0.08] bg-white/[0.04] px-4 py-3 text-sm text-white placeholder-neutral-500 outline-none transition-colors focus:border-gold/40 focus:bg-white/[0.06]"
/>
</div>
<button
type="submit"
className="flex w-full items-center justify-center gap-2 rounded-xl bg-gold py-3 text-sm font-semibold text-black transition-all hover:bg-gold-light hover:shadow-lg hover:shadow-gold/20 cursor-pointer"
>
<Send size={15} />
Отправить в Instagram
</button>
</form>
{/* Divider */}
<div className="my-5 flex items-center gap-3">
<span className="h-px flex-1 bg-white/[0.06]" />
<span className="text-xs text-neutral-500">или напрямую</span>
<span className="h-px flex-1 bg-white/[0.06]" />
</div>
{/* Direct links */}
<div className="flex gap-2">
<a
href={contact.instagram}
target="_blank"
rel="noopener noreferrer"
className="flex flex-1 items-center justify-center gap-2 rounded-xl border border-white/[0.08] bg-white/[0.03] py-3 text-sm font-medium text-neutral-300 transition-all hover:border-gold/30 hover:text-gold-light cursor-pointer"
>
<Instagram size={16} />
Instagram
</a>
<a
href={`tel:${contact.phone.replace(/\s/g, "")}`}
className="flex flex-1 items-center justify-center gap-2 rounded-xl border border-white/[0.08] bg-white/[0.03] py-3 text-sm font-medium text-neutral-300 transition-all hover:border-gold/30 hover:text-gold-light cursor-pointer"
>
<Phone size={16} />
Позвонить
</a>
</div>
</>
)}
</div>
</div>,
document.body
);
}