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>
This commit is contained in:
2026-03-15 16:45:27 +03:00
parent 4f92057411
commit 6981376171
4 changed files with 37 additions and 3 deletions

View File

@@ -1,6 +1,7 @@
"use client";
import { useState, useMemo, useCallback } from "react";
import { BookingModal } from "@/components/ui/BookingModal";
import { CalendarDays, Users, LayoutGrid } from "lucide-react";
import { SectionHeading } from "@/components/ui/SectionHeading";
import { Reveal } from "@/components/ui/Reveal";
@@ -28,6 +29,7 @@ export function Schedule({ data: schedule, classItems }: ScheduleProps) {
const [filterStatus, setFilterStatus] = useState<StatusFilter>("all");
const [filterTime, setFilterTime] = useState<TimeFilter>("all");
const [filterDaySet, setFilterDaySet] = useState<Set<string>>(new Set());
const [bookingGroup, setBookingGroup] = useState<string | null>(null);
const isAllMode = locationMode === "all";
@@ -329,9 +331,15 @@ export function Schedule({ data: schedule, classItems }: ScheduleProps) {
filterTrainer={filterTrainer}
setFilterTrainer={setFilterTrainerFromCard}
showLocation={isAllMode}
onBook={setBookingGroup}
/>
</Reveal>
)}
<BookingModal
open={bookingGroup !== null}
onClose={() => setBookingGroup(null)}
groupInfo={bookingGroup ?? undefined}
/>
</section>
);
}

View File

@@ -55,6 +55,7 @@ interface GroupViewProps {
filterTrainer: string | null;
setFilterTrainer: (trainer: string | null) => void;
showLocation?: boolean;
onBook?: (groupInfo: string) => void;
}
export function GroupView({
@@ -65,6 +66,7 @@ export function GroupView({
filterTrainer,
setFilterTrainer,
showLocation,
onBook,
}: GroupViewProps) {
const groups = buildGroups(filteredDays);
@@ -177,6 +179,14 @@ export function GroupView({
</div>
))}
</div>
{onBook && (
<button
onClick={() => onBook(`${group.type}, ${group.trainer}, ${group.slots.map(s => s.dayShort).join("/")} ${group.slots[0]?.time ?? ""}`)}
className="w-full mt-3 rounded-xl bg-gold/15 border border-gold/25 py-2 text-xs font-semibold text-gold-dark dark:text-gold hover:bg-gold/25 transition-colors cursor-pointer"
>
Записаться
</button>
)}
</div>
</div>
);

View File

@@ -2,6 +2,7 @@ import { useState, useEffect, useRef, useCallback } from "react";
import Image from "next/image";
import { ArrowLeft, Instagram, Trophy, GraduationCap, ExternalLink, X, Award, Scale, Clock, MapPin } from "lucide-react";
import type { TeamMember, RichListItem, VictoryItem, ScheduleLocation } from "@/types/content";
import { BookingModal } from "@/components/ui/BookingModal";
interface TeamProfileProps {
member: TeamMember;
@@ -11,6 +12,7 @@ interface TeamProfileProps {
export function TeamProfile({ member, onBack, schedule }: TeamProfileProps) {
const [lightbox, setLightbox] = useState<string | null>(null);
const [bookingGroup, setBookingGroup] = useState<string | null>(null);
useEffect(() => {
function handleKeyDown(e: KeyboardEvent) {
@@ -177,7 +179,7 @@ export function TeamProfile({ member, onBack, schedule }: TeamProfileProps) {
</span>
<ScrollRow>
{uniqueGroups.map((g, i) => (
<div key={i} className="w-44 shrink-0 rounded-xl border border-white/[0.08] bg-white/[0.03] p-3 space-y-1.5">
<div key={i} className="w-48 shrink-0 rounded-xl border border-white/[0.08] bg-white/[0.03] p-3 space-y-1.5">
<p className="text-xs font-semibold uppercase tracking-wider text-white/80">{g.type}</p>
<div className="flex items-center gap-1.5 text-xs text-white/50">
<Clock size={11} />
@@ -195,6 +197,12 @@ export function TeamProfile({ member, onBack, schedule }: TeamProfileProps) {
Набор открыт
</span>
)}
<button
onClick={() => setBookingGroup(`${g.type}, ${g.days.join("/")} ${g.time}`)}
className="w-full mt-1 rounded-lg bg-gold/15 border border-gold/25 py-1.5 text-[11px] font-semibold text-gold hover:bg-gold/25 transition-colors cursor-pointer"
>
Записаться
</button>
</div>
))}
</ScrollRow>
@@ -274,6 +282,12 @@ export function TeamProfile({ member, onBack, schedule }: TeamProfileProps) {
</div>
</div>
)}
<BookingModal
open={bookingGroup !== null}
onClose={() => setBookingGroup(null)}
groupInfo={bookingGroup ?? undefined}
/>
</div>
);
}

View File

@@ -8,9 +8,10 @@ import { siteContent } from "@/data/content";
interface BookingModalProps {
open: boolean;
onClose: () => void;
groupInfo?: string;
}
export function BookingModal({ open, onClose }: BookingModalProps) {
export function BookingModal({ open, onClose, groupInfo }: BookingModalProps) {
const { contact } = siteContent;
const [name, setName] = useState("");
const [phone, setPhone] = useState("+375 ");
@@ -65,7 +66,8 @@ export function BookingModal({ open, onClose }: BookingModalProps) {
(e: React.FormEvent) => {
e.preventDefault();
// Build Instagram DM message with pre-filled text
const message = `Здравствуйте! Меня зовут ${name}, хочу записаться на занятие. Мой телефон: ${phone}`;
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);