Доработки по главной странице, итерация 12
This commit is contained in:
parent
e420440cea
commit
2b7c336239
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DatePicker } from "@/components/ui/date-picker";
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
|
|
@ -13,7 +12,7 @@ import {
|
|||
import Image from "next/image";
|
||||
import Icon from "@/components/ui/icon";
|
||||
import { useState } from "react";
|
||||
import { GuestPicker } from "@/components/form/guest-picker";
|
||||
import { GuestDatePicker } from "@/components/form/guest-date-picker";
|
||||
|
||||
const yacht = {
|
||||
name: "Яхта",
|
||||
|
|
@ -140,7 +139,7 @@ export default function FeaturedYacht() {
|
|||
{/* Promoted badge */}
|
||||
{yacht.isPromoted && (
|
||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||
<Icon size={21} name="ad" />
|
||||
<Icon className="min-w-[21px] min-h-[21px]" size={21} name="ad" />
|
||||
<span>
|
||||
Это объявление продвигается.{" "}
|
||||
<span className="underline cursor-pointer">
|
||||
|
|
@ -152,7 +151,7 @@ export default function FeaturedYacht() {
|
|||
</div>
|
||||
|
||||
{/* Right side - Booking form */}
|
||||
<div className="min-w-[296px] flex flex-col justify-between">
|
||||
<div className="min-w-[296px] flex-0 flex flex-col justify-between">
|
||||
<div>
|
||||
{/* Promoted banner - Desktop only */}
|
||||
<div
|
||||
|
|
@ -182,16 +181,11 @@ export default function FeaturedYacht() {
|
|||
</div>
|
||||
|
||||
{/* Booking form */}
|
||||
<div className="space-y-5 mb-8">
|
||||
<div className="mb-8">
|
||||
<div>
|
||||
<DatePicker showIcon={false} />
|
||||
</div>
|
||||
<div>
|
||||
<DatePicker showIcon={false} />
|
||||
</div>
|
||||
<div>
|
||||
<GuestPicker showIcon={false} />
|
||||
<GuestDatePicker />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Book button */}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,310 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { format } from "date-fns";
|
||||
import { ru } from "date-fns/locale";
|
||||
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import { Counter } from "@/components/ui/counter";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
|
||||
interface GuestDatePickerProps {
|
||||
onApply?: (data: {
|
||||
date: Date | undefined;
|
||||
departureTime: string;
|
||||
arrivalTime: string;
|
||||
adults: number;
|
||||
children: number;
|
||||
}) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface CommonPopoverContentProps {
|
||||
date: Date | undefined;
|
||||
setDate: (date: Date | undefined) => void;
|
||||
departureTime: string;
|
||||
setDepartureTime: (time: string) => void;
|
||||
arrivalTime: string;
|
||||
setArrivalTime: (time: string) => void;
|
||||
adults: number;
|
||||
setAdults: (count: number) => void;
|
||||
childrenCount: number;
|
||||
setChildrenCount: (count: number) => void;
|
||||
handleApply: () => void;
|
||||
}
|
||||
|
||||
const CommonPopoverContent: React.FC<CommonPopoverContentProps> = ({
|
||||
date,
|
||||
setDate,
|
||||
departureTime,
|
||||
setDepartureTime,
|
||||
arrivalTime,
|
||||
setArrivalTime,
|
||||
adults,
|
||||
setAdults,
|
||||
childrenCount,
|
||||
setChildrenCount,
|
||||
handleApply,
|
||||
}) => {
|
||||
return (
|
||||
<PopoverContent className="rounded-[20px] p-6 pb-4 w-[374px]">
|
||||
{/* Календарь */}
|
||||
<div className="min-h-fit">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
className="mb-[24px]"
|
||||
locale={ru}
|
||||
disabled={(date) =>
|
||||
date < new Date(new Date().setHours(0, 0, 0, 0))
|
||||
}
|
||||
classNames={{
|
||||
root: "w-full",
|
||||
month: "flex w-full flex-col gap-4",
|
||||
button_previous:
|
||||
"h-8 w-8 flex items-center justify-center hover:bg-gray-100 rounded-md",
|
||||
button_next:
|
||||
"h-8 w-8 flex items-center justify-center hover:bg-gray-100 rounded-md",
|
||||
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",
|
||||
day_button: "font-bold ring-0 focus:ring-0",
|
||||
week: "mt-2 flex w-full",
|
||||
today: "bg-gray-100 text-gray-900 rounded-full",
|
||||
outside: "text-gray-300",
|
||||
disabled: "text-gray-400 cursor-not-allowed",
|
||||
selected:
|
||||
"rounded-full border-none outline-none !bg-brand text-white",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Счетчики гостей */}
|
||||
<div className="mb-[24px] flex gap-3">
|
||||
<Counter
|
||||
label="Взрослые"
|
||||
value={adults}
|
||||
onChange={setAdults}
|
||||
min={0}
|
||||
max={10}
|
||||
/>
|
||||
<Counter
|
||||
label="Дети"
|
||||
value={childrenCount}
|
||||
onChange={setChildrenCount}
|
||||
min={0}
|
||||
max={10}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Поля времени */}
|
||||
<div className="flex gap-3 mb-6">
|
||||
<div className="relative w-full h-12 px-3 border border-gray-300 rounded-full text-gray-700 font-medium text-center">
|
||||
<label className="absolute left-[24px] top-0 transform -translate-y-1/2 text-xs text-gray-500 pointer-events-none transition-all duration-200 bg-white px-1">
|
||||
Выход
|
||||
</label>
|
||||
<div className="relative h-full flex align-center">
|
||||
<ChevronDownIcon className="absolute right-0 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500 pointer-events-none" />
|
||||
<input
|
||||
type="time"
|
||||
value={departureTime}
|
||||
onChange={(e) => setDepartureTime(e.target.value)}
|
||||
className="w-full focus:outline-none focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative w-full h-12 px-3 border border-gray-300 rounded-full text-gray-700 font-medium text-center">
|
||||
<label className="absolute left-[24px] top-0 transform -translate-y-1/2 text-xs text-gray-500 pointer-events-none transition-all duration-200 bg-white px-1">
|
||||
Заход
|
||||
</label>
|
||||
<div className="relative h-full flex align-center">
|
||||
<ChevronDownIcon className="absolute right-0 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-500 pointer-events-none" />
|
||||
<input
|
||||
type="time"
|
||||
value={arrivalTime}
|
||||
onChange={(e) => setArrivalTime(e.target.value)}
|
||||
className="w-full focus:outline-none focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Кнопка Применить */}
|
||||
<Button
|
||||
onClick={handleApply}
|
||||
variant="gradient"
|
||||
className="font-bold text-white h-[44px] w-full px-8"
|
||||
>
|
||||
Применить
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
);
|
||||
};
|
||||
|
||||
export const GuestDatePicker: React.FC<GuestDatePickerProps> = ({
|
||||
onApply,
|
||||
className,
|
||||
}) => {
|
||||
const [date, setDate] = useState<Date>();
|
||||
const [departureTime, setDepartureTime] = useState("12:00");
|
||||
const [arrivalTime, setArrivalTime] = useState("13:00");
|
||||
const [adults, setAdults] = useState(1);
|
||||
const [children, setChildren] = useState(0);
|
||||
const [isDepartureOpen, setIsDepartureOpen] = useState(false);
|
||||
const [isArrivalOpen, setIsArrivalOpen] = useState(false);
|
||||
const [isGuestOpen, setIsGuestOpen] = useState(false);
|
||||
|
||||
const handleApply = () => {
|
||||
onApply?.({
|
||||
date,
|
||||
departureTime,
|
||||
arrivalTime,
|
||||
adults,
|
||||
children,
|
||||
});
|
||||
setIsDepartureOpen(false);
|
||||
setIsArrivalOpen(false);
|
||||
setIsGuestOpen(false);
|
||||
};
|
||||
|
||||
const getDepartureDisplayText = () => {
|
||||
if (!date || !departureTime) return "Выход";
|
||||
|
||||
return (
|
||||
<>
|
||||
{format(date, "d MMMM", {
|
||||
locale: ru,
|
||||
})}
|
||||
, <span className="font-bold">{departureTime}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getArrivalDisplayText = () => {
|
||||
if (!date || !arrivalTime) return "Заход";
|
||||
|
||||
return (
|
||||
<>
|
||||
{format(date, "d MMMM", {
|
||||
locale: ru,
|
||||
})}
|
||||
, <span className="font-bold">{arrivalTime}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getGuestDisplayText = () => {
|
||||
if (adults === 1 && children === 0) return "1 гость";
|
||||
return (
|
||||
<span className="font-bold">
|
||||
Взрослых: {adults}, Детей: {children}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className="space-y-5">
|
||||
{/* Кнопка Выход */}
|
||||
<Popover
|
||||
open={isDepartureOpen}
|
||||
onOpenChange={setIsDepartureOpen}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-[64px] px-4 w-full justify-between font-normal"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span>{getDepartureDisplayText()}</span>
|
||||
</div>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<CommonPopoverContent
|
||||
date={date}
|
||||
setDate={setDate}
|
||||
departureTime={departureTime}
|
||||
setDepartureTime={setDepartureTime}
|
||||
arrivalTime={arrivalTime}
|
||||
setArrivalTime={setArrivalTime}
|
||||
adults={adults}
|
||||
setAdults={setAdults}
|
||||
childrenCount={children}
|
||||
setChildrenCount={setChildren}
|
||||
handleApply={handleApply}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
{/* Кнопка Заход */}
|
||||
<Popover open={isArrivalOpen} onOpenChange={setIsArrivalOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-[64px] px-4 w-full justify-between font-normal"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span>{getArrivalDisplayText()}</span>
|
||||
</div>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<CommonPopoverContent
|
||||
date={date}
|
||||
setDate={setDate}
|
||||
departureTime={departureTime}
|
||||
setDepartureTime={setDepartureTime}
|
||||
arrivalTime={arrivalTime}
|
||||
setArrivalTime={setArrivalTime}
|
||||
adults={adults}
|
||||
setAdults={setAdults}
|
||||
childrenCount={children}
|
||||
setChildrenCount={setChildren}
|
||||
handleApply={handleApply}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
{/* Кнопка Гости */}
|
||||
<Popover open={isGuestOpen} onOpenChange={setIsGuestOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-[64px] px-4 w-full justify-between font-normal"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span>{getGuestDisplayText()}</span>
|
||||
</div>
|
||||
{isGuestOpen ? (
|
||||
<ChevronUpIcon className="h-4 w-4" />
|
||||
) : (
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<CommonPopoverContent
|
||||
date={date}
|
||||
setDate={setDate}
|
||||
departureTime={departureTime}
|
||||
setDepartureTime={setDepartureTime}
|
||||
arrivalTime={arrivalTime}
|
||||
setArrivalTime={setArrivalTime}
|
||||
adults={adults}
|
||||
setAdults={setAdults}
|
||||
childrenCount={children}
|
||||
setChildrenCount={setChildren}
|
||||
handleApply={handleApply}
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -31,7 +31,7 @@ export const Counter: React.FC<CounterProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="relative flex w-full items-center justify-between items-center border border-gray-200 rounded-full px-8 py-3 bg-white">
|
||||
<div className="relative flex w-full items-center justify-between items-center border border-gray-200 rounded-full px-4 py-3 bg-white">
|
||||
<label className="absolute left-[32px] top-0 transform -translate-y-1/2 text-xs text-gray-500 transition-all duration-200 bg-white px-1">
|
||||
{label}
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
|
||||
Loading…
Reference in New Issue