Доработки по главной странице, итерация 12
This commit is contained in:
parent
e420440cea
commit
2b7c336239
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { DatePicker } from "@/components/ui/date-picker";
|
|
||||||
import {
|
import {
|
||||||
Carousel,
|
Carousel,
|
||||||
CarouselContent,
|
CarouselContent,
|
||||||
|
|
@ -13,7 +12,7 @@ import {
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Icon from "@/components/ui/icon";
|
import Icon from "@/components/ui/icon";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { GuestPicker } from "@/components/form/guest-picker";
|
import { GuestDatePicker } from "@/components/form/guest-date-picker";
|
||||||
|
|
||||||
const yacht = {
|
const yacht = {
|
||||||
name: "Яхта",
|
name: "Яхта",
|
||||||
|
|
@ -140,7 +139,7 @@ export default function FeaturedYacht() {
|
||||||
{/* Promoted badge */}
|
{/* Promoted badge */}
|
||||||
{yacht.isPromoted && (
|
{yacht.isPromoted && (
|
||||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
<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>
|
||||||
Это объявление продвигается.{" "}
|
Это объявление продвигается.{" "}
|
||||||
<span className="underline cursor-pointer">
|
<span className="underline cursor-pointer">
|
||||||
|
|
@ -152,7 +151,7 @@ export default function FeaturedYacht() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right side - Booking form */}
|
{/* 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>
|
<div>
|
||||||
{/* Promoted banner - Desktop only */}
|
{/* Promoted banner - Desktop only */}
|
||||||
<div
|
<div
|
||||||
|
|
@ -182,16 +181,11 @@ export default function FeaturedYacht() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Booking form */}
|
{/* Booking form */}
|
||||||
<div className="space-y-5 mb-8">
|
<div className="mb-8">
|
||||||
<div>
|
<div>
|
||||||
<DatePicker showIcon={false} />
|
<GuestDatePicker />
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<DatePicker showIcon={false} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<GuestPicker showIcon={false} />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Book button */}
|
{/* 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 (
|
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 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}
|
||||||
</label>
|
</label>
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
|
|
||||||
Loading…
Reference in New Issue