diff --git a/public/images/sport/sport1.jpg b/public/images/sport/sport1.jpg new file mode 100644 index 0000000..013222d Binary files /dev/null and b/public/images/sport/sport1.jpg differ diff --git a/public/images/sport/sport2.jpg b/public/images/sport/sport2.jpg new file mode 100644 index 0000000..ff2ff60 Binary files /dev/null and b/public/images/sport/sport2.jpg differ diff --git a/public/images/sport/sport3.jpg b/public/images/sport/sport3.jpg new file mode 100644 index 0000000..a624f1d Binary files /dev/null and b/public/images/sport/sport3.jpg differ diff --git a/public/images/sport/sport4.png b/public/images/sport/sport4.png new file mode 100644 index 0000000..ae8890c Binary files /dev/null and b/public/images/sport/sport4.png differ diff --git a/public/images/sport/sport5.jpg b/public/images/sport/sport5.jpg new file mode 100644 index 0000000..3f15ad2 Binary files /dev/null and b/public/images/sport/sport5.jpg differ diff --git a/public/images/sport/sport6.png b/public/images/sport/sport6.png new file mode 100644 index 0000000..1b41b31 Binary files /dev/null and b/public/images/sport/sport6.png differ diff --git a/src/app/components/Hero.tsx b/src/app/components/Hero.tsx index d26f541..7197ce9 100644 --- a/src/app/components/Hero.tsx +++ b/src/app/components/Hero.tsx @@ -3,20 +3,11 @@ import Image from "next/image"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { useState } from "react"; import { DatePicker } from "@/components/ui/date-picker"; import Icon from "@/components/ui/icon"; +import { GuestPicker } from "@/components/form/guest-picker"; export default function Hero() { - const [guests, setGuests] = useState(""); - return (
- + {/* Кнопка поиска */} diff --git a/src/app/components/Sport.tsx b/src/app/components/Sport.tsx new file mode 100644 index 0000000..533833b --- /dev/null +++ b/src/app/components/Sport.tsx @@ -0,0 +1,91 @@ +"use client"; + +import Image from "next/image"; +import { Card } from "@/components/ui/card"; + +// Массив с данными о карточках +const sportCards = [ + { + id: 1, + image: "/images/sport/sport1.jpg", + alt: "Вейксерф", + title: "Вейксерф", + }, + { + id: 2, + image: "/images/sport/sport2.jpg", + alt: "Вейкборд", + title: "Вейкборд", + }, + { + id: 3, + image: "/images/sport/sport3.jpg", + alt: "Гидроциклы", + title: "Гидроциклы", + }, + { + id: 4, + image: "/images/sport/sport4.png", + alt: "Дайвинг", + title: "Дайвинг", + }, + { + id: 5, + image: "/images/sport/sport5.jpg", + alt: "Сапы", + title: "Сапы", + }, + { + id: 6, + image: "/images/sport/sport6.png", + alt: "Каяки", + title: "Каяки", + }, +]; + +export default function Sport() { + return ( +
+
+
+

+ Активный отдых и спорт +

+

+ Идеальное сочетание драйва, свободы и природы. +

+

+ Попробуйте вейксерф, вейкборд, гидрофоил, дайвинг, сапы + и каяки — каждый из них подарит уникальные впечатления и + сделает вашу морскую прогулку по-настоящему + незабываемой. +

+
+ + {/* Три маленьких карточки на всю ширину */} +
+ {sportCards.map((card) => ( +
+ +
+ {card.alt} +
+
+
+

+ {card.title} +

+
+
+ ))} +
+
+
+ ); +} diff --git a/src/app/globals.css b/src/app/globals.css index d053b8f..e7e30c5 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -133,3 +133,7 @@ @apply bg-background text-foreground; } } + +input[type="time"]::-webkit-calendar-picker-indicator { + background: none; +} \ No newline at end of file diff --git a/src/app/page.tsx b/src/app/page.tsx index f6ee5e8..6128b97 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,6 +5,7 @@ import Advantages from "@/app/components/Advantages"; import VideoSection from "@/app/components/VideoSection"; import Articles from "@/app/components/Articles"; import Reviews from "@/app/components/Reviews"; +import Sport from "@/app/components/Sport"; export default function Home() { return ( @@ -13,6 +14,7 @@ export default function Home() { + diff --git a/src/components/form/guest-picker.tsx b/src/components/form/guest-picker.tsx new file mode 100644 index 0000000..b800967 --- /dev/null +++ b/src/components/form/guest-picker.tsx @@ -0,0 +1,84 @@ +"use client"; + +import React, { useState } from "react"; +import { + Select, + SelectContent, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Button } from "@/components/ui/button"; +import Icon from "../ui/icon"; +import { Counter } from "../ui/counter"; + +interface GuestPickerProps { + onApply?: (adults: number, children: number) => void; + className?: string; +} + +export const GuestPicker: React.FC = ({ onApply }) => { + const [adults, setAdults] = useState(1); + const [children, setChildren] = useState(0); + const [isOpen, setIsOpen] = useState(false); + + const handleApply = () => { + onApply?.(adults, children); + setIsOpen(false); + }; + + const getDisplayText = () => { + const total = adults + children; + if (total === 0) return "Сколько гостей?"; + if (children === 0) + return `${adults} ${adults === 1 ? "взрослый" : "взрослых"}`; + return `${adults} ${ + adults === 1 ? "взрослый" : "взрослых" + }, ${children} ${children === 1 ? "ребенок" : "детей"}`; + }; + + return ( +
+ +
+ ); +}; diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx index 1ef590d..04d0fa3 100644 --- a/src/components/ui/calendar.tsx +++ b/src/components/ui/calendar.tsx @@ -1,216 +1,230 @@ -"use client" +"use client"; -import * as React from "react" +import * as React from "react"; import { - ChevronDownIcon, - ChevronLeftIcon, - ChevronRightIcon, -} from "lucide-react" -import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker" -import { ru } from "date-fns/locale" + ChevronDownIcon, + ChevronLeftIcon, + ChevronRightIcon, +} from "lucide-react"; +import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"; +import { ru } from "date-fns/locale"; -import { cn } from "@/lib/utils" -import { Button, buttonVariants } from "@/components/ui/button" +import { cn } from "@/lib/utils"; +import { Button, buttonVariants } from "@/components/ui/button"; function Calendar({ - className, - classNames, - showOutsideDays = true, - captionLayout = "label", - buttonVariant = "ghost", - formatters, - components, - ...props + className, + classNames, + showOutsideDays = true, + captionLayout = "label", + buttonVariant = "ghost", + formatters, + components, + ...props }: React.ComponentProps & { - buttonVariant?: React.ComponentProps["variant"] + buttonVariant?: React.ComponentProps["variant"]; }) { - const defaultClassNames = getDefaultClassNames() + const defaultClassNames = getDefaultClassNames(); - return ( - svg]:rotate-180`, - String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, - className - )} - captionLayout={captionLayout} - formatters={{ - formatMonthDropdown: (date) => - date.toLocaleString("ru", { month: "short" }), - ...formatters, - - }} - classNames={{ - root: cn("w-fit", defaultClassNames.root), - months: cn( - "relative flex flex-col gap-6 md:flex-row", - defaultClassNames.months - ), - month: cn("flex w-full flex-col gap-6", defaultClassNames.month), - nav: cn( - "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1", - defaultClassNames.nav - ), - button_previous: cn( - buttonVariants({ variant: buttonVariant }), - "h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50", - defaultClassNames.button_previous - ), - button_next: cn( - buttonVariants({ variant: buttonVariant }), - "h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50", - defaultClassNames.button_next - ), - month_caption: cn( - "flex h-[--cell-size] w-full items-center justify-center px-[--cell-size]", - defaultClassNames.month_caption - ), - dropdowns: cn( - "flex h-[--cell-size] w-full items-center justify-center gap-1.5 text-sm font-medium", - defaultClassNames.dropdowns - ), - dropdown_root: cn( - "has-focus:border-ring border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] relative rounded-md border", - defaultClassNames.dropdown_root - ), - dropdown: cn( - "bg-popover absolute inset-0 opacity-0", - defaultClassNames.dropdown - ), - caption_label: cn( - "select-none font-medium", - captionLayout === "label" - ? "text-sm" - : "[&>svg]:text-muted-foreground flex h-8 items-center gap-1 rounded-md pl-2 pr-1 text-sm [&>svg]:size-3.5", - defaultClassNames.caption_label - ), - table: "w-full border-collapse", - weekdays: cn("flex", defaultClassNames.weekdays), - weekday: cn( - "text-muted-foreground flex-1 select-none rounded-md text-[0.8rem] font-normal p-2", - defaultClassNames.weekday - ), - week: cn("mt-3 flex w-full", defaultClassNames.week), - week_number_header: cn( - "w-[--cell-size] select-none", - defaultClassNames.week_number_header - ), - week_number: cn( - "text-muted-foreground select-none text-[0.8rem]", - defaultClassNames.week_number - ), - day: cn( - "group/day relative aspect-square h-full w-full select-none p-1 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md", - defaultClassNames.day - ), - range_start: cn( - "bg-accent rounded-l-md", - defaultClassNames.range_start - ), - range_middle: cn("rounded-none", defaultClassNames.range_middle), - range_end: cn("bg-accent rounded-r-md", defaultClassNames.range_end), - today: cn( - "bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none", - defaultClassNames.today - ), - outside: cn( - "text-muted-foreground aria-selected:text-muted-foreground", - defaultClassNames.outside - ), - disabled: cn( - "text-muted-foreground opacity-50", - defaultClassNames.disabled - ), - hidden: cn("invisible", defaultClassNames.hidden), - ...classNames, - }} - components={{ - Root: ({ className, rootRef, ...props }) => { - return ( -
- ) - }, - Chevron: ({ className, orientation, ...props }) => { - if (orientation === "left") { - return ( - - ) - } + return ( + svg]:rotate-180`, + String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, + className + )} + captionLayout={captionLayout} + formatters={{ + formatMonthDropdown: (date) => + date.toLocaleString("ru", { month: "short" }), + ...formatters, + }} + classNames={{ + root: cn("w-fit", defaultClassNames.root), + months: cn( + "relative flex flex-col gap-6 md:flex-row", + defaultClassNames.months + ), + month: cn( + "flex w-full flex-col gap-6", + defaultClassNames.month + ), + nav: cn( + "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1", + defaultClassNames.nav + ), + button_previous: cn( + buttonVariants({ variant: buttonVariant }), + "h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50", + defaultClassNames.button_previous + ), + button_next: cn( + buttonVariants({ variant: buttonVariant }), + "h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50", + defaultClassNames.button_next + ), + month_caption: cn( + "flex h-[--cell-size] w-full items-center justify-center px-[--cell-size]", + defaultClassNames.month_caption + ), + dropdowns: cn( + "flex h-[--cell-size] w-full items-center justify-center gap-1.5 text-sm font-medium", + defaultClassNames.dropdowns + ), + dropdown_root: cn( + "has-focus:border-ring border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] relative rounded-md border", + defaultClassNames.dropdown_root + ), + dropdown: cn( + "bg-popover absolute inset-0 opacity-0", + defaultClassNames.dropdown + ), + caption_label: cn( + "select-none font-medium", + captionLayout === "label" + ? "text-sm" + : "[&>svg]:text-muted-foreground flex h-8 items-center gap-1 rounded-md pl-2 pr-1 text-sm [&>svg]:size-3.5", + defaultClassNames.caption_label + ), + table: "w-full border-collapse", + weekdays: cn("flex", defaultClassNames.weekdays), + weekday: cn( + "text-muted-foreground flex-1 select-none rounded-md text-[0.8rem] font-normal p-2", + defaultClassNames.weekday + ), + week: cn("mt-3 flex w-full", defaultClassNames.week), + week_number_header: cn( + "w-[--cell-size] select-none", + defaultClassNames.week_number_header + ), + week_number: cn( + "text-muted-foreground select-none text-[0.8rem]", + defaultClassNames.week_number + ), + day: cn( + "group/day relative aspect-square h-full w-full select-none p-1 text-center", + defaultClassNames.day + ), + range_start: cn( + "bg-accent rounded-l-md", + defaultClassNames.range_start + ), + range_middle: cn( + "rounded-none", + defaultClassNames.range_middle + ), + range_end: cn( + "bg-accent rounded-r-md", + defaultClassNames.range_end + ), + today: cn( + "bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none", + defaultClassNames.today + ), + outside: cn( + "text-muted-foreground aria-selected:text-muted-foreground", + defaultClassNames.outside + ), + disabled: cn( + "text-muted-foreground opacity-50", + defaultClassNames.disabled + ), + hidden: cn("invisible", defaultClassNames.hidden), + ...classNames, + }} + components={{ + Root: ({ className, rootRef, ...props }) => { + return ( +
+ ); + }, + Chevron: ({ className, orientation, ...props }) => { + if (orientation === "left") { + return ( + + ); + } - if (orientation === "right") { - return ( - - ) - } + if (orientation === "right") { + return ( + + ); + } - return ( - - ) - }, - DayButton: CalendarDayButton, - WeekNumber: ({ children, ...props }) => { - return ( - -
- {children} -
- - ) - }, - ...components, - }} - {...props} - /> - ) + return ( + + ); + }, + DayButton: CalendarDayButton, + WeekNumber: ({ children, ...props }) => { + return ( + +
+ {children} +
+ + ); + }, + ...components, + }} + {...props} + /> + ); } function CalendarDayButton({ - className, - day, - modifiers, - ...props + className, + day, + modifiers, + ...props }: React.ComponentProps) { - const defaultClassNames = getDefaultClassNames() + const defaultClassNames = getDefaultClassNames(); - const ref = React.useRef(null) - React.useEffect(() => { - if (modifiers.focused) ref.current?.focus() - }, [modifiers.focused]) + const ref = React.useRef(null); + React.useEffect(() => { + if (modifiers.focused) ref.current?.focus(); + }, [modifiers.focused]); - return ( - + + {value} + + +
+ ); +}; + +export default Counter; diff --git a/src/components/ui/date-picker.tsx b/src/components/ui/date-picker.tsx index 0f24a3e..d3b95ce 100644 --- a/src/components/ui/date-picker.tsx +++ b/src/components/ui/date-picker.tsx @@ -18,14 +18,15 @@ export function DatePicker() { const [date, setDate] = React.useState(); const [departureTime, setDepartureTime] = React.useState("12:00"); const [arrivalTime, setArrivalTime] = React.useState("13:00"); + const [open, setOpen] = React.useState(false); const handleApply = () => { - // Логика применения выбранных даты и времени - console.log("Применено:", { date, departureTime, arrivalTime }); + // Закрываем popover после применения + setOpen(false); }; return ( - + - +
{/* Календарь */} {/* Поля времени */}
-
setDepartureTime(e.target.value) } - className="w-full h-10 px-3 border border-gray-300 rounded-md text-gray-700 font-medium focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent" + className="w-full h-12 px-3 border border-gray-300 rounded-full text-gray-700 font-medium text-center focus:outline-none focus:ring-2 focus:ring-[#008299] focus:border-transparent" /> +
-
setArrivalTime(e.target.value) } - className="w-full h-10 px-3 border border-gray-300 rounded-md text-gray-700 font-medium focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent" + className="w-full h-12 px-3 border border-gray-300 rounded-full text-gray-700 font-medium text-center focus:outline-none focus:ring-2 focus:ring-[#008299] focus:border-transparent" /> +
diff --git a/src/components/ui/icon.tsx b/src/components/ui/icon.tsx index 67e0413..5cfc724 100644 --- a/src/components/ui/icon.tsx +++ b/src/components/ui/icon.tsx @@ -1,6 +1,6 @@ import React, { forwardRef, memo } from "react"; -import { IconProps } from "../../types/icon"; +import { SVGProps } from "react"; // Импорт SVG иконок как React компонентов import CalendarIcon from "../../../public/images/icons/calendar.svg"; import MapIcon from "../../../public/images/icons/map.svg"; @@ -33,6 +33,31 @@ const icons = { tg: TgIcon, }; +export type IconName = + | "calendar" + | "map" + | "money" + | "people" + | "logo" + | "restart" + | "anchor" + | "width" + | "like" + | "star" + | "vk" + | "dzen" + | "tg"; + +export interface IconProps + extends Omit, "name" | "preserveAspectRatio"> { + name: IconName; + className?: string; + size?: number | string; + color?: string; + preserveAspectRatio?: boolean; + maxSize?: number | string; +} + // Компонент Icon оптимизированный для Next.js export const Icon = memo( forwardRef( diff --git a/src/types/icon.ts b/src/types/icon.ts index 60bf5ee..8b13789 100644 --- a/src/types/icon.ts +++ b/src/types/icon.ts @@ -1,26 +1 @@ -import { SVGProps } from "react"; -export type IconName = - | "calendar" - | "map" - | "money" - | "people" - | "logo" - | "restart" - | "anchor" - | "width" - | "like" - | "star" - | "vk" - | "dzen" - | "tg"; - -export interface IconProps - extends Omit, "name" | "preserveAspectRatio"> { - name: IconName; - className?: string; - size?: number | string; - color?: string; - preserveAspectRatio?: boolean; - maxSize?: number | string; -}