мобильные версии страниц

This commit is contained in:
Sergey Bolshakov 2025-12-13 23:47:06 +03:00
parent 2383f75fe2
commit bc31770c51
8 changed files with 819 additions and 227 deletions

View File

@ -2,19 +2,33 @@
import { useState } from "react";
import { Calendar } from "@/components/ui/calendar";
import { isSameMonth, isBefore, startOfDay, format } from "date-fns";
import {
isSameMonth,
isBefore,
startOfDay,
format,
eachDayOfInterval,
startOfMonth,
endOfMonth,
} from "date-fns";
import { ru } from "date-fns/locale";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
import { ChevronLeftIcon, ChevronRightIcon, Clock } from "lucide-react";
interface YachtAvailabilityProps {
price: string;
mobile?: boolean;
}
export function YachtAvailability({ price }: YachtAvailabilityProps) {
export function YachtAvailability({
price,
mobile = false,
}: YachtAvailabilityProps) {
const today = startOfDay(new Date());
const [currentMonth, setCurrentMonth] = useState(
new Date(today.getFullYear(), today.getMonth(), 1)
);
const [startTime, setStartTime] = useState<string>("");
const [endTime, setEndTime] = useState<string>("");
const unavailableDates = Array.from({ length: 26 }, (_, i) => {
return new Date(2025, 3, i + 1);
@ -38,26 +52,182 @@ export function YachtAvailability({ price }: YachtAvailabilityProps) {
return isDateUnavailable(date) || isDateInPast(date);
};
const isDateAvailable = (date: Date) => {
return !shouldBeCrossedOut(date) && isSameMonth(date, currentMonth);
};
const getAvailableDaysCount = () => {
const monthStart = startOfMonth(currentMonth);
const monthEnd = endOfMonth(currentMonth);
const daysInMonth = eachDayOfInterval({
start: monthStart,
end: monthEnd,
});
return daysInMonth.filter((day) => isDateAvailable(day)).length;
};
const goToPreviousMonth = () => {
setCurrentMonth(
new Date(
currentMonth.getFullYear(),
currentMonth.getMonth() - 1,
1
)
new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1)
);
};
const goToNextMonth = () => {
setCurrentMonth(
new Date(
currentMonth.getFullYear(),
currentMonth.getMonth() + 1,
1
)
new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1)
);
};
// Генерация времени для селекта
const timeOptions = Array.from({ length: 24 * 2 }, (_, i) => {
const hours = Math.floor(i / 2);
const minutes = (i % 2) * 30;
const timeString = `${String(hours).padStart(2, "0")}:${String(
minutes
).padStart(2, "0")}`;
return { value: timeString, label: timeString };
});
if (mobile) {
return (
<div className="w-full">
{/* Навигация по месяцам */}
<div className="flex items-center justify-between mb-4">
<button
onClick={goToPreviousMonth}
className="w-10 h-10 rounded-full border border-[#dfdfdf] flex items-center justify-center hover:bg-gray-50 transition-colors"
>
<ChevronLeftIcon className="size-4 text-[#333333]" />
</button>
<div className="flex flex-col items-center">
<span className="text-lg font-medium text-[#333333] capitalize">
{format(currentMonth, "LLLL", { locale: ru })}
</span>
<span className="text-sm text-[#999999]">
Свободных дней: {getAvailableDaysCount()}
</span>
</div>
<button
onClick={goToNextMonth}
className="w-10 h-10 rounded-full border border-[#dfdfdf] flex items-center justify-center hover:bg-gray-50 transition-colors"
>
<ChevronRightIcon className="size-4 text-[#333333]" />
</button>
</div>
{/* Календарь */}
<div style={{ flexShrink: 0 }}>
<Calendar
mode="single"
month={currentMonth}
onMonthChange={setCurrentMonth}
showOutsideDays={false}
className="w-full p-0"
locale={ru}
formatters={{
formatWeekdayName: (date) => {
const weekdays = [
"ВС",
"ПН",
"ВТ",
"СР",
"ЧТ",
"ПТ",
"СБ",
];
return weekdays[date.getDay()];
},
}}
classNames={{
root: "w-full",
month: "flex w-full flex-col gap-2",
nav: "hidden",
month_caption: "hidden",
caption_label: "hidden",
button_previous: "hidden",
button_next: "hidden",
table: "w-full border-collapse table-fixed",
weekdays: "flex w-full mb-2",
weekday:
"flex-1 text-[#999999] text-xs font-normal p-2 text-center",
week: "flex w-full min-h-[50px]",
day: "relative flex-1 min-w-0 flex-shrink-0",
}}
components={{
DayButton: ({ day, ...props }) => {
if (!isSameMonth(day.date, currentMonth)) {
return <div className="hidden" />;
}
const isCrossedOut = shouldBeCrossedOut(
day.date
);
return (
<button
{...props}
className={`relative w-full flex items-center justify-center text-sm font-medium transition-colors ${
isCrossedOut
? "text-[#CCCCCC] line-through"
: "text-[#333333] hover:bg-gray-100"
}`}
style={
{
aspectRatio: "1 / 1",
minHeight: "44px",
} as React.CSSProperties
}
disabled={isCrossedOut}
>
{day.date.getDate()}
</button>
);
},
}}
/>
</div>
{/* Выбор времени */}
<div className="space-y-4 mb-4" style={{ marginTop: "24px" }}>
<div className="flex gap-3">
<div className="flex-1">
<select
value={startTime}
onChange={(e) => setStartTime(e.target.value)}
className="w-full px-4 py-3 border border-[#DFDFDF] rounded-lg text-base text-[#333333] bg-white appearance-none"
>
<option value="">--:--</option>
{timeOptions.map((time) => (
<option key={time.value} value={time.value}>
{time.label}
</option>
))}
</select>
</div>
<div className="flex-1">
<select
value={endTime}
onChange={(e) => setEndTime(e.target.value)}
className="w-full px-4 py-3 border border-[#DFDFDF] rounded-lg text-base text-[#333333] bg-white appearance-none"
>
<option value="">--:--</option>
{timeOptions.map((time) => (
<option key={time.value} value={time.value}>
{time.label}
</option>
))}
</select>
</div>
</div>
<div className="flex items-center gap-2 text-sm text-[#999999]">
<Clock size={16} />
<span>По местному времени яхты</span>
</div>
</div>
</div>
);
}
return (
<div className="space-y-4 w-full">
<div className="flex items-center justify-between">

View File

@ -52,7 +52,7 @@ export function YachtGallery({ images, badge }: YachtGalleryProps) {
<CarouselContent>
{images.map((img, index) => (
<CarouselItem key={index}>
<div className="relative w-full h-[592px] rounded-lg overflow-hidden">
<div className="relative w-full h-[60vh] lg:h-[592px] rounded-0 lg:rounded-[24px] overflow-hidden">
<Image
src={img}
alt={`Yacht image ${index + 1}`}
@ -64,12 +64,12 @@ export function YachtGallery({ images, badge }: YachtGalleryProps) {
</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" />
<CarouselPrevious className="left-2 lg:left-4 bg-white/80 hover:bg-white border-gray-300 w-10 h-10 lg:w-12 lg:h-12 rounded-full" />
<CarouselNext className="right-2 lg:right-4 bg-white/80 hover:bg-white border-gray-300 w-10 h-10 lg:w-12 lg:h-12 rounded-full" />
</Carousel>
{/* Badge - поверх слайдера, не скроллится */}
{badge && (
<div className="absolute top-4 left-4 z-20 pointer-events-none">
<div className="absolute bottom-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>
@ -84,8 +84,8 @@ export function YachtGallery({ images, badge }: YachtGalleryProps) {
</div>
</div>
{/* Thumbnails */}
<div className="flex gap-2 overflow-x-auto pb-2">
{/* Thumbnails - скрыты на мобильных */}
<div className="hidden lg:flex gap-2 overflow-x-auto pb-2">
{images.map((img, index) => (
<button
key={index}
@ -108,4 +108,3 @@ export function YachtGallery({ images, badge }: YachtGalleryProps) {
</div>
);
}

View File

@ -0,0 +1,37 @@
export const YACHT = {
id: 1,
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",
},
};

View File

@ -1,62 +1,52 @@
"use client";
import { useParams } from "next/navigation";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { useState } from "react";
import { ArrowLeft, Heart } from "lucide-react";
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";
import { YACHT } from "./const";
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",
},
};
// const params = useParams();
const router = useRouter();
const [activeTab, setActiveTab] = useState<
| "availability"
| "description"
| "characteristics"
| "contact"
| "requisites"
| "reviews"
>("availability");
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">
<main className="bg-[#f4f4f4] min-h-screen ">
{/* Мобильная фиксированная верхняя панель навигации */}
<div className="lg:hidden fixed top-[73px] left-0 right-0 z-[50] bg-white border-b border-gray-200">
<div className="flex items-center justify-between px-4 h-14">
<button
onClick={() => router.back()}
className="flex items-center justify-center"
>
<ArrowLeft size={24} className="text-[#333333]" />
</button>
<h2 className="text-base font-medium text-[#333333]">
Яхта
</h2>
<button className="flex items-center justify-center">
<Heart size={24} className="text-[#333333]" />
</button>
</div>
</div>
{/* Десктопная версия - Breadcrumbs */}
<div className="hidden lg:block container max-w-6xl mx-auto px-4 py-4">
<div className="text-sm text-[#999999] flex items-center gap-4">
<Link href="/">
<span className="cursor-pointer hover:text-[#333333] transition-colors">
Аренда яхты
@ -69,26 +59,147 @@ export default function YachtDetailPage() {
</span>
</Link>
<span>&gt;</span>
<span className="text-[#333333]">{yacht.name}</span>
<span className="text-[#333333]">{YACHT.name}</span>
</div>
</div>
{/* Main Content Container */}
<div className="bg-white rounded-[16px] p-6">
<div className="lg:container lg:max-w-6xl lg:mx-auto lg:px-4 lg:pb-6">
<div className="bg-white lg:rounded-[16px] lg:p-6">
{/* Мобильная версия - без отступов сверху, с отступом для фиксированной панели */}
<div className="lg:hidden pt-[50px]">
{/* Gallery */}
<YachtGallery
images={YACHT.images}
badge={YACHT.badge}
/>
{/* Yacht Title */}
<div className="px-4 pt-4">
<h1 className="text-xl font-bold text-[#333333] mb-4">
{YACHT.name}
</h1>
</div>
{/* Tabs */}
<div className="px-4 border-b border-gray-200 overflow-x-auto">
<div className="flex gap-6 min-w-max">
<button
onClick={() => setActiveTab("availability")}
className={`pb-3 text-base font-medium transition-colors whitespace-nowrap ${
activeTab === "availability"
? "text-[#008299] border-b-2 border-[#008299]"
: "text-[#999999]"
}`}
>
Свободные даты
</button>
<button
onClick={() => setActiveTab("description")}
className={`pb-3 text-base font-medium transition-colors whitespace-nowrap ${
activeTab === "description"
? "text-[#008299] border-b-2 border-[#008299]"
: "text-[#999999]"
}`}
>
Описание
</button>
<button
onClick={() =>
setActiveTab("characteristics")
}
className={`pb-3 text-base font-medium transition-colors whitespace-nowrap ${
activeTab === "characteristics"
? "text-[#008299] border-b-2 border-[#008299]"
: "text-[#999999]"
}`}
>
Характеристики
</button>
<button
onClick={() => setActiveTab("contact")}
className={`pb-3 text-base font-medium transition-colors whitespace-nowrap ${
activeTab === "contact"
? "text-[#008299] border-b-2 border-[#008299]"
: "text-[#999999]"
}`}
>
Контактное лицо и реквизиты
</button>
<button
onClick={() => setActiveTab("reviews")}
className={`pb-3 text-base font-medium transition-colors whitespace-nowrap ${
activeTab === "reviews"
? "text-[#008299] border-b-2 border-[#008299]"
: "text-[#999999]"
}`}
>
Отзывы
</button>
</div>
</div>
{/* Tab Content */}
<div className="px-4 py-6">
{activeTab === "availability" && (
<YachtAvailability
price={YACHT.price}
mobile={true}
/>
)}
{activeTab === "description" && (
<div>
<p className="text-base text-[#666666] leading-relaxed">
{YACHT.description}
</p>
</div>
)}
{activeTab === "characteristics" && (
<YachtCharacteristics yacht={YACHT} />
)}
{activeTab === "contact" && (
<ContactInfo
contactPerson={YACHT.contactPerson}
requisites={YACHT.requisites}
/>
)}
{activeTab === "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>
</div>
{/* Десктопная версия */}
<div className="hidden lg:block">
{/* 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-xl md:text-2 xl font-bold text-[#333333]">
{yacht.name}
<h1 className="text-xl md:text-2xl 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="pin" size={32} />
<span className="text-base">
{yacht.location}
{YACHT.location}
</span>
</div>
<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>
<span className="text-base">
Поделиться
</span>
</button>
<button className="flex items-center gap-2 cursor-pointer text-[#333333] hover:text-[#008299] transition-colors">
<Icon name="heart" size={32} />
@ -101,8 +212,8 @@ export default function YachtDetailPage() {
<div className="space-y-6">
{/* Gallery */}
<YachtGallery
images={yacht.images}
badge={yacht.badge}
images={YACHT.images}
badge={YACHT.badge}
/>
{/* Content with Booking Widget on the right */}
@ -110,10 +221,10 @@ export default function YachtDetailPage() {
{/* Left column - all content below gallery */}
<div className="flex-1 space-y-6">
{/* Availability */}
<YachtAvailability price={yacht.price} />
<YachtAvailability price={YACHT.price} />
{/* Characteristics */}
<YachtCharacteristics yacht={yacht} />
<YachtCharacteristics yacht={YACHT} />
{/* Description */}
<div>
@ -121,14 +232,14 @@ export default function YachtDetailPage() {
Описание
</h2>
<p className="text-base text-[#666666] leading-relaxed">
{yacht.description}
{YACHT.description}
</p>
</div>
{/* Contact and Requisites */}
<ContactInfo
contactPerson={yacht.contactPerson}
requisites={yacht.requisites}
contactPerson={YACHT.contactPerson}
requisites={YACHT.requisites}
/>
{/* Reviews */}
@ -149,12 +260,33 @@ export default function YachtDetailPage() {
{/* Right column - Booking Widget (sticky) */}
<div className="lg:w-74 flex-shrink-0 lg:sticky lg:top-24 self-start">
<BookingWidget price={yacht.price} />
<BookingWidget price={YACHT.price} />
</div>
</div>
</div>
</div>
</div>
</div>
{/* Мобильная фиксированная нижняя панель бронирования */}
<div className="lg:hidden sticky bottom-0 left-0 right-0 z-[10] bg-white border-t border-gray-200 px-4 py-3">
<div className="flex items-center justify-between">
<div>
<span className="text-lg font-bold text-[#333333]">
{YACHT.price}
</span>
<span className="text-sm text-[#999999] ml-1">
/ час
</span>
</div>
<button
onClick={() => router.push("/confirm")}
className="bg-[#008299] text-white px-6 py-3 rounded-lg font-bold text-base hover:bg-[#006d7a] transition-colors"
>
Забронировать
</button>
</div>
</div>
</main>
);
}

View File

@ -10,7 +10,11 @@ import { DatePicker } from "@/components/ui/date-picker";
import { GuestPicker } from "@/components/form/guest-picker";
import Icon from "@/components/ui/icon";
export default function CatalogSidebar() {
interface CatalogSidebarProps {
onApply?: () => void;
}
export default function CatalogSidebar({ onApply }: CatalogSidebarProps) {
const [lengthRange, setLengthRange] = useState([7, 50]);
const [priceRange, setPriceRange] = useState([3000, 200000]);
const [yearRange, setYearRange] = useState([1991, 2025]);
@ -254,7 +258,10 @@ export default function CatalogSidebar() {
</div>
{/* Кнопка Применить */}
<Button className="w-full bg-[#008299] hover:bg-[#006d7f] text-white font-bold h-12 mt-2">
<Button
onClick={onApply}
className="w-full bg-[#008299] hover:bg-[#006d7f] text-white font-bold h-12 mt-2"
>
Применить
</Button>
</div>

View File

@ -1,5 +1,6 @@
"use client";
import { useState } from "react";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import Image from "next/image";
import Icon from "@/components/ui/icon";
@ -12,6 +13,8 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Sliders, X, ArrowLeft } from "lucide-react";
import { Button } from "@/components/ui/button";
const yachts = [
{
@ -113,11 +116,13 @@ const yachts = [
];
export default function CatalogPage() {
const [isFiltersOpen, setIsFiltersOpen] = useState(false);
return (
<main className="bg-[#f4f4f4] min-h-screen">
<div className="container max-w-6xl mx-auto px-4 py-6">
{/* Breadcrumbs */}
<div className="mb-4 text-sm text-[#999999] flex items-center gap-[16px]">
{/* Breadcrumbs - скрыты на мобильных */}
<div className="hidden lg:flex mb-4 text-sm text-[#999999] items-center gap-[16px]">
<Link href="/">
<span className="cursor-pointer hover:text-[#333333] transition-colors">
Аренда яхты
@ -127,21 +132,86 @@ export default function CatalogPage() {
<span className="text-[#333333]">Каталог яхт</span>
</div>
{/* Мобильная навигационная панель */}
<div className="lg:hidden mb-4">
<div className="flex items-center gap-3">
{/* Кнопка "Назад" */}
<Link href="/">
<button className="flex-shrink-0 w-8 h-8 flex items-center justify-center">
<ArrowLeft
size={32}
className="text-[#333333] font-bold"
/>
</button>
</Link>
{/* Текстовый блок */}
<div className="flex-1 min-w-0">
<div className="font-bold text-[#333333] text-base truncate">
Балаклава
</div>
<div className="text-sm text-[#999999] truncate">
Когда? · С кем?
</div>
</div>
{/* Кнопка "Фильтры" */}
<Button
onClick={() => setIsFiltersOpen(true)}
className="flex-shrink-0 w-[56px] h-[56px] rounded-full bg-white hover:bg-gray-50 text-black p-0"
>
<Sliders size={20} className="text-[#333333]" />
</Button>
</div>
</div>
<div className="flex flex-col lg:flex-row gap-[74px]">
{/* Sidebar */}
<div className="w-full lg:w-[260px] flex-shrink-0">
{/* Sidebar - скрыт на мобильных, виден на десктопе */}
<div className="hidden lg:block w-full lg:w-[260px] flex-shrink-0">
<CatalogSidebar />
</div>
{/* Мобильное модальное окно фильтров */}
{isFiltersOpen && (
<div className="fixed inset-0 z-50 lg:hidden">
{/* Overlay */}
<div
className="absolute inset-0 bg-black/50"
onClick={() => setIsFiltersOpen(false)}
/>
{/* Модальное окно */}
<div className="absolute inset-0 bg-white overflow-y-auto">
<div className="sticky top-0 bg-white border-b border-gray-200 p-4 flex items-center justify-between z-10">
<h2 className="text-lg font-bold text-[#333333]">
Фильтры
</h2>
<Button
variant="ghost"
size="icon"
onClick={() => setIsFiltersOpen(false)}
className="h-8 w-8"
>
<X size={20} />
</Button>
</div>
<div className="p-4">
<CatalogSidebar
onApply={() => setIsFiltersOpen(false)}
/>
</div>
</div>
</div>
)}
{/* Main Content */}
<div className="flex-1">
{/* Header */}
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-end mb-6 gap-4">
<div>
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-end mb-6">
<div className="w-full lg:w-auto">
<h1 className="text-2xl md:text-3xl font-bold text-[#333333] mb-4">
Аренда яхт
</h1>
<p className="text-lg text-[#333333]">
<p className="hidden lg:block text-lg text-[#333333]">
Доступно яхт:{" "}
<span className="text-[#b2b2b2]">
{yachts.length}

View File

@ -3,29 +3,207 @@
import { Button } from "@/components/ui/button";
import Image from "next/image";
import Link from "next/link";
import { User, ArrowUpRight, Map } from "lucide-react";
import { User, ArrowUpRight, Map, ArrowLeft, Heart } from "lucide-react";
import { useState } from "react";
import { useRouter } from "next/navigation";
export default function ConfirmPage() {
const [promocode, setPromocode] = useState("");
const router = useRouter();
return (
<main className="bg-[#f4f4f4] grow">
{/* Мобильная версия */}
<div className="lg:hidden">
{/* Верхний блок с навигацией */}
<div className="bg-white border-b border-[#DFDFDF]">
<div className="container max-w-6xl mx-auto px-4 py-3">
<div className="flex items-center justify-between gap-4">
{/* Кнопка назад */}
<button
onClick={() => router.back()}
className="flex-shrink-0 w-10 h-10 rounded-full border border-[#DFDFDF] flex items-center justify-center hover:bg-[#f4f4f4] transition-colors"
>
<ArrowLeft
size={20}
className="text-[#333333]"
/>
</button>
{/* Центральный блок с информацией */}
<div className="flex-1 min-w-0 text-center">
<h2 className="text-base font-bold text-[#333333] mb-1">
Яхта Сеньорита
</h2>
<div className="flex justify-center gap-10 text-xs text-[#666666]">
<span>09 авг.</span>
<span>Гостей: 1</span>
</div>
</div>
{/* Кнопка избранного */}
<button className="flex-shrink-0 w-10 h-10 flex items-center justify-center hover:opacity-70 transition-opacity">
<Heart
size={20}
className="text-[#333333] stroke-2"
/>
</button>
</div>
</div>
</div>
<div className="container max-w-6xl mx-auto">
<div className="bg-white p-4">
{/* Заголовок с иконкой */}
<div className="flex items-center gap-2 mb-6">
<h1 className="text-xl text-[#333333]">
Ваше бронирование 🛥
</h1>
</div>
{/* Поля Выход и Заход */}
<div className="grid grid-cols-2 gap-3 mb-3">
<div className="relative">
<label className="absolute -top-2 left-8 px-1 bg-white text-xs text-[#999999]">
Выход
</label>
<div className="bg-white rounded-full px-8 py-4 border border-[#DFDFDF]">
<div className="text-[#333333]">
9 Авг 00:00
</div>
</div>
</div>
<div className="relative">
<label className="absolute -top-2 left-8 px-1 bg-white text-xs text-[#999999]">
Заход
</label>
<div className="bg-white rounded-full px-8 py-4 border border-[#DFDFDF]">
<div className="text-[#333333]">
9 Авг 02:00
</div>
</div>
</div>
</div>
{/* По местному времени яхты */}
<div className="flex items-center gap-2 text-sm text-[#333333] mb-4">
<Map size={16} className="text-[#333333]" />
<span>По местному времени яхты</span>
</div>
{/* Гости */}
<div className="mb-6">
<div className="relative">
<label className="absolute -top-2 left-8 px-1 bg-white text-xs text-[#999999]">
Гостей
</label>
<div className="bg-white rounded-full px-8 py-4 border border-[#DFDFDF] flex items-center justify-between">
<span className="text-[#333333]">
1 гость
</span>
</div>
</div>
</div>
{/* Правила отмены */}
<div className="mb-8">
<h3 className="text-base font-bold text-[#333333] mb-4">
Правила отмены
</h3>
<p className="text-base text-[#333333]">
При отмене до 10 мая вы получите частичный
возврат.{" "}
<Link
href="#"
className="text-sm text-[#2D908D] hover:text-[#007088] font-bold transition-colors"
>
Подробнее
</Link>
</p>
</div>
{/* Детализация цены */}
<div className="mb-8">
<h3 className="text-base font-bold text-[#333333] mb-4">
Детализация цены
</h3>
<div>
<div className="flex justify-between items-center mb-4">
<span className="text-[#333333]">
26 400 x 2ч
</span>
<span className="text-[#333333]">
52 800
</span>
</div>
<div className="flex justify-between items-center mb-4 pb-4 border-b border-[#DFDFDF]">
<span className="text-[#333333]">
Услуги
</span>
<span className="text-[#333333]">0 Р</span>
</div>
<div className="flex justify-between items-center">
<span className="text-[#333333]">
Итого:
</span>
<span className="font-bold text-[#333333]">
52 800 Р
</span>
</div>
</div>
</div>
{/* Промокод */}
<div className="mb-4 pb-6 border-b border-[#DFDFDF]">
<div className="w-full flex gap-2">
<input
type="text"
placeholder="Промокод"
value={promocode}
onChange={(e) =>
setPromocode(e.target.value)
}
className="flex-1 min-w-0 px-4 py-3 h-[64px] border border-[#DFDFDF] rounded-full text-sm text-[#757575] focus:outline-none focus:ring-2 focus:ring-[#008299] focus:border-transparent"
/>
<Button
variant="default"
className="flex-shrink-0 h-[64px] w-[64px] bg-[#2D908D] hover:bg-[#007088] text-white rounded-full p-0 transition-colors duration-200"
>
<ArrowUpRight size={14} />
</Button>
</div>
</div>
{/* Кнопка отправки заявки */}
<Button
variant="default"
className="w-full h-[56px] bg-[#2D908D] hover:bg-[#007088] text-white font-bold rounded-full transition-colors duration-200"
>
Отправить заявку
</Button>
</div>
</div>
</div>
{/* Десктопная версия */}
<div className="hidden lg:block">
<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-[16px]">
{/* Breadcrumbs - скрыты на мобильных */}
<div className="hidden lg:flex mb-6 text-sm text-[#999999] items-center gap-[16px]">
<Link href="/">
<span className="cursor-pointer hover:text-[#333333] transition-colors">
Аренда яхты
</span>
</Link>
<span>&gt;</span>
<span className="text-[#333333]">Ваше бронирование</span>
<span className="text-[#333333]">
Ваше бронирование
</span>
</div>
<div className="flex flex-col lg:flex-row gap-6">
{/* Левая колонка - Информация о яхте и ценах */}
<div className="w-full lg:w-[336px] flex-shrink-0 flex flex-col gap-6">
{/* Левая колонка - Информация о яхте и ценах - скрыта на мобильных */}
<div className="hidden lg:flex w-full lg:w-[336px] flex-shrink-0 flex-col gap-6">
<div className="bg-white rounded-[16px]">
<div className="p-4">
{/* Изображение яхты */}
@ -121,8 +299,7 @@ export default function ConfirmPage() {
{/* Правая колонка - Подтверждение бронирования */}
<div className="flex-1">
<div className="bg-white rounded-[16px]">
<div className="p-8">
<div className="bg-white rounded-[16px] p-8">
{/* Заголовок */}
<h1 className="text-2xl text-[#333333] mb-4">
Проверьте данные

View File

@ -11,7 +11,7 @@ export default function Header() {
const [isAuthDialogOpen, setIsAuthDialogOpen] = useState(false);
return (
<header className="sticky top-0 z-50 bg-white/95 backdrop-blur-sm border-b border-gray-100">
<header className="sticky top-0 z-50 bg-white backdrop-blur-sm border-b border-gray-100">
<div className="w-full max-w-6xl mx-auto">
<div className="flex justify-between items-center px-4 sm:px-6 lg:px-8 py-4">
{/* Логотип */}