Яхта, доработки
This commit is contained in:
parent
d5e466b879
commit
ec00216b93
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
|
|
@ -0,0 +1,11 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_0_6016" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="32">
|
||||
<path d="M32 0H0V32H32V0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_0_6016)">
|
||||
<path d="M20.3996 6.30371C18.7042 6.30371 17.1601 6.95075 16 8.01102C14.8399 6.95075 13.2958 6.30371 11.6005 6.30371C7.99768 6.30371 5.07715 9.22424 5.07715 12.827C5.07715 14.9028 5.79843 16.6222 7.16376 18.2532C8.5291 19.8841 15.9958 25.697 15.9958 25.697C15.9958 25.697 23.5087 19.9101 24.8438 18.2145C26.1274 16.5842 26.9229 14.9153 26.9229 12.827C26.9229 9.22424 24.0024 6.30371 20.3996 6.30371Z" fill="white"/>
|
||||
<path d="M20.3996 6.30371C18.7042 6.30371 17.1601 6.95075 16 8.01102C14.8399 6.95075 13.2958 6.30371 11.6005 6.30371C7.99768 6.30371 5.07715 9.22424 5.07715 12.827C5.07715 14.9028 5.79843 16.6222 7.16376 18.2532C8.5291 19.8841 15.9958 25.697 15.9958 25.697C15.9958 25.697 23.5087 19.9101 24.8438 18.2145C26.1274 16.5842 26.9229 14.9153 26.9229 12.827C26.9229 9.22424 24.0024 6.30371 20.3996 6.30371Z" stroke="white" stroke-width="5.12"/>
|
||||
<path d="M20.3996 6.30371C18.7042 6.30371 17.1601 6.95075 16 8.01102C14.8399 6.95075 13.2958 6.30371 11.6005 6.30371C7.99768 6.30371 5.07715 9.22424 5.07715 12.827C5.07715 14.9028 5.79843 16.6222 7.16376 18.2532C8.5291 19.8841 15.9958 25.697 15.9958 25.697C15.9958 25.697 23.5087 19.9101 24.8438 18.2145C26.1274 16.5842 26.9229 14.9153 26.9229 12.827C26.9229 9.22424 24.0024 6.30371 20.3996 6.30371Z" fill="white"/>
|
||||
<path d="M20.3996 6.30371C18.7042 6.30371 17.1601 6.95075 16 8.01102C14.8399 6.95075 13.2958 6.30371 11.6005 6.30371C7.99768 6.30371 5.07715 9.22424 5.07715 12.827C5.07715 14.9028 5.79843 16.6222 7.16376 18.2532C8.5291 19.8841 15.9958 25.697 15.9958 25.697C15.9958 25.697 23.5087 19.9101 24.8438 18.2145C26.1274 16.5842 26.9229 14.9153 26.9229 12.827C26.9229 9.22424 24.0024 6.30371 20.3996 6.30371Z" stroke="#0072A8" stroke-width="1.70667"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16 4C14.7743 3.99987 13.5606 4.24118 12.4281 4.71015C11.2957 5.17911 10.2667 5.86656 9.39994 6.73322C8.53319 7.59988 7.84563 8.62879 7.37654 9.76118C6.90745 10.8936 6.66602 12.1073 6.66602 13.333C6.66602 18.667 12.666 24 15.999 27.333C19.333 24 25.333 18.488 25.333 13.333C25.333 12.1073 25.0916 10.8936 24.6225 9.76118C24.1534 8.62879 23.4658 7.59988 22.5991 6.73322C21.7323 5.86656 20.7034 5.17911 19.5709 4.71015C18.4385 4.24118 17.2257 3.99987 16 4Z" stroke="#0072A8" stroke-width="2"/>
|
||||
<path d="M16.001 16C17.4739 16 18.668 14.806 18.668 13.333C18.668 11.8601 17.4739 10.666 16.001 10.666C14.528 10.666 13.334 11.8601 13.334 13.333C13.334 14.806 14.528 16 16.001 16Z" fill="#0072A8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 803 B |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_0_5837)">
|
||||
<path d="M8.00005 0L9.88096 5.41115L15.6085 5.52787L11.0434 8.98885L12.7023 14.4721L8.00005 11.2L3.29777 14.4721L4.95667 8.98885L0.391602 5.52787L6.11915 5.41115L8.00005 0Z" fill="#FFAA2B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_0_5837">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 434 B |
|
|
@ -0,0 +1,6 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.5 11C22.8807 11 24 9.88071 24 8.5C24 7.11929 22.8807 6 21.5 6C20.1193 6 19 7.11929 19 8.5C19 9.88071 20.1193 11 21.5 11Z" stroke="#0072A8" stroke-width="2"/>
|
||||
<path d="M10.5 19C9.11929 19 8 17.8807 8 16.5C8 15.1193 9.11929 14 10.5 14C11.8807 14 13 15.1193 13 16.5C13 17.8807 11.8807 19 10.5 19Z" stroke="#0072A8" stroke-width="2"/>
|
||||
<path d="M21.5 26C22.8807 26 24 24.8807 24 23.5C24 22.1193 22.8807 21 21.5 21C20.1193 21 19 22.1193 19 23.5C19 24.8807 20.1193 26 21.5 26Z" stroke="#0072A8" stroke-width="2"/>
|
||||
<path d="M12.5 15L20 10M20 23L12.5 18" stroke="#0072A8" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 698 B |
|
|
@ -4,15 +4,14 @@ 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 [departureDate] = useState<Date | undefined>();
|
||||
const [arrivalDate] = useState<Date | undefined>();
|
||||
const [guests, setGuests] = useState({ adults: 1, children: 0 });
|
||||
const [total] = useState(0);
|
||||
|
||||
|
|
@ -30,10 +29,13 @@ export function BookingWidget({ price }: BookingWidgetProps) {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white border border-gray-200 rounded-lg p-6 sticky top-24">
|
||||
<div className="bg-white border border-gray-200 rounded-lg p-6">
|
||||
<div className="mb-6">
|
||||
<p className="text-2xl font-bold text-[#333333] mb-2">
|
||||
от {price} р/час
|
||||
от {price} ₽{" "}
|
||||
<span className="text-base font-normal text-[#999999]">
|
||||
/час
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -85,15 +87,12 @@ export function BookingWidget({ price }: BookingWidgetProps) {
|
|||
|
||||
<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 className="text-base text-[#333333]">Итого:</span>
|
||||
<span className="text-base font-bold text-[#333333]">
|
||||
{total} ₽
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,64 +14,57 @@ interface ContactInfoProps {
|
|||
};
|
||||
}
|
||||
|
||||
export function ContactInfo({
|
||||
contactPerson,
|
||||
requisites,
|
||||
}: ContactInfoProps) {
|
||||
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">
|
||||
<div className="flex flex-col sm:flex-row gap-5">
|
||||
<div className="flex-1 rounded-[24px] px-6 py-5 bg-[#f4f4f4]">
|
||||
<div className="flex items-center gap-4 h-full">
|
||||
<div className="relative rounded-full overflow-hidden bg-gray-200 flex items-center justify-center">
|
||||
<Image
|
||||
src={contactPerson.avatar}
|
||||
src="/images/avatar.png"
|
||||
alt={contactPerson.name}
|
||||
width={64}
|
||||
height={64}
|
||||
className="object-cover"
|
||||
width={124}
|
||||
height={124}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-[#333333] mb-1">
|
||||
Контактное лицо
|
||||
<div className="flex flex-col justify-between h-full">
|
||||
<h3 className="text-base font-bold text-[#333333]">
|
||||
{contactPerson.name}
|
||||
</h3>
|
||||
<p className="text-base text-[#333333]">
|
||||
{contactPerson.name}
|
||||
Контактное лицо
|
||||
</p>
|
||||
</div>
|
||||
</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 className="flex-1 rounded-[24px] px-6 py-5 bg-[#f4f4f4]">
|
||||
<h3 className="text-base font-bold 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-base text-[#333333]">ИП</span>
|
||||
<span className="text-base text-[#999999]">
|
||||
{requisites.ip}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row sm:justify-between gap-1">
|
||||
<span className="text-base text-[#333333]">ИНН</span>
|
||||
<span className="text-base text-[#999999]">
|
||||
{requisites.inn}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row sm:justify-between gap-1">
|
||||
<span className="text-base text-[#333333]">
|
||||
ОГРН/ОГРНИП
|
||||
</span>
|
||||
<span className="text-base text-[#999999]">
|
||||
{requisites.ogrn}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,38 +2,24 @@
|
|||
|
||||
import { useState } from "react";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { format } from "date-fns";
|
||||
import { isSameMonth, isBefore, startOfDay, format } from "date-fns";
|
||||
import { ru } from "date-fns/locale";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import Icon from "@/components/ui/icon";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||
|
||||
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 today = startOfDay(new Date());
|
||||
const [currentMonth, setCurrentMonth] = useState(
|
||||
new Date(today.getFullYear(), today.getMonth(), 1)
|
||||
);
|
||||
|
||||
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) =>
|
||||
|
|
@ -43,101 +29,135 @@ export function YachtAvailability({ price }: YachtAvailabilityProps) {
|
|||
);
|
||||
};
|
||||
|
||||
const handlePreviousMonth = () => {
|
||||
const isDateInPast = (date: Date) => {
|
||||
return isBefore(startOfDay(date), today);
|
||||
};
|
||||
|
||||
const shouldBeCrossedOut = (date: Date) => {
|
||||
// Перечеркиваем если день занят или находится до текущего дня
|
||||
return isDateUnavailable(date) || isDateInPast(date);
|
||||
};
|
||||
|
||||
const goToPreviousMonth = () => {
|
||||
setCurrentMonth(
|
||||
new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)
|
||||
new Date(
|
||||
currentMonth.getFullYear(),
|
||||
currentMonth.getMonth() - 1,
|
||||
1
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const handleNextMonth = () => {
|
||||
const goToNextMonth = () => {
|
||||
setCurrentMonth(
|
||||
new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)
|
||||
new Date(
|
||||
currentMonth.getFullYear(),
|
||||
currentMonth.getMonth() + 1,
|
||||
1
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4 w-full">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-xl font-bold text-[#333333]">
|
||||
<h2 className="text-base 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 className="bg-white w-full">
|
||||
<div className="w-full flex justify-end mb-8">
|
||||
<div className="flex items-center gap-5">
|
||||
<button
|
||||
onClick={goToPreviousMonth}
|
||||
className="cursor-pointer rounded-full border border-[#dfdfdf] h-12 w-12 flex items-center justify-center"
|
||||
>
|
||||
<ChevronLeftIcon className="size-4" />
|
||||
</button>
|
||||
<span className="text-2xl text-[#333333]">
|
||||
{format(currentMonth, "LLLL yyyy", { locale: ru })}
|
||||
</span>
|
||||
<button
|
||||
onClick={goToNextMonth}
|
||||
className="cursor-pointer rounded-full border border-[#dfdfdf] h-12 w-12 flex items-center justify-center"
|
||||
>
|
||||
<ChevronRightIcon className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Calendar
|
||||
mode="single"
|
||||
month={currentMonth}
|
||||
onMonthChange={setCurrentMonth}
|
||||
className="w-full"
|
||||
showOutsideDays={false}
|
||||
className="w-full p-0"
|
||||
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",
|
||||
nav: "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-2",
|
||||
month_caption: "hidden",
|
||||
caption_label: "text-2xl",
|
||||
button_previous: "hidden",
|
||||
button_next: "hidden",
|
||||
table: "w-full border-collapse",
|
||||
weekdays: "flex",
|
||||
weekdays: "hidden",
|
||||
weekday:
|
||||
"flex-1 text-gray-500 text-xs font-normal p-2 text-center",
|
||||
week: "mt-2 flex w-full",
|
||||
day: "relative",
|
||||
week: "flex w-full",
|
||||
day: "relative flex-1",
|
||||
}}
|
||||
components={{
|
||||
DayButton: ({ day, ...props }) => {
|
||||
const isAvailable = isDateAvailable(day.date);
|
||||
const isUnavailable = isDateUnavailable(day.date);
|
||||
const isDay30 = day.date.getDate() === 30;
|
||||
// Показываем только дни текущего месяца
|
||||
if (!isSameMonth(day.date, currentMonth)) {
|
||||
return <div className="hidden" />;
|
||||
}
|
||||
|
||||
const isCrossedOut = shouldBeCrossedOut(day.date);
|
||||
|
||||
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}
|
||||
className="relative w-full h-20 flex flex-col items-start justify-start px-2 py-[2px] border border-gray-200"
|
||||
disabled={isCrossedOut}
|
||||
>
|
||||
<span className="text-sm font-medium">
|
||||
{day.date.getDate()}
|
||||
</span>
|
||||
{isAvailable && (
|
||||
{isCrossedOut ? (
|
||||
// Перечеркнутая ячейка для недоступных дней
|
||||
<>
|
||||
<span className="text-[10px] mt-1 text-center leading-tight">
|
||||
Доступно: 08:00-20:00
|
||||
<span className="text-sm font-medium text-[#333333] self-end">
|
||||
{day.date.getDate()}
|
||||
</span>
|
||||
<span className="text-[10px] mt-0.5 text-center leading-tight">
|
||||
{price} р/час
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<span className="text-gray-300 text-4xl font-light leading-none">
|
||||
✕
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
// Доступный день с информацией
|
||||
<>
|
||||
{/* Дата и "Доступно:" в одной строке */}
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<span className="text-xs text-gray-400">
|
||||
Доступно:
|
||||
</span>
|
||||
<span className="text-sm font-medium text-[#333333]">
|
||||
{day.date.getDate()}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1.5 w-full mt-1">
|
||||
<div className="w-fit bg-[#F6BD4D] text-white text-[10px] font-medium px-1 py-0 rounded-full inline-block">
|
||||
08:00—20:00
|
||||
</div>
|
||||
</div>
|
||||
{/* Цена в нижнем правом углу */}
|
||||
<span className="absolute bottom-[2px] right-[4px] text-xs text-[#333333] font-medium">
|
||||
{price} / час
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{(isUnavailable || isDay30) && (
|
||||
<span className="text-lg mt-1">✕</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
|
|
@ -147,4 +167,3 @@ export function YachtAvailability({ price }: YachtAvailabilityProps) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,19 +33,19 @@ export function YachtCharacteristics({ yacht }: YachtCharacteristicsProps) {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-[#333333] mb-4">
|
||||
<h2 className="text-base font-bold text-[#333333] mb-4">
|
||||
Характеристики
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-x-6">
|
||||
{characteristics.map((char, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex justify-between items-center py-2 border-b border-gray-200"
|
||||
className="flex justify-between items-center py-4 border-b border-gray-200"
|
||||
>
|
||||
<span className="text-base text-[#999999]">
|
||||
{char.label}:
|
||||
{char.label}
|
||||
</span>
|
||||
<span className="text-base font-medium text-[#333333]">
|
||||
<span className="text-base font-regular text-[#333333]">
|
||||
{char.value}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -54,6 +54,3 @@ export function YachtCharacteristics({ yacht }: YachtCharacteristicsProps) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export function YachtGallery({ images, badge }: YachtGalleryProps) {
|
|||
<CarouselContent>
|
||||
{images.map((img, index) => (
|
||||
<CarouselItem key={index}>
|
||||
<div className="relative w-full h-[500px] rounded-lg overflow-hidden">
|
||||
<div className="relative w-full h-[592px] rounded-lg overflow-hidden">
|
||||
<Image
|
||||
src={img}
|
||||
alt={`Yacht image ${index + 1}`}
|
||||
|
|
@ -60,17 +60,6 @@ export function YachtGallery({ images, badge }: YachtGalleryProps) {
|
|||
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>
|
||||
))}
|
||||
|
|
@ -78,6 +67,21 @@ export function YachtGallery({ images, badge }: YachtGalleryProps) {
|
|||
<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>
|
||||
{/* Badge - поверх слайдера, не скроллится */}
|
||||
{badge && (
|
||||
<div className="absolute top-4 left-4 z-20 pointer-events-none">
|
||||
<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>
|
||||
)}
|
||||
{/* Photo counter - поверх слайдера, не скроллится */}
|
||||
<div className="absolute bottom-4 right-4 z-20 pointer-events-none">
|
||||
<div className="bg-black/40 text-white px-3 py-1 rounded-lg text-sm">
|
||||
{current + 1}/{images.length}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Thumbnails */}
|
||||
|
|
@ -86,7 +90,7 @@ export function YachtGallery({ images, badge }: YachtGalleryProps) {
|
|||
<button
|
||||
key={index}
|
||||
onClick={() => scrollTo(index)}
|
||||
className={`relative flex-shrink-0 w-24 h-24 rounded-lg overflow-hidden border-2 transition-all ${
|
||||
className={`relative flex-shrink-0 w-17 h-14 rounded-lg overflow-hidden border-2 transition-all ${
|
||||
current === index
|
||||
? "border-[#008299] opacity-100"
|
||||
: "border-transparent opacity-60 hover:opacity-100"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
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";
|
||||
|
|
@ -37,7 +36,11 @@ export default function YachtDetailPage() {
|
|||
cabins: 2,
|
||||
material: "Стеклопластик",
|
||||
power: 740,
|
||||
description: "Яхта",
|
||||
description: `Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта
|
||||
Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта
|
||||
Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта
|
||||
Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта
|
||||
Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта Яхта`,
|
||||
contactPerson: {
|
||||
name: "Денис",
|
||||
avatar: "/images/logo.svg",
|
||||
|
|
@ -73,73 +76,83 @@ export default function YachtDetailPage() {
|
|||
<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]">
|
||||
<h1 className="text-xl md:text-2 xl 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>
|
||||
<Icon name="pin" size={32} />
|
||||
<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} />
|
||||
<button className="flex items-center gap-2 cursor-pointer text-[#333333] hover:text-[#008299] transition-colors">
|
||||
<Icon name="share" size={32} />
|
||||
<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} />
|
||||
<button className="flex items-center gap-2 cursor-pointer text-[#333333] hover:text-[#008299] transition-colors">
|
||||
<Icon name="heart" size={32} />
|
||||
<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">
|
||||
{/* Main Content */}
|
||||
<div className="space-y-6">
|
||||
{/* Gallery */}
|
||||
<YachtGallery
|
||||
images={yacht.images}
|
||||
badge={yacht.badge}
|
||||
/>
|
||||
|
||||
{/* Availability */}
|
||||
<YachtAvailability price={yacht.price} />
|
||||
{/* Content with Booking Widget on the right */}
|
||||
<div className="flex flex-col lg:flex-row gap-6 items-start">
|
||||
{/* Left column - all content below gallery */}
|
||||
<div className="flex-1 space-y-6">
|
||||
{/* Availability */}
|
||||
<YachtAvailability price={yacht.price} />
|
||||
|
||||
{/* Characteristics */}
|
||||
<YachtCharacteristics yacht={yacht} />
|
||||
{/* 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>
|
||||
{/* Description */}
|
||||
<div>
|
||||
<h2 className="text-base font-bold text-[#333333] mb-4">
|
||||
Описание
|
||||
</h2>
|
||||
<p className="text-base text-[#666666] leading-relaxed">
|
||||
{yacht.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Contact and Requisites */}
|
||||
<ContactInfo
|
||||
contactPerson={yacht.contactPerson}
|
||||
requisites={yacht.requisites}
|
||||
/>
|
||||
{/* 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>
|
||||
{/* Reviews */}
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Icon name="reviewStar" size={16} />
|
||||
<h2 className="text-base font-bold text-[#333333]">
|
||||
Отзывы
|
||||
</h2>
|
||||
</div>
|
||||
<div className="border border-[#DFDFDF] rounded-[12px] flex items-center justify-center py-18">
|
||||
<p className="text-lg text-[#999999]">
|
||||
У этой яхты пока нет отзывов
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right column - Booking Widget (sticky) */}
|
||||
<div className="lg:w-74 flex-shrink-0 lg:sticky lg:top-24 self-start">
|
||||
<BookingWidget price={yacht.price} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Booking Widget */}
|
||||
<div className="lg:col-span-1">
|
||||
<BookingWidget price={yacht.price} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ import PriceMinIcon from "../../../public/images/icons/price-min.svg";
|
|||
import PriceMaxIcon from "../../../public/images/icons/price-max.svg";
|
||||
import BoatYearMinIcon from "../../../public/images/icons/boat-year-min.svg";
|
||||
import BoatYearMaxIcon from "../../../public/images/icons/boat-year-max.svg";
|
||||
import PinIcon from "../../../public/images/icons/pin.svg";
|
||||
import HeartIcon from "../../../public/images/icons/heart.svg";
|
||||
import ShareIcon from "../../../public/images/icons/share.svg";
|
||||
import ReviewStarIcon from "../../../public/images/icons/review-star.svg";
|
||||
|
||||
// Объект с иконками для удобного доступа
|
||||
const icons = {
|
||||
|
|
@ -45,6 +49,10 @@ const icons = {
|
|||
priceMax: PriceMaxIcon,
|
||||
boatYearMin: BoatYearMinIcon,
|
||||
boatYearMax: BoatYearMaxIcon,
|
||||
pin: PinIcon,
|
||||
heart: HeartIcon,
|
||||
share: ShareIcon,
|
||||
reviewStar: ReviewStarIcon,
|
||||
};
|
||||
|
||||
export type IconName =
|
||||
|
|
@ -67,7 +75,11 @@ export type IconName =
|
|||
| "priceMin"
|
||||
| "priceMax"
|
||||
| "boatYearMin"
|
||||
| "boatYearMax";
|
||||
| "boatYearMax"
|
||||
| "pin"
|
||||
| "heart"
|
||||
| "share"
|
||||
| "reviewStar";
|
||||
|
||||
export interface IconProps
|
||||
extends Omit<SVGProps<SVGSVGElement>, "name" | "preserveAspectRatio"> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue