Вёрстка страницы 'Яхта'
This commit is contained in:
parent
81d293434c
commit
d5e466b879
|
|
@ -0,0 +1,99 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { DatePicker } from "@/components/ui/date-picker";
|
||||||
|
import { GuestPicker } from "@/components/form/guest-picker";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
|
|
||||||
|
interface BookingWidgetProps {
|
||||||
|
price: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BookingWidget({ price }: BookingWidgetProps) {
|
||||||
|
const [departureDate, setDepartureDate] = useState<Date | undefined>();
|
||||||
|
const [arrivalDate, setArrivalDate] = useState<Date | undefined>();
|
||||||
|
const [guests, setGuests] = useState({ adults: 1, children: 0 });
|
||||||
|
const [total] = useState(0);
|
||||||
|
|
||||||
|
const handleGuestsChange = (adults: number, children: number) => {
|
||||||
|
setGuests({ adults, children });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBook = () => {
|
||||||
|
// Логика бронирования
|
||||||
|
console.log("Booking:", {
|
||||||
|
departureDate,
|
||||||
|
arrivalDate,
|
||||||
|
guests,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white border border-gray-200 rounded-lg p-6 sticky top-24">
|
||||||
|
<div className="mb-6">
|
||||||
|
<p className="text-2xl font-bold text-[#333333] mb-2">
|
||||||
|
от {price} р/час
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4 mb-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-[#333333] mb-2">
|
||||||
|
Выход
|
||||||
|
</label>
|
||||||
|
<DatePicker
|
||||||
|
variant="small"
|
||||||
|
placeholder="Выберите дату и время"
|
||||||
|
showIcon={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-[#333333] mb-2">
|
||||||
|
Заход
|
||||||
|
</label>
|
||||||
|
<DatePicker
|
||||||
|
variant="small"
|
||||||
|
placeholder="Выберите дату и время"
|
||||||
|
showIcon={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-[#333333] mb-2">
|
||||||
|
Гостей
|
||||||
|
</label>
|
||||||
|
<GuestPicker
|
||||||
|
adults={guests.adults}
|
||||||
|
childrenCount={guests.children}
|
||||||
|
onChange={handleGuestsChange}
|
||||||
|
variant="small"
|
||||||
|
showIcon={false}
|
||||||
|
placeholder="1 гость"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={handleBook}
|
||||||
|
variant="gradient"
|
||||||
|
className="w-full h-12 font-bold text-white mb-4"
|
||||||
|
>
|
||||||
|
Забронировать
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div className="pt-4 border-t border-gray-200">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-base font-semibold text-[#333333]">
|
||||||
|
Итого:
|
||||||
|
</span>
|
||||||
|
<span className="text-xl font-bold text-[#333333]">
|
||||||
|
{total} Р
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
interface ContactInfoProps {
|
||||||
|
contactPerson: {
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
};
|
||||||
|
requisites: {
|
||||||
|
ip: string;
|
||||||
|
inn: string;
|
||||||
|
ogrn: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ContactInfo({
|
||||||
|
contactPerson,
|
||||||
|
requisites,
|
||||||
|
}: ContactInfoProps) {
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="flex flex-col sm:flex-row gap-6">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="relative w-16 h-16 rounded-full overflow-hidden bg-gray-200 flex items-center justify-center">
|
||||||
|
<Image
|
||||||
|
src={contactPerson.avatar}
|
||||||
|
alt={contactPerson.name}
|
||||||
|
width={64}
|
||||||
|
height={64}
|
||||||
|
className="object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-base font-semibold text-[#333333] mb-1">
|
||||||
|
Контактное лицо
|
||||||
|
</h3>
|
||||||
|
<p className="text-base text-[#333333]">
|
||||||
|
{contactPerson.name}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="text-base font-semibold text-[#333333] mb-3">
|
||||||
|
Реквизиты
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex flex-col sm:flex-row sm:justify-between gap-1">
|
||||||
|
<span className="text-sm text-[#999999]">ИП:</span>
|
||||||
|
<span className="text-sm text-[#333333]">
|
||||||
|
{requisites.ip}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col sm:flex-row sm:justify-between gap-1">
|
||||||
|
<span className="text-sm text-[#999999]">ИНН:</span>
|
||||||
|
<span className="text-sm text-[#333333]">
|
||||||
|
{requisites.inn}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col sm:flex-row sm:justify-between gap-1">
|
||||||
|
<span className="text-sm text-[#999999]">
|
||||||
|
ОГРН/ОГРНИП:
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-[#333333]">
|
||||||
|
{requisites.ogrn}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { ru } from "date-fns/locale";
|
||||||
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
|
|
||||||
|
interface YachtAvailabilityProps {
|
||||||
|
price: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function YachtAvailability({ price }: YachtAvailabilityProps) {
|
||||||
|
const [currentMonth, setCurrentMonth] = useState(new Date(2025, 3, 1)); // Апрель 2025
|
||||||
|
|
||||||
|
// Генерируем доступные даты (27, 28, 29 апреля доступны)
|
||||||
|
const availableDates = [
|
||||||
|
new Date(2025, 3, 27),
|
||||||
|
new Date(2025, 3, 28),
|
||||||
|
new Date(2025, 3, 29),
|
||||||
|
];
|
||||||
|
|
||||||
|
const unavailableDates = Array.from({ length: 26 }, (_, i) => {
|
||||||
|
return new Date(2025, 3, i + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
const isDateAvailable = (date: Date) => {
|
||||||
|
return availableDates.some(
|
||||||
|
(d) =>
|
||||||
|
d.getDate() === date.getDate() &&
|
||||||
|
d.getMonth() === date.getMonth() &&
|
||||||
|
d.getFullYear() === date.getFullYear()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDateUnavailable = (date: Date) => {
|
||||||
|
return unavailableDates.some(
|
||||||
|
(d) =>
|
||||||
|
d.getDate() === date.getDate() &&
|
||||||
|
d.getMonth() === date.getMonth() &&
|
||||||
|
d.getFullYear() === date.getFullYear()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePreviousMonth = () => {
|
||||||
|
setCurrentMonth(
|
||||||
|
new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNextMonth = () => {
|
||||||
|
setCurrentMonth(
|
||||||
|
new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h2 className="text-xl font-bold text-[#333333]">
|
||||||
|
Доступность яхты
|
||||||
|
</h2>
|
||||||
|
<div className="flex items-center gap-2 text-sm text-[#999999]">
|
||||||
|
<Icon name="calendar" size={16} />
|
||||||
|
<span>По местному времени яхты</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white border border-gray-200 rounded-lg p-4">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<button
|
||||||
|
onClick={handlePreviousMonth}
|
||||||
|
className="p-2 hover:bg-gray-100 rounded-md transition-colors"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="w-5 h-5 text-gray-600" />
|
||||||
|
</button>
|
||||||
|
<h3 className="text-lg font-semibold text-[#333333] capitalize">
|
||||||
|
{format(currentMonth, "LLLL yyyy", { locale: ru })}
|
||||||
|
</h3>
|
||||||
|
<button
|
||||||
|
onClick={handleNextMonth}
|
||||||
|
className="p-2 hover:bg-gray-100 rounded-md transition-colors"
|
||||||
|
>
|
||||||
|
<ChevronRight className="w-5 h-5 text-gray-600" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
month={currentMonth}
|
||||||
|
onMonthChange={setCurrentMonth}
|
||||||
|
className="w-full"
|
||||||
|
locale={ru}
|
||||||
|
classNames={{
|
||||||
|
root: "w-full",
|
||||||
|
month: "flex w-full flex-col gap-4",
|
||||||
|
month_caption:
|
||||||
|
"flex h-8 w-full items-center justify-center px-8 text-gray-700 font-semibold",
|
||||||
|
table: "w-full border-collapse",
|
||||||
|
weekdays: "flex",
|
||||||
|
weekday:
|
||||||
|
"flex-1 text-gray-500 text-xs font-normal p-2 text-center",
|
||||||
|
week: "mt-2 flex w-full",
|
||||||
|
day: "relative",
|
||||||
|
}}
|
||||||
|
components={{
|
||||||
|
DayButton: ({ day, ...props }) => {
|
||||||
|
const isAvailable = isDateAvailable(day.date);
|
||||||
|
const isUnavailable = isDateUnavailable(day.date);
|
||||||
|
const isDay30 = day.date.getDate() === 30;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
{...props}
|
||||||
|
className={`relative w-full h-16 flex flex-col items-center justify-center rounded-md transition-colors ${
|
||||||
|
isAvailable
|
||||||
|
? "bg-[#008299] text-white hover:bg-[#008299]"
|
||||||
|
: isUnavailable || isDay30
|
||||||
|
? "text-gray-400 cursor-not-allowed bg-gray-100"
|
||||||
|
: "hover:bg-gray-100"
|
||||||
|
}`}
|
||||||
|
disabled={isUnavailable || isDay30}
|
||||||
|
>
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{day.date.getDate()}
|
||||||
|
</span>
|
||||||
|
{isAvailable && (
|
||||||
|
<>
|
||||||
|
<span className="text-[10px] mt-1 text-center leading-tight">
|
||||||
|
Доступно: 08:00-20:00
|
||||||
|
</span>
|
||||||
|
<span className="text-[10px] mt-0.5 text-center leading-tight">
|
||||||
|
{price} р/час
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{(isUnavailable || isDay30) && (
|
||||||
|
<span className="text-lg mt-1">✕</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
interface YachtCharacteristicsProps {
|
||||||
|
yacht: {
|
||||||
|
year: number;
|
||||||
|
maxCapacity: number;
|
||||||
|
comfortableCapacity: number;
|
||||||
|
length: number;
|
||||||
|
width: number;
|
||||||
|
cabins: number;
|
||||||
|
material: string;
|
||||||
|
power: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function YachtCharacteristics({ yacht }: YachtCharacteristicsProps) {
|
||||||
|
const characteristics = [
|
||||||
|
{ label: "Год", value: yacht.year },
|
||||||
|
{
|
||||||
|
label: "Максимальная вместимость",
|
||||||
|
value: `${yacht.maxCapacity} человек`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Комфортная вместимость",
|
||||||
|
value: `${yacht.comfortableCapacity} человек`,
|
||||||
|
},
|
||||||
|
{ label: "Длина", value: `${yacht.length} м` },
|
||||||
|
{ label: "Ширина", value: `${yacht.width} м` },
|
||||||
|
{ label: "Каюты", value: yacht.cabins },
|
||||||
|
{ label: "Материал", value: yacht.material },
|
||||||
|
{ label: "Мощность", value: `${yacht.power} л/с` },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-bold text-[#333333] mb-4">
|
||||||
|
Характеристики
|
||||||
|
</h2>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
|
{characteristics.map((char, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="flex justify-between items-center py-2 border-b border-gray-200"
|
||||||
|
>
|
||||||
|
<span className="text-base text-[#999999]">
|
||||||
|
{char.label}:
|
||||||
|
</span>
|
||||||
|
<span className="text-base font-medium text-[#333333]">
|
||||||
|
{char.value}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
type CarouselApi,
|
||||||
|
} from "@/components/ui/carousel";
|
||||||
|
|
||||||
|
interface YachtGalleryProps {
|
||||||
|
images: string[];
|
||||||
|
badge?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function YachtGallery({ images, badge }: YachtGalleryProps) {
|
||||||
|
const [api, setApi] = useState<CarouselApi>();
|
||||||
|
const [current, setCurrent] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrent(api.selectedScrollSnap());
|
||||||
|
|
||||||
|
api.on("select", () => {
|
||||||
|
setCurrent(api.selectedScrollSnap());
|
||||||
|
});
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
const scrollTo = (index: number) => {
|
||||||
|
api?.scrollTo(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Main Image Carousel */}
|
||||||
|
<div className="relative">
|
||||||
|
<Carousel
|
||||||
|
setApi={setApi}
|
||||||
|
opts={{
|
||||||
|
align: "start",
|
||||||
|
loop: false,
|
||||||
|
}}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<CarouselContent>
|
||||||
|
{images.map((img, index) => (
|
||||||
|
<CarouselItem key={index}>
|
||||||
|
<div className="relative w-full h-[500px] rounded-lg overflow-hidden">
|
||||||
|
<Image
|
||||||
|
src={img}
|
||||||
|
alt={`Yacht image ${index + 1}`}
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
priority={index === 0}
|
||||||
|
/>
|
||||||
|
{badge && (
|
||||||
|
<div className="absolute top-4 left-4 z-10">
|
||||||
|
<div className="flex items-center justify-center bg-black/40 text-white px-3 py-1 rounded-lg text-sm gap-1">
|
||||||
|
<Icon size={16} name="restart" />
|
||||||
|
<span>{badge}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="absolute bottom-4 right-4 bg-black/40 text-white px-3 py-1 rounded-lg text-sm">
|
||||||
|
{index + 1}/{images.length}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious className="left-4 bg-white/80 hover:bg-white border-gray-300" />
|
||||||
|
<CarouselNext className="right-4 bg-white/80 hover:bg-white border-gray-300" />
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Thumbnails */}
|
||||||
|
<div className="flex gap-2 overflow-x-auto pb-2">
|
||||||
|
{images.map((img, index) => (
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
onClick={() => scrollTo(index)}
|
||||||
|
className={`relative flex-shrink-0 w-24 h-24 rounded-lg overflow-hidden border-2 transition-all ${
|
||||||
|
current === index
|
||||||
|
? "border-[#008299] opacity-100"
|
||||||
|
: "border-transparent opacity-60 hover:opacity-100"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={img}
|
||||||
|
alt={`Thumbnail ${index + 1}`}
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useParams } from "next/navigation";
|
||||||
|
import Link from "next/link";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Icon from "@/components/ui/icon";
|
||||||
|
import { YachtGallery } from "./components/YachtGallery";
|
||||||
|
import { YachtAvailability } from "./components/YachtAvailability";
|
||||||
|
import { BookingWidget } from "./components/BookingWidget";
|
||||||
|
import { YachtCharacteristics } from "./components/YachtCharacteristics";
|
||||||
|
import { ContactInfo } from "./components/ContactInfo";
|
||||||
|
|
||||||
|
export default function YachtDetailPage() {
|
||||||
|
const params = useParams();
|
||||||
|
const id = params.id as string;
|
||||||
|
|
||||||
|
// Данные яхты (в реальном приложении будут загружаться из API)
|
||||||
|
const yacht = {
|
||||||
|
id: id,
|
||||||
|
name: "Яхта Название",
|
||||||
|
location: "7 Футов",
|
||||||
|
price: "18 000",
|
||||||
|
images: [
|
||||||
|
"/images/yachts/yacht1.jpg",
|
||||||
|
"/images/yachts/yacht2.jpg",
|
||||||
|
"/images/yachts/yacht3.jpg",
|
||||||
|
"/images/yachts/yacht4.jpg",
|
||||||
|
"/images/yachts/yacht5.jpg",
|
||||||
|
"/images/yachts/yacht6.jpg",
|
||||||
|
],
|
||||||
|
badge: "По запросу",
|
||||||
|
year: 2000,
|
||||||
|
maxCapacity: 11,
|
||||||
|
comfortableCapacity: 11,
|
||||||
|
length: 13,
|
||||||
|
width: 4,
|
||||||
|
cabins: 2,
|
||||||
|
material: "Стеклопластик",
|
||||||
|
power: 740,
|
||||||
|
description: "Яхта",
|
||||||
|
contactPerson: {
|
||||||
|
name: "Денис",
|
||||||
|
avatar: "/images/logo.svg",
|
||||||
|
},
|
||||||
|
requisites: {
|
||||||
|
ip: "Иванов Иван Иванович",
|
||||||
|
inn: "23000000000",
|
||||||
|
ogrn: "310000000000001",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="bg-[#f4f4f4] min-h-screen">
|
||||||
|
<div className="container max-w-6xl mx-auto px-4 py-6">
|
||||||
|
{/* Breadcrumbs */}
|
||||||
|
<div className="mb-6 text-sm text-[#999999] flex items-center gap-4">
|
||||||
|
<Link href="/">
|
||||||
|
<span className="cursor-pointer hover:text-[#333333] transition-colors">
|
||||||
|
Аренда яхты
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
<span>></span>
|
||||||
|
<Link href="/catalog">
|
||||||
|
<span className="cursor-pointer hover:text-[#333333] transition-colors">
|
||||||
|
Моторные яхты
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
<span>></span>
|
||||||
|
<span className="text-[#333333]">{yacht.name}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Content Container */}
|
||||||
|
<div className="bg-white rounded-[16px] p-6">
|
||||||
|
{/* Yacht Title and Actions */}
|
||||||
|
<div className="mb-6 flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||||
|
<h1 className="text-2xl md:text-3xl font-bold text-[#333333]">
|
||||||
|
{yacht.name}
|
||||||
|
</h1>
|
||||||
|
<div className="flex items-center gap-6">
|
||||||
|
<div className="flex items-center gap-2 text-[#333333]">
|
||||||
|
<Icon name="map" size={20} />
|
||||||
|
<span className="text-base">{yacht.location}</span>
|
||||||
|
</div>
|
||||||
|
<button className="flex items-center gap-2 text-[#333333] hover:text-[#008299] transition-colors">
|
||||||
|
<Icon name="ad" size={20} />
|
||||||
|
<span className="text-base">Поделиться</span>
|
||||||
|
</button>
|
||||||
|
<button className="flex items-center gap-2 text-[#333333] hover:text-[#008299] transition-colors">
|
||||||
|
<Icon name="like" size={20} />
|
||||||
|
<span className="text-base">Избранное</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Content Grid */}
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
|
{/* Left Column - Gallery and Availability */}
|
||||||
|
<div className="lg:col-span-2 space-y-6">
|
||||||
|
{/* Gallery */}
|
||||||
|
<YachtGallery
|
||||||
|
images={yacht.images}
|
||||||
|
badge={yacht.badge}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Availability */}
|
||||||
|
<YachtAvailability price={yacht.price} />
|
||||||
|
|
||||||
|
{/* Characteristics */}
|
||||||
|
<YachtCharacteristics yacht={yacht} />
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-bold text-[#333333] mb-4">
|
||||||
|
Описание
|
||||||
|
</h2>
|
||||||
|
<p className="text-base text-[#333333] leading-relaxed">
|
||||||
|
{yacht.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact and Requisites */}
|
||||||
|
<ContactInfo
|
||||||
|
contactPerson={yacht.contactPerson}
|
||||||
|
requisites={yacht.requisites}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Reviews */}
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-bold text-[#333333] mb-4">
|
||||||
|
Отзывы
|
||||||
|
</h2>
|
||||||
|
<p className="text-base text-[#999999]">
|
||||||
|
У этой яхты пока нет отзывов
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right Column - Booking Widget */}
|
||||||
|
<div className="lg:col-span-1">
|
||||||
|
<BookingWidget price={yacht.price} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -48,11 +48,10 @@ export const GuestPicker: React.FC<GuestPickerProps> = ({
|
||||||
const getDisplayText = () => {
|
const getDisplayText = () => {
|
||||||
const total = adults + childrenCount;
|
const total = adults + childrenCount;
|
||||||
if (total === 0) return placeholder;
|
if (total === 0) return placeholder;
|
||||||
|
if (total === 1) return "1 гость";
|
||||||
if (childrenCount === 0)
|
if (childrenCount === 0)
|
||||||
return `${adults} ${adults === 1 ? "взрослый" : "взрослых"}`;
|
return `${adults} ${adults === 1 ? "гость" : "гостей"}`;
|
||||||
return `${adults} ${
|
return `${total} ${total === 1 ? "гость" : "гостей"}`;
|
||||||
adults === 1 ? "взрослый" : "взрослых"
|
|
||||||
}, ${childrenCount} ${childrenCount === 1 ? "ребенок" : "детей"}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue